冷饭新炒:理解JWT的实现原理和基本使用 (6)

然后是JwtGlobalFilter和JwtWebFilter的非完全实现:

@Component public class JwtGlobalFilter implements GlobalFilter, Ordered, EnvironmentAware { private final AntPathMatcher pathMatcher = new AntPathMatcher(); private List<String> accessUriList; @Autowired private JwtSpi jwtSpi; private static final String JSON_WEB_TOKEN_KEY = "X-TOKEN"; private static final String UID_KEY = "X-UID"; private static final String JWT_ID_KEY = "X-JTI"; @Override public void setEnvironment(Environment environment) { accessUriList = Arrays.asList(Objects.requireNonNull(environment.getProperty("jwt.access.uris")) .split(",")); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // OPTIONS 请求直接放行 HttpMethod method = request.getMethod(); if (Objects.nonNull(method) && Objects.equals(method, HttpMethod.OPTIONS)) { return chain.filter(exchange); } // 获取请求路径 String requestPath = request.getPath().value(); // 命中请求路径白名单 boolean matchWhiteRequestPathList = Optional.ofNullable(accessUriList) .map(paths -> paths.stream().anyMatch(path -> pathMatcher.match(path, requestPath))) .orElse(false); if (matchWhiteRequestPathList) { return chain.filter(exchange); } HttpHeaders headers = request.getHeaders(); String token = headers.getFirst(JSON_WEB_TOKEN_KEY); if (!StringUtils.hasLength(token)) { throw new BusinessException(BusinessErrorCode.TOKEN_ERROR.getCode(), "token is null"); } VerifyJwtResultDto resultDto = jwtSpi.verify(token); if (Objects.equals(resultDto.getValid(), Boolean.FALSE)) { throw new BusinessException(BusinessErrorCode.TOKEN_ERROR.getCode(), resultDto.getThrowable()); } headers.set(JWT_ID_KEY, String.valueOf(resultDto.getJwtId())); headers.set(UID_KEY, String.valueOf(resultDto.getContent().getCustomerId())); return chain.filter(exchange); } @Override public int getOrder() { return 1; } } @Component public class JwtWebFilter implements WebFilter { @Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping; @Autowired private JwtSpi jwtSpi; private static final String JSON_WEB_TOKEN_KEY = "X-TOKEN"; private static final String UID_KEY = "X-UID"; private static final String JWT_ID_KEY = "X-JTI"; @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { // OPTIONS 请求直接放行 HttpMethod method = exchange.getRequest().getMethod(); if (Objects.nonNull(method) && Objects.equals(method, HttpMethod.OPTIONS)) { return chain.filter(exchange); } HandlerMethod handlerMethod = requestMappingHandlerMapping.getHandlerInternal(exchange).block(); if (Objects.isNull(handlerMethod)) { return chain.filter(exchange); } RequireJWT typeAnnotation = handlerMethod.getBeanType().getAnnotation(RequireJWT.class); RequireJWT methodAnnotation = handlerMethod.getMethod().getAnnotation(RequireJWT.class); if (Objects.isNull(typeAnnotation) && Objects.isNull(methodAnnotation)) { return chain.filter(exchange); } HttpHeaders headers = exchange.getRequest().getHeaders(); String token = headers.getFirst(JSON_WEB_TOKEN_KEY); if (!StringUtils.hasLength(token)) { throw new BusinessException(BusinessErrorCode.TOKEN_ERROR.getCode(), "token is null"); } VerifyJwtResultDto resultDto = jwtSpi.verify(token); if (Objects.equals(resultDto.getValid(), Boolean.FALSE)) { throw new BusinessException(BusinessErrorCode.TOKEN_ERROR.getCode(), resultDto.getThrowable()); } headers.set(JWT_ID_KEY, String.valueOf(resultDto.getJwtId())); headers.set(UID_KEY, String.valueOf(resultDto.getContent().getCustomerId())); return chain.filter(exchange); } }

最后是一些配置属性:

jwt.hmac.secretKey='00000000111111112222222233333333' jwt.exp.seed.min=360000 jwt.exp.seed.max=8640000 jwt.issuer='throwx' jwt.access.uris=http://www.likecs.com/index,/actuator/* 使用JWT曾经遇到的坑

笔者负责的API网关使用了JWT应用于认证场景,算法上使用了安全性稍高的RS256,使用RSA算法进行签名生成。项目上线初期,JWT的过期时间都固定设置为7天,生产日志发现该API网关周期性发生"假死"现象,具体表现为:

Nginx自检周期性出现自检接口调用超时,提示部分或者全部API网关节点宕机

API网关所在机器的CPU周期性飙高,在用户访问量低的时候表现平稳

通过ELK进行日志排查,发现故障出现时段有JWT集中性过期和重新生成的日志痕迹

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjzjp.html