SpringCloudGateWay实现token的校验和传输

SpringCloudGateWay实现token的校验和传输
Spring Cloud GateWay实现token的校验和传输
1.1 SpringCloud Gateway 简介
Spring Cloud GateWay基于WebFlux框架实现的,⽽WebFlux框架底层则使⽤了⾼性能的Reactor模式通信框架Netty.
Spring Cloud Gateway 的⽬标,不仅提供统⼀的路由⽅式,并且基于 Filter 链的⽅式提供了⽹关基本的功能,例如:安全,监控/指标,和限流。
提前声明:Spring Cloud Gateway 底层使⽤了⾼性能的通信框架Netty
1.2 SpringCloud Gateway 特征
SpringCloud官⽅,对SpringCloud Gateway 特征介绍如下:
基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
集成 Hystrix 断路器
德国人的性格集成 Spring Cloud DiscoveryClient
Predicates 和 Filters 作⽤于特定路由,易于编写的 Predicates 和 Filters
具备⼀些⽹关的⾼级功能:动态路由、限流、路径重写
从以上的特征来说,和Zuul的特征差别不⼤。SpringCloud Gateway和Zuul主要的区别,还是在底层的通信框架上。
简单说明⼀下上⽂中的三个术语:
**(**1) Filter(过滤器):
和Zuul的过滤器在概念上类似,可以使⽤它拦截和修改请求,并且对上游的响应,进⾏⼆次处理。过滤器为
org.springframework.cloud.gateway.filter.GatewayFilter类的实例。
(2)Route(路由):
⽹关配置的基本组成模块,和Zuul的路由配置模块类似。⼀个Route模块由⼀个 ID,⼀个⽬标 URI,
⼀组断⾔和⼀组过滤器定义。如果断⾔为真,则路由匹配,⽬标URI会被访问。
**(**3)Predicate(断⾔):
这是⼀个 Java 8 的 Predicate,可以使⽤它来匹配来⾃ HTTP 请求的任何内容,例如 headers 或参数。断⾔的输⼊类型是⼀个ServerWebExchange。
1.3 SpringCloud Zuul的IO模型/GateWay的IO模型
Zuul
Springcloud中所集成的Zuul版本,采⽤的是Tomcat容器,使⽤的是传统的Servlet IO处理模型。
⼤家知道,servlet由servlet container进⾏⽣命周期管理。container启动时构造servlet对象并调⽤servlet init()进⾏初始化;container关闭时调⽤servlet destory ()销毁servlet;container运⾏时接受请求,并为每个请求分配⼀个线程(⼀般从线程池中获取空闲线程)然后调⽤service()。
弊端:servlet是⼀个简单的⽹络IO模型,当请求进⼊servlet container时,servlet container就会为其绑定⼀个线程,在并发不⾼的场景下这种模型是适⽤的,但是⼀旦并发上升,线程数量就会上涨,⽽线程资源代价是昂贵的(上线⽂切换,内存消耗⼤)严重影响请求的处理时间。在⼀些简单的业务场
景下,不希望为每个request分配⼀个线程,只需要1个或⼏个线程就能应对极⼤并发的请求,这种业务场景下servlet模型没有优势。
所以Springcloud Zuul 是基于servlet之上的⼀个阻塞式处理模型,即spring实现了处理所有request请求的⼀个servlet(DispatcherServlet),并由该servlet阻塞式处理处理。所以Springcloud Zuul⽆法摆脱servlet模型的弊端。虽然Zuul 2.0开始,使⽤了Netty,并且已经有了⼤规模Zuul 2.0集部署的成熟案例,但是
GateWay
Webflux模式替换了旧的Servlet线程模型。⽤少量的线程处理request和response io操作,这些线程称为Loop线程,⽽业务交给响应式编程框架处理,响应式编程是⾮常灵活的,⽤户可以将业务中阻塞的操作提交到响应式框架的work线程中执⾏,⽽不阻塞的操作依然可以在Loop线程中进⾏处理,⼤⼤提⾼了Loop线程的利⽤率。
Webflux虽然可以兼容多个底层的通信框架,但是⼀般情况下,底层使⽤的还是Netty,毕竟,Netty是⽬前业界认可的最⾼性能的通信框架。⽽Webflux的Loop 线程,正好就是著名的Reactor 模式IO处理模型的Reactor线程,如果使⽤的是⾼性能的通信框架Netty,这就是Netty的EventLoop线程。
1.4 token传递以及存储
由于在⽹关层⾯拦截token之后,需要检验并且解析,并且将解析后的token(包含⽤户信息)传递给其他微服务,实现微服务之间的调⽤AuthLoginGlobalFilter全局认证过滤器,实现GlobalFilter接⼝,进⾏请求的过滤.
@Slf4j
@PropertySource(value ="classpath:loginfilter.properties")
@Component
public class AuthLoginGlobalFilter  implements GlobalFilter, Ordered {
@Value("#{'${jwt.ignoreUrls}'.split(',')}")
List<String> ignoreUrls;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* @Description  全局校验过滤器
* @author Liu_gx
* @date 2021/1/8 17:29
* @param exchange
* @param chain
* @return
*/
/*@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest Request();
if(ignoreUrls!=null&&URI().getPath())){
return chain.filter(exchange);
}
//获取token
String Headers().getFirst(Constants.LOGIN_TOKEN);
//获取服务名称
String serverName = URI().getPath());
//获取具体的请求服务实例
AbstractRequestUrlResolver serverInstance = ServerInstance(serverName);
if(null == serverInstance){
return onError(exchange,new ResponseEntity("fail", HttpStatus.BAD_REQUEST.value(), "暂不⽀持该登录类型", Collections.EMPTY_MAP));
}
serverInstance.setRedisTemplate(redisTemplate);
//token如果为null,app端需要校验JwtUser,放⾏
if(StringUtils.isEmpty(authoriztion)){
//是否需要强制校验token
kenNullIsCheckToken()){
return onError(exchange,serverInstance.setResponseEntity("fail", HttpStatus.UNAUTHORIZED.value(), "尚未登录", Collections.EMPTY_MAP));            }else {
ServerHttpRequest shr=request.mutate().header(Constants.JWT_USERINFO, JSONString( new JwtUserInfo())).build();
return chain.filter(exchange.mutate().request(shr).build());
}
}
//校验token
ResponseEntity responseEntity = serverInstance.checkAuthToken(authoriztion);
if(null != responseEntity && Result().equals("fail")){
return onError(exchange,responseEntity);
}
ServerHttpRequest shr=request.mutate().header(Constants.JWT_USERINFO, Data())).build();
return chain.filter(exchange.mutate().request(shr).build());
}*/
private static final String X_CLIENT_TOKEN_USER ="x-client-token-user";
@Override
public Mono<Void>filter(ServerWebExchange exchange, GatewayFilterChain chain){
ServerHttpRequest Request();
if(ignoreUrls!=null&&URI().getPath())){
return chain.filter(exchange);
}
//获取token
String Headers().getFirst(Constants.LOGIN_TOKEN);
//获取服务名称
String serverName =URI().getPath());
//获取具体的请求服务实例
AbstractRequestUrlResolver serverInstance = ServerInstance(serverName);
if(null == serverInstance){
return onError(exchange,new ResponseEntity("fail", HttpStatus.BAD_REQUEST.value(),"暂不⽀持该登录类型", Collections.EMPTY_MAP));
}
serverInstance.setRedisTemplate(redisTemplate);
//token如果为null,app端需要校验JwtUser,放⾏
if(StringUtils.isEmpty(authoriztion)){
//是否需要强制校验token
kenNullIsCheckToken()){
return onError(exchange,serverInstance.setResponseEntity("fail", HttpStatus.UNAUTHORIZED.value(),"尚未登录", Collections.EMPTY_MAP));
}else{
ServerHttpRequest shr=request.mutate().header(X_CLIENT_TOKEN_USER, JSONString(new JwtUserInfo())).build();
return chain.filter(exchange.mutate().request(shr).build());
}
}
//校验token
ResponseEntity responseEntity = serverInstance.checkAuthToken(authoriztion);
if(null != responseEntity && Result().equals("fail")){
return onError(exchange,responseEntity);
}
JSONObject jsonObject =(JSONObject) Data());
ServerHttpRequest shr=request.mutate().header(X_CLIENT_TOKEN_USER, Data())).build();
return chain.filter(exchange.mutate().request(shr).build());
}
/**
* @Description  获取请求的服务名称
* @author Liu_gx
* @date 2021/1/8 17:28
* @param path  请求路径
* @return
*/
private String getServerNameByPath(String path){
return getServerNameByPath(path,
(pathUrl)->{
place("/api/","");
},(pathUrl)->{
return pathUrl.substring(0, pathUrl.indexOf("/"));
});
}
private  String getServerNameByPath(String str, Function<String,String> fun1,Function<String,String> fun2){
return fun1.andThen (fun2).apply (str);
}
/**
* @Description  异常返回信息
* @author Liu_gx
* @date 2021/1/8 17:29
* @param exchange
* @param rd
* @return
private Mono<Void>onError(ServerWebExchange exchange,ResponseEntity rd){
ServerHttpResponse Response();
response.setStatusCode(HttpStatus.Rescode()));
ObjectMapper objectMapper=new ObjectMapper();
String rs="";
try{
rs=objectMapper.writeValueAsString(rd);
}catch(JsonProcessingException e){
<("occur Exception:"+e);
}
DataBuffer buffer= response.bufferFactory().Bytes());视域
return response.writeWith(Flux.just(buffer));
}
@Override
public int getOrder(){
return0;
}
}
⽅式⼀
HandlerMethodArgumentResolver是⽤来处理⽅法参数的解析器,包含以下2个⽅法:
supportsParameter(满⾜某种要求,返回true,⽅可进⼊resolveArgument做参数处理)
第一个无产阶级政党resolveArgument 解析操作
知识储备已到位,接下来着⼿实现,主要分为三步⾛:
1. ⾃定义参数注解@LoginUser,添加⾄controller的⽅法参数userId之上;
2. ⾃定义⽅法参数解析器LoginUserHandlerResolver,取出request中的userInfo,并赋值给添加了@LoginUser注解的参数张茜倩
userId。
3. 将⾃定义参数解析器LoginUserHandlerResolver注册到spring容器中.
在Common公⽤模块⾃定义参数解析器LoginUserHandlerResolver实现HandlerMethodArgumentResolver(⽅法参数解析器)
@Configuration
public class LoginUserHandlerResolver implements HandlerMethodArgumentResolver
{
private static final String CURRENT_ID ="jwtUserInfo";
@Override
public boolean supportsParameter(MethodParameter parameter)
{
return parameter.hasParameterAnnotation(LoginUser.class);
数据执行保护
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
NativeWebRequest nativeWebRequest, WebDataBinderFactory factory)throws Exception {
HttpServletRequest request = NativeRequest(HttpServletRequest.class);
// 获取⽤户ID
String userid = String.Header(CURRENT_ID));
if(userid == null){
return null;
}
return userid;
}
}
定义@LoginUser注解,绑定⽤户信息
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser
{}
将⾃定义解析器注册到spring容器中
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Value("${file.userfiles-path}")
private String filePath;
/
**
* 登录校验
*
* @return
*/
@Bean
public AuthenticationInterceptor loginRequiredInterceptor(){
return new AuthenticationInterceptor();
}
/**
* CurrentUser 注解参数解析器
*
* @return
*/
@Bean
cos系统下载public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver(){
return new CurrentUserMethodArgumentResolver();
}
/**
* 参数解析器
*
* @param argumentResolvers
*/

本文发布于:2024-09-22 04:33:56,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/230840.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:线程   请求   处理   参数   框架   路由   模型
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议