1.post请求 /** * @desc: (gateway主要接收前端请求 , 然后对请求的数据进行验证 , 验证之后请求反向代理到服务器 。 *当请求 method 为 GET 时 , 可以顺利通过gateway 。 当请求 method 为 POST 时 , gateway则会报如下错误 。 *java.lang.IllegalStateException : Only one connection receive subscriber allowed. *实际上spring - cloud - gateway反向代理的原理是 , 首先读取原请求的数据 , 然后构造一个新的请求 , *将原请求的数据封装到新的请求中 , 然后再转发出去 。 然而我们在他封装之前读取了一次request body , *而request body只能读取一次 。 因此就出现了上面的错误 。 *解决方案 : 读取request body的时候 , 我们再封装一次request , 转发出去) */ @Component public class PostFilter extends AbstractNameValueGatewayFilterFactory implements Ordered { @Override public GatewayFilter apply(NameValueConfig nameValueConfig) { return (exchange, chain) -> { URI uri = exchange.getRequest().getURI(); URI ex = UriComponentsBuilder.fromUri(uri).build(true).toUri(); ServerHttpRequest request = exchange.getRequest().mutate().uri(ex).build(); //封装我们的request if ("POST".equalsIgnoreCase(request.getMethodValue())) {//判断是否为POST请求 Fluxbody = request.getBody(); AtomicReference bodyRef = new AtomicReference<>();//缓存读取的request body信息 body.subscribe(dataBuffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()); DataBufferUtils.release(dataBuffer); bodyRef.set(charBuffer.toString()); });//读取request body到缓存 String bodyStr = bodyRef.get();//获取request body DataBuffer bodyDataBuffer = stringBuffer(bodyStr); Flux bodyFlux = Flux.just(bodyDataBuffer); request = new ServerHttpRequestDecorator(request) { @Override public Flux getBody() { return bodyFlux; } }; } return chain.filter(exchange.mutate().request(request).build()); }; } private DataBuffer stringBuffer(String value) { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); buffer.write(bytes); return buffer; } @Override public int getOrder() { return -100; } }
2.网关
@Component @ConfigurationProperties("auth.skip.urls") @Getter @Setter @Slf4j public class AuthorizeFilter implements GlobalFilter, Ordered { /** * 令牌的名字 */ private static final String AUTHORIZE_TOKEN = "Authorization"; private static final String APP_KEY = "joe-plat-dest"; /** * Jwt解析的key */ private static final String JWT_SECRET_KEY = "joemicroservice"; /** * ACCESS_TOKEN key */ private final static String ACCESS_TOKEN_REDIS_KEY = "USER:ACCESS_TOKEN:userInfo:"; private final static String ACCESS_TOKEN_USERID_REDIS_KEY = "USER:ACCESS_TOKEN:userId:"; /** * redis中access_token过期时间 */ private static final Long ACCESS_TOKEN_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L; /** * 接口放行路径 */ private String[] skipAuthUrls; @Autowired private StringRedisTemplate stringRedisTemplate; /** * 服务名 */ @Value("${spring.application.name}") private String applicationName; @Bean @ConditionalOnMissingBean public HttpMessageConverters messageConverters(ObjectProvider> converters) { return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList())); } /** * 全局拦截 * * @param exchange * @param chain * @return */ @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { val traceId = UUID.randomUUID().toString().replace("-", ""); MDC.put("traceId", traceId); MDC.put("requestId", UUID.randomUUID().toString().replace("-", "")); Object uriObj = exchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR); if (uriObj != null) { URI uri = (URI) uriObj; uri = this.upgradeConnection(uri, "http"); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri); } ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); String url = exchange.getRequest().getURI().getPath(); if (!(url.startsWith("berData") || url.endsWith("/push"))) { log.info("接受到的url路径为:" + url); } StringBuffer sb = new StringBuffer(); String urlPrefix = sb.append("/").append(applicationName).toString(); if (url.startsWith(urlPrefix)) { url = url.substring(applicationName.length() + 1); log.info("截取到的url路径为:" + url); } //跳过不需要验证的路径 if (checkSkipAuthUrls(Arrays.asList(skipAuthUrls), url)) { return chain.filter(exchange.mutate().request(exchange.getRequest().mutate() .header("X-TraceId", traceId).build()).build()); } //获取用户令牌信息 //头文件中 log.info("-----------------------获取请求头中的令牌---------------------"); final String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN); final String appId = request.getHeaders().getFirst(APP_KEY); //如果没有令牌,则拦截 if (StringUtils.isEmpty(token)) { log.info("-----------------------未获取到token令牌---------------------"); //设置没有权限的状态码401 return unAuth(exchange, "登录失效,请重新登录", null); } //如果有令牌,则检验令牌是否有效 try { String accessTokenRedisKey = ACCESS_TOKEN_USERID_REDIS_KEY + token; Boolean isExistKey = stringRedisTemplate.hasKey(accessTokenRedisKey); String redisKey = ACCESS_TOKEN_REDIS_KEY + token; Boolean isExistFlag = stringRedisTemplate.hasKey(redisKey); if (BooleanUtils.isTrue(isExistKey) || BooleanUtils.isTrue(isExistFlag)) { //判断用户是否有效,如果无效,返回指定状态码,跳出登陆 if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(ACCESS_TOKEN_USERID_REDIS_KEY + token))) { log.info("客户登陆状态无效,返回401"); return unAuth(exchange, "登录失效,请重新登录", null); } if (BooleanUtils.isFalse(JwtUtils.validateToken(token))) { log.info("客户登陆状态无效,返回403"); return unAuth(exchange, "登录失效,请重新登录", 403); } // stringRedisTemplate.expire(accessTokenRedisKey, ACCESS_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS); // stringRedisTemplate.expire(redisKey, ACCESS_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS); } else { return unAuth(exchange, "登录失效,请重新登录", null); } } catch (Exception e) { log.error("客户登陆失败, e:", e); return unAuth(exchange, "登录失效,请重新登录", null); } //将令牌封装到头文件中 Consumer httpHeaders = httpHeader -> { httpHeader.set(AUTHORIZE_TOKEN, token); httpHeader.set(APP_KEY, appId); }; ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build(); exchange.mutate().request(serverHttpRequest).build(); //有效,放行 return chain.filter(exchange.mutate().request(exchange.getRequest().mutate() .header("X-TraceId", traceId).build()).build()); } private Boolean checkSkipAuthUrls(List skipAuthUrls, String url) { if (skipAuthUrls.contains(url)) { return true; } List superSkipUrlList = skipAuthUrls.stream().filter(x -> x.contains("**")).collect(Collectors.toList()); if (!CollectionUtils.isEmpty(superSkipUrlList)) { for (String x : superSkipUrlList) { if (url.startsWith(x.substring(0, x.indexOf("**")))) { return true; } } } return false; } private Mono unAuth(ServerWebExchange exchange, String msg, Integer errCode) { ServerHttpResponse response = exchange.getResponse(); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); response.setStatusCode(HttpStatus.OK); log.error("[鉴权异常处理]请求路径:{} ,状态码:{}", exchange.getRequest().getPath(), errCode == null ? HttpStatus.UNAUTHORIZED.value() : errCode); return response.writeWith(Mono.fromSupplier(() -> { DataBufferFactory bufferFactory = response.bufferFactory(); return bufferFactory.wrap(JSON.toJSONBytes(ResponseResult.fail( errCode == null ? HttpStatus.UNAUTHORIZED.value() : errCode, msg))); })); } @Override public int getOrder() { return 0; } private URI upgradeConnection(URI uri, String scheme) { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(uri).scheme(scheme); if (uri.getRawQuery() != null) { uriComponentsBuilder.replaceQuery(uri.getRawQuery().replace("+", "%20")); } return uriComponentsBuilder.build(true).toUri(); } }