上篇说了当前端访问微服务网关,借助ZuulFilter过滤器来过滤所有请求,获取request,判断cookie是否有身份短令牌,request的header中是否有Jwt令牌,redis中是否有Jwt令牌。但是这个数据传递只能是前端访问微服务时,网关进行过滤,在微服务访问微服务时,则没有数据向下传递。
所以我们使用Fegin拦截器来做微服务之间的数据下沉,数据传递。
因为在每个微服务使用Fegin远程调用时都会使用,所以写在了common包下。
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
/** Fegin远程调用拦截器
*/
public class FeignClientInterceptor implements RequestInterceptor {
/**
* 每次远程调用都会走这个方法
* @param requestTemplate
*/
@Override
public void apply(RequestTemplate requestTemplate) {
//的到requst中Header数据
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(requestAttributes!=null){
HttpServletRequest request = requestAttributes.getRequest();
//取出header中的Jwt令牌
Enumeration<String> headerNames = request.getHeaderNames();
if(headerNames!=null){
while(headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
//向下传递
requestTemplate.header(headerName,headerValue);
}
}
}
}
}
可以从代码中看到,implements RequestInterceptor每个微服务远程调用都会走下面的实现方法,在apply()方法中,我们获取当前的request中Header中所有数据,然后用requestTemplate.header(headerName,headerValue) 将所有数据下沉。
所以在使用fegin远程调用时是可以这样利用fegin拦截器进行数据下沉的,但是在使用别的方法远程调用微服务时这个拦截器是不会处理的。
比如说,当我们使用的是restTemplate进行远程调用时,
//注入restTemplate
@Bean
@LoadBalanced//开启客户端负载均衡
public RestTemplate restTemplate(){
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
//rest远程获取数据
ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
此时进行数据下沉需要进行数据处理,借助request中header的HttpEntity进行数据存储,数据传递。
比如
private AuthToken applyToken(String clientId, String clientSecret, String username, String password) {
//获取认证服务 的微服务实例
ServiceInstance serviceInstance = loadBalancerClient.choose(XcServiceList.XC_SERVICE_UCENTER_AUTH);
if (serviceInstance == null) {
LOGGER.error("choose an auth instance fail");
ExceptionCast.cast(AuthCode.AUTH_LOGIN_AUTHSERVER_NOTFOUND);
}
// http://IP:port/
URI uri = serviceInstance.getUri();
String authUrl = uri + "/auth/oauth/token";
//HttpEntity
//body
LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type","password");
body.add("username",username);
body.add("password",password);
//headers
LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
header.add("Authorization",getHttpBasic(clientId,clientSecret));
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
//设置restTemplate远程调用时,对400,401错误不报错,正确返回数据
restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
@Override
public void handleError(ClientHttpResponse response) throws IOException {
if(response.getRawStatusCode()!=400&&response.getRawStatusCode()!=401){
super.handleError(response);
}
}
});
//申请令牌的信息
Map map = null;
try {
ResponseEntity<Map> bodyMap = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);
map = bodyMap.getBody();
} catch (RestClientException e) {
e.printStackTrace();
LOGGER.error("request oauth_token_password error: {}",e.getMessage());
e.printStackTrace();
ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
}
if(map == null ||
map.get("access_token") == null ||
map.get("refresh_token") == null ||
map.get("jti") == null){
//确定用户名 或者 密码错误异常抛出
if(map!=null){
String error_description = (String) map.get("error_description");
if(error_description.indexOf("UserDetailsService returned null")>=0){
ExceptionCast.cast(AuthCode.AUTH_ACCOUNT_NOTEXISTS);
}else if(error_description.indexOf("坏的凭证")>=0){
ExceptionCast.cast(AuthCode.AUTH_CREDENTIAL_ERROR);
}
}
//jti是jwt令牌的唯一标识作为用户身份令牌
ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
}
AuthToken authToken = new AuthToken();
//访问令牌(jwt)
String jwt_token = (String) map.get("access_token");
//刷新令牌(jwt)
String refresh_token = (String) map.get("refresh_token");
//jti,作为用户的身份标识
String access_token = (String) map.get("jti");
authToken.setJwt_token(jwt_token);
authToken.setAccess_token(access_token);
authToken.setRefresh_token(refresh_token);
return authToken;
}