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();
    }

}
相关推荐
2501_9061505614 分钟前
私有部署问卷系统操作实战记录-DWSurvey
java·运维·服务器·spring·开源
better_liang26 分钟前
每日Java面试场景题知识点之-TCP/IP协议栈与Socket编程
java·tcp/ip·计算机网络·网络编程·socket·面试题
VX:Fegn089537 分钟前
计算机毕业设计|基于springboot + vue校园社团管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
niucloud-admin38 分钟前
java服务端——controller控制器
java·开发语言
To Be Clean Coder39 分钟前
【Spring源码】通过 Bean 工厂获取 Bean 的过程
java·后端·spring
Fortunate Chen1 小时前
类与对象(下)
java·javascript·jvm
程序员水自流1 小时前
【AI大模型第9集】Function Calling,让AI大模型连接外部世界
java·人工智能·llm
‿hhh1 小时前
综合交通运行协调与应急指挥平台项目说明
java·ajax·npm·json·需求分析·个人开发·规格说明书
小徐Chao努力1 小时前
【Langchain4j-Java AI开发】06-工具与函数调用
java·人工智能·python
无心水1 小时前
【神经风格迁移:全链路压测】33、全链路监控与性能优化最佳实践:Java+Python+AI系统稳定性保障的终极武器
java·python·性能优化