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请求
Flux<DataBuffer> body = request.getBody();
AtomicReference<String> 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<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
request = new ServerHttpRequestDecorator(request) {
@Override
public Flux<DataBuffer> 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<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}
/**
* 全局拦截
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> 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> 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<String> skipAuthUrls, String url) {
if (skipAuthUrls.contains(url)) {
return true;
}
List<String> 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<Void> 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();
}
}