0-我们通过客户端=》网关=》微服务的顺序访问服务端
1-网关:这一步主要是获取token进行验证,成功后把用户信息保存到请求头以供微服务调取
因为微服务模块比较多,如果每一个都写拦截器会造成不必要的冗余,所以我们统一把拦截器放在网关模块
网关的信息传递流程为 客户端=》断言=》过滤器=》微服务=》过滤器=》断言=》客户端
通过上面流程我们就知道可以把登录校验问题写的prefilter,这样一旦验证失效就不用访问微服务,完成鉴权功能
基本流程为:获取req-》获取path检查是否可以直接放行(比如登录,或者一些不需要登录也允许的操作)-》通过头信息获取token-》通过token获得用户信息-》携带用户信息转发给微服务
@Component @RequiredArgsConstructor public class AuthGlobalFilter implements GlobalFilter, Ordered { private final AuthProperties authProperties; private final JwtTool jwtTool; private final AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取req ServerHttpRequest request = exchange.getRequest(); // 获取路径 RequestPath path = request.getPath(); // 判断是否放行 if (exclude(path.toString())) { return chain.filter(exchange); } // 获取token String token = null; List<String> headers = request.getHeaders().get("authorization"); if (headers != null && !headers.isEmpty()) { token = headers.get(0); } Long userId = null; try { userId = jwtTool.parseToken(token); } catch (UnauthorizedException e) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } // 传递用户信息,这样在后续的过滤器中就可以获取到用户信息了 String userIdString = userId.toString(); ServerWebExchange serverWebExchange = exchange.mutate() .request(builder -> { builder.header("user-info", userIdString); }).build(); return chain.filter(serverWebExchange); } private boolean exclude(String string) { for (String way : authProperties.getExcludePaths()) { if (antPathMatcher.match(way, string)) { return true; } } return false; } @Override public int getOrder() { return 1; } }
2.微服务:这一步我们主要通过拦截器获取用户信息,以便使用
因为微服务模块比较多,如果每一个都写拦截器会造成不必要的冗余,所以我们统一把拦截器放在常用类模块,与上述非阻塞式不同,这里拦截器作为一个bean,供其他模块调用,因此我们要记住把bean注册在resources\META-INF\spring.facotories中。每当我们启动Context时,都会先扫描starter中的工厂,再通过这个工厂去管理非本模块中的bean。工厂创建如下例:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.hmall.common.config.JsonConfig
拦截器只需为请求头添加一个用户信息,即无论如何都会放行。在结束后及时销毁以保证信息安全
public class UserInfoInterceptors implements HandlerInterceptor{ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String userInfo = request.getHeader("user-info"); if(StrUtil.isNotBlank(userInfo)) UserContext.setUser(Long.valueOf(userInfo)); return true; } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { UserContext.removeUser(); } }
然后,我们在实现WebMvcConfig的接口中添加拦截器(这里为什么添加了条件注解:在网关模块中无法提供user-info请求头,因为网关模块压根没有引入springWEB,而DispatcherServlet.class作为springMVC的重要组件,个人理解DispatcherServlet约等于struct)
@Configuration @ConditionalOnClass(DispatcherServlet.class) public class MvcConfig implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserInfoInterceptors()); } }
完成上述操作后,我们已经可以在网关---微服务之间搭起用户信息传递的桥梁
但是我们还有微服务---微服务之间的通信问题没有解决,因为微服务=》微服务是基于openFeign发送,他明显不携带我们创建的请求头。基于此,我们可以在openFeign中创建一个拦截器,效仿网关=》微服务,添加一个bean
public class DefaultFeignConfig { @Bean public RequestInterceptor requestInterceptor(){ return new RequestInterceptor(){ @Override public void apply(RequestTemplate template) { Long id = UserContext.getUser(); if(id!=null) // 添加公共参数 template.header("user-info", id.toString()); } }; } }
那么这两个拦截器有什么不同?
common中的拦截器我们称为C,而openFeign中的拦截器我们称为o,
首先C是springMVC的拦截器;而O是feign中的拦截器。
c的拦截器是被其他模块调用,需要在其他模块启动的时候自动加载和应用,因此他需要装入spring.factroies中。当我们实现WebMvcConfigurer时,并不需要@bean来注册,springMVC会自动完成,简而言之:c是由springMVC管理生命周期;
而o的拦截器是feign中的拦截器,并不同于spring,不需要添加到mvc即可生效
总的来说拦截器是由mvc注册的,一般不需要bean管理
标签:网关,拦截器,return,登录,验证,流程,模块,服务,public From: https://www.cnblogs.com/kun1790051360/p/18156418