Spring Boot Feign service calls with token problems
- 2021-11-13 07:46:22
- OfStack
Feign Service Tuning Service Delivery Data with token Authentication
It is worth reminding that when Feign service transfers data, for example, a certain user service needs token authentication, and when calling that user service, an error is reported, prompting that token is empty, because Feign does not bring token when requesting Feign
Solution
To solve this problem, you can guess that the most convenient thing is to add token to the request head and bring it with you
Feign provides 1 interface, RequestInterceptor
As long as this interface is implemented, simply do some processing. For example, if we verify that the token of the request header is called Access-Token, we will first take out the token of the current request and put it on the feign request header
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* Feign Configure
* Use FeignClient Invoke and transfer between services headers Information
*/
@Configuration
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// Add token
requestTemplate.header("Access-Token", request.getHeader("Access-Token"));
}
}
In this way, Token has been successfully added to the Feign request header. In order to facilitate local debugging, a filter can be added to Spring and Boot, and one filter can be added every time there is no Token in local call, as long as the Filter interface provided by Spring is realized
import org.apache.commons.lang3.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
/**
* Every request filter intercepts plus Token
*/
public class AddTokenFilter implements Filter {
/**
* superAdmin
*/
private static final String DEFAULT_TOKEN = " Yours token";
private String profilesActive;
public AddTokenFilter(String profilesActive) {
super();
this.profilesActive = profilesActive;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// Judgment is the development mode ( dev ) or production environment ( pro )
// If it is not a development environment, do not do any operation, it is a development environment, and test it locally request Add request header
if (profilesActive == null || !EnumEnvType.DEV.toString().equalsIgnoreCase(profilesActive)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
filterChain.doFilter(new CustomeizedRequest((HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
}
// Inheritance HttpServletRequestWrapper , rewrite getHeader Gets the value of the request header
private class CustomeizedRequest extends HttpServletRequestWrapper {
/**
* Constructs a request object wrapping the given request.
*
* @param request
* @throws IllegalArgumentException if the request is null
*/
public CustomeizedRequest(HttpServletRequest request) {
super(request);
}
@Override
public String getHeader(String name) {
if (!Constant.HTTP_HEADER_ACCESS_TOKEN.equalsIgnoreCase(name)) {
return super.getHeader(name);
}
String token = super.getHeader(name);
return StringUtils.isNotBlank(token) ? token : DEFAULT_TOKEN;
}
}
}
Using this Filter is very simple. Create a new WebMvcConfig class and configure an bean
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.uhope.rl.watersource.filter.AddTokenFilter;
import com.uhope.rl.watersource.filter.ServiceFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.nio.charset.Charset;
/**
* Spring MVC Configure
* @author chenbin on 2017/12/25
* @version 3.0.0
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(WebMvcConfig.class);
/**
* Currently active profile
*/
@Value("${spring.profiles.active}")
private String env;
/**
* Solve the problem of path resource mapping
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
/**
* Use fastJson Substitute Jackjson Analyse JSON Data
*
* @return
*/
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
/*
* Convert to JSON String, default :
* WriteNullListAsEmpty List If the field is null, Output is [], Instead of null
* WriteNullStringAsEmpty Character type field if null, The output is "" , Instead of null
* WriteMapNullValue Whether the output value is null Fields of , Default to false
*/
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
fastConverter.setFastJsonConfig(fastJsonConfig);
fastConverter.setDefaultCharset(Charset.forName("UTF-8"));
HttpMessageConverter<?> converter = fastConverter;
return new HttpMessageConverters(converter);
}
/**
* This Filter Solve the problem of cross-domain page access
*/
@Bean
public FilterRegistrationBean omsFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new ServiceFilter());
registration.addUrlPatterns("/*");
registration.setName("MainFilter");
registration.setAsyncSupported(true);
registration.setOrder(1);
return registration;
}
/**
* This Filter Add token
*/
@Bean
public FilterRegistrationBean addTokenFilter(){
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new AddTokenFilter(env));
registration.addUrlPatterns("/*");
registration.setName("addTokenFilter");
registration.setAsyncSupported(true);
registration.setOrder(2);
return registration;
}
}
Summary 1
In this way, the token of adding local test in the development environment is realized. If it is not the development environment, the token of web page request is very convenient, and the problem of Feign missing the request header is also solved
Feign call for token authentication
1. Project scenario
Here, we use two springboot applications, which are called remotely through feign (yes, the architecture is so wonderful). Then, when calling feign, I hope token authentication can be performed.
2. Solutions
When the request comes in, check token of header by interceptor, and then intercept feign request by adding a new feign interceptor when calling feignClient in service, and add token in current header to the request header of feign. Realize the transmission of token in the link.
3. Concrete implementation
Added feign Interceptor Configuration
/**
* Feign Request interceptor configuration .
*
* @author linzp
* @version 1.0.0
* @date 2021/4/16 21:19
*/
@Configuration
public class FeignInterceptorConfig implements RequestInterceptor {
public FeignInterceptorConfig() {}
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// Settings token To request header
template.header(ConstantCommon.HEADER_TOKEN_KEY, request.getHeader(ConstantCommon.HEADER_TOKEN_KEY));
}
}
Then in the feignClient interface, add = = configuration = FeignInterceptorConfig. class = =
Pay attention to Bug! ! !
Attention! ! ! In this case, there will be an exception, and the request obtained will be null. The reason is that the hytrix isolation policy is thread and the threadLocal variable cannot be read.
Solution! ! Change policy
Add the following configuration in the configuration file to solve it!
# Replacement hystrix Policy, resolution cannot be delivered threadLocal Variable problem
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE