spring 请求等问题

复制代码
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();
    }

}
相关推荐
葫芦和十三5 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp6 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑6 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯7 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan9 小时前
多Agent之间的区别
后端
青石路11 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充12 小时前
1.面向对象设计思想
后端
IT_陈寒12 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro12 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗13 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端