从流程可以看出,浏览在访问服务时会携带jwt向微服务发起请求,中间会先经过网关。网关从jwt中获取用户信息并保存到请求中,然后再转发给对应服务。在到达业务层之前会有拦截器将用户信息存入ThreadLocal中。
1.在经过网关过滤器被拦截时将用户信息保存到请求头中
ini
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取request
ServerHttpRequest request = exchange.getRequest();
// 判断是否需要拦截
if(isExclude(request.getPath().toString())){
// 无需拦截 放行
return chain.filter(exchange);
}
// 获取请求头中的token
String token = null;
List<String> headers = request.getHeaders().get("authorization");
if(CollUtils.isNotEmpty(headers)){
token = headers.get(0);
}
//校验并解析token
Long userId = null;
try {
userId = jwtTool.parseToken(token);
} catch (UnauthorizedException e) {
//无效 拦截
ServerHttpResponse response = exchange.getResponse();
response.setRawStatusCode(401);
return response.setComplete();
}
// 如果有效,传递用户信息
String userInfo = userId.toString();
ServerWebExchange ex = exchange.mutate()
.request(b -> b.header("user-info", userInfo))
.build();
// 放行
return chain.filter(exchange);
}
由于jwt令牌中会携带用户id,将用户id存入请求头 "user-info" 中;
2.在公用模块中定义拦截器,在拦截器中将请求头中用户信息保存到ThreadLocal中
java
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userInfo = request.getHeader("user-info");
//保存到ThreadLocal
if(StrUtil.isNotBlank(userInfo)){
UserContext.setUser(Long.valueOf(userInfo));
}
//放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//移除用户
UserContext.removeUser();
}
}
从 "user-info" 请求头中获取到用户id,将其保存到ThreadLocal中。在经过网关后会先经过该拦截器再到达业务层,从而携带登录用户的id到业务逻辑中。
less
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInfoInterceptor());
}
}
在该模块中定义SpringMVC的配置类,将定义的拦截器注册
由于是在微服务框架中,在其他启动模块中不会扫描到MvcConfig,所以需要手动配置
3.处理跨服务调用无法拿到用户信息的问题
当基于OpenFeign实现跨服务调用时,从一个服务向另一个服务发送请求时Feign会重新创建一个Http请求,而这个新的请求并不会继承原始的请求头信息。所以需要实现Feign提供的一个拦截器接口:feign.RequestInterceptor
typescript
public class DefaultFeignConfig {
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
return new RequestInterceptor(){
@Override
public void apply(RequestTemplate requestTemplate) {
//获取登录用户
Long userId = UserContext.getUser();
if(userId == null){
return;
}
//如果不为空,添加到请求头中
requestTemplate.header("user-info", userId.toString());
}
};
}
}
在Feign发送Http请求前,通过RequestInterceptor拦截器将请求头添加到Feign创建的请求的头信息中。
Pasted image 20260724174630.png由于这是Feign的最佳实践,如果在DefaultFeignConfig类中加了 @Configuration,Spring 组件扫描会把它扫进主容器,里面的 @Bean 就变成全局 Bean 了。而 Feign 的配置类应该只作用于 Feign 自身的上下文,避免污染主容器。所以 Feign 框架会自己拿到这个类,为它创建独立的子上下文,完全不需要 Spring 扫描。需要在每个需要用到的微服务模块中添加这个注解。从流程可以看出,浏览在访问服务时会携带jwt向微服务发起请求,中间会先经过网关。网关从jwt中获取用户信息并保存到请求中,然后再转发给对应服务。在到达业务层之前会有拦截器将用户信息存入ThreadLocal中。
1.在经过网关过滤器被拦截时将用户信息保存到请求头中
ini
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取request
ServerHttpRequest request = exchange.getRequest();
// 判断是否需要拦截
if(isExclude(request.getPath().toString())){
// 无需拦截 放行
return chain.filter(exchange);
}
// 获取请求头中的token
String token = null;
List<String> headers = request.getHeaders().get("authorization");
if(CollUtils.isNotEmpty(headers)){
token = headers.get(0);
}
//校验并解析token
Long userId = null;
try {
userId = jwtTool.parseToken(token);
} catch (UnauthorizedException e) {
//无效 拦截
ServerHttpResponse response = exchange.getResponse();
response.setRawStatusCode(401);
return response.setComplete();
}
// 如果有效,传递用户信息
String userInfo = userId.toString();
ServerWebExchange ex = exchange.mutate()
.request(b -> b.header("user-info", userInfo))
.build();
// 放行
return chain.filter(exchange);
}
由于jwt令牌中会携带用户id,将用户id存入请求头* "user-info" *中;
2.在公用模块中定义拦截器,在拦截器中将请求头中用户信息保存到ThreadLocal中
java
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userInfo = request.getHeader("user-info");
//保存到ThreadLocal
if(StrUtil.isNotBlank(userInfo)){
UserContext.setUser(Long.valueOf(userInfo));
}
//放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//移除用户
UserContext.removeUser();
}
}
从* "user-info" *请求头中获取到用户id,将其保存到ThreadLocal中。在经过网关后会先经过该拦截器再到达业务层,从而携带登录用户的id到业务逻辑中。
less
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInfoInterceptor());
}
}
在该模块中定义SpringMVC的配置类,将定义的拦截器注册
697由于是在微服务框架中,在其他启动模块中不会扫描到MvcConfig,所以需要手动配置
3.处理跨服务调用无法拿到用户信息的问题
当基于OpenFeign实现跨服务调用时,从一个服务向另一个服务发送请求时Feign会重新创建一个Http请求,而这个新的请求并不会继承原始的请求头信息。所以需要实现Feign提供的一个拦截器接口:feign.RequestInterceptor
typescript
public class DefaultFeignConfig {
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
return new RequestInterceptor(){
@Override
public void apply(RequestTemplate requestTemplate) {
//获取登录用户
Long userId = UserContext.getUser();
if(userId == null){
return;
}
//如果不为空,添加到请求头中
requestTemplate.header("user-info", userId.toString());
}
};
}
}
在Feign发送Http请求前,通过RequestInterceptor拦截器将请求头添加到Feign创建的请求的头信息中。
Pasted image 20260724174630.png由于这是Feign的最佳实践,如果在DefaultFeignConfig类中加了 @Configuration,Spring 组件扫描会把它扫进主容器,里面的 @Bean 就变成全局 Bean 了。而 Feign 的配置类应该只作用于 Feign 自身的上下文,避免污染主容器。所以 Feign 框架会自己拿到这个类,为它创建独立的子上下文,完全不需要 Spring 扫描。需要在每个需要用到的微服务模块中添加这个注解。从流程可以看出,浏览在访问服务时会携带jwt向微服务发起请求,中间会先经过网关。网关从jwt中获取用户信息并保存到请求中,然后再转发给对应服务。在到达业务层之前会有拦截器将用户信息存入ThreadLocal中。
1.在经过网关过滤器被拦截时将用户信息保存到请求头中
ini
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取request
ServerHttpRequest request = exchange.getRequest();
// 判断是否需要拦截
if(isExclude(request.getPath().toString())){
// 无需拦截 放行
return chain.filter(exchange);
}
// 获取请求头中的token
String token = null;
List<String> headers = request.getHeaders().get("authorization");
if(CollUtils.isNotEmpty(headers)){
token = headers.get(0);
}
//校验并解析token
Long userId = null;
try {
userId = jwtTool.parseToken(token);
} catch (UnauthorizedException e) {
//无效 拦截
ServerHttpResponse response = exchange.getResponse();
response.setRawStatusCode(401);
return response.setComplete();
}
// 如果有效,传递用户信息
String userInfo = userId.toString();
ServerWebExchange ex = exchange.mutate()
.request(b -> b.header("user-info", userInfo))
.build();
// 放行
return chain.filter(exchange);
}
由于jwt令牌中会携带用户id,将用户id存入请求头* "user-info" *中;
2.在公用模块中定义拦截器,在拦截器中将请求头中用户信息保存到ThreadLocal中
java
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userInfo = request.getHeader("user-info");
//保存到ThreadLocal
if(StrUtil.isNotBlank(userInfo)){
UserContext.setUser(Long.valueOf(userInfo));
}
//放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//移除用户
UserContext.removeUser();
}
}
从* "user-info" *请求头中获取到用户id,将其保存到ThreadLocal中。在经过网关后会先经过该拦截器再到达业务层,从而携带登录用户的id到业务逻辑中。
less
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInfoInterceptor());
}
}
在该模块中定义SpringMVC的配置类,将定义的拦截器注册
697由于是在微服务框架中,在其他启动模块中不会扫描到MvcConfig,所以需要手动配置
3.处理跨服务调用无法拿到用户信息的问题
当基于OpenFeign实现跨服务调用时,从一个服务向另一个服务发送请求时Feign会重新创建一个Http请求,而这个新的请求并不会继承原始的请求头信息。所以需要实现Feign提供的一个拦截器接口:feign.RequestInterceptor
typescript
public class DefaultFeignConfig {
@Bean
public RequestInterceptor userInfoRequestInterceptor(){
return new RequestInterceptor(){
@Override
public void apply(RequestTemplate requestTemplate) {
//获取登录用户
Long userId = UserContext.getUser();
if(userId == null){
return;
}
//如果不为空,添加到请求头中
requestTemplate.header("user-info", userId.toString());
}
};
}
}
在Feign发送Http请求前,通过RequestInterceptor拦截器将请求头添加到Feign创建的请求的头信息中。
由于这是Feign的最佳实践,如果在DefaultFeignConfig类中加了 @Configuration,Spring 组件扫描会把它扫进主容器,里面的 @Bean 就变成全局 Bean 了。而 Feign 的配置类应该只作用于 Feign 自身的上下文,避免污染主容器。所以 Feign 框架会自己拿到这个类,为它创建独立的子上下文,完全不需要 Spring 扫描。需要在每个需要用到的微服务模块中添加这个注解。