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

}
相关推荐
众拾达人30 分钟前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.32 分钟前
Mybatis-Plus
java·开发语言
不良人天码星32 分钟前
lombok插件不生效
java·开发语言·intellij-idea
守护者1701 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
源码哥_博纳软云1 小时前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
禾高网络1 小时前
租赁小程序成品|租赁系统搭建核心功能
java·人工智能·小程序
学会沉淀。1 小时前
Docker学习
java·开发语言·学习
如若1231 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
追逐时光者1 小时前
免费、简单、直观的数据库设计工具和 SQL 生成器
后端·mysql