• 【疑难杂症】-一种简单高效的Spring Security oauth token兼容JSON格式的办法


    在这里插入图片描述

    为了统一接口请求格式,要将Spring Security获取token接口改成接收JSON格式,如下是我的几种尝试,最后一种为简单有效办法。

    在Spring Cloud Gateway处理JSON转application/x-www-form-urlencoded(无效)

    代码是这样的

    @Component
    @RequiredArgsConstructor(onConstructor_ = @Autowired)
    public class PlatformRequestGlobalFilter implements GlobalFilter, Ordered {
    
        @Value("${project.gateway.filter.order: -10}")
        private int order;
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            return DataBufferUtils.join(request.getBody())
                    .flatMap(dataBuffer -> {
                        byte[] bytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(bytes);
                        String json = new String(bytes, StandardCharsets.UTF_8);
                        DataBufferUtils.release(dataBuffer);
                        try {
                            //json参数转换
                            HashMap<String, String> result =  new ObjectMapper().readValue(json, HashMap.class);
                            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                                @Override
                                public HttpHeaders getHeaders() {
                                    HttpHeaders httpHeaders = new HttpHeaders();
                                    httpHeaders.putAll(super.getHeaders());
                                    httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                                    return httpHeaders;
                                }
    
                                @Override
                                public URI getURI() {
                                    //json参数转换成key=value&key1=value1形式
                                    String params = mapToString(result);
                                    //只是测试,地址写死了,参数是拼接的
                                    return URI.create("http://localhost:8010/oauth/token?" + params);
                                }
                            };
                            //替换request
                            return chain.filter(exchange.mutate().request(mutatedRequest).build());
                        } catch (JsonProcessingException e) {
                            e.printStackTrace();
                        }
                        
                        return chain.filter(exchange);
    
                    });
        }
    
        private String mapToString(HashMap<String, String> map) {
            final StringBuilder strBuilder = StrUtil.builder();
            boolean isFirst = true;
            if (MapUtil.isNotEmpty(map)) {
                for (Entry<String, String> entry : map.entrySet()) {
                    if (entry.getKey() != null && entry.getValue() != null) {
                        if (isFirst) {
                            isFirst = false;
                        } else {
                            strBuilder.append("&");
                        }
                        strBuilder.append(CharSequenceUtil.toUnderlineCase(entry.getKey())).append("=").append(Convert.toStr(entry.getValue()));
                    }
                }
            }
            return strBuilder.toString();
        }
    
        @Override
        public int getOrder() {
            return order;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    原以为将json请求在网关层替换为application/x-www-form-urlencoded格式,并将参数转换key=value&key1=value1形式,拼接最后的请求地址
    http://localhost:8010/oauth/token?key=value&key1=value1,这样就可以生效了,但测试后会报:java.lang.IllegalStateException: completed 错误

    Filter RouteLocator 路由跳转形式(无效)

    @Configuration
    public class FilterConfig {
    
        @Bean
        public RouteLocator routes(RouteLocatorBuilder builder, ObjectMapper objectMapper) {
            return builder
                    .routes()
                    .route("path_route_change",
                            r -> r.path("/oauth/token")
                                    .filters(f -> f
                                            .modifyRequestBody(String.class,String.class,"application/x-www-form-urlencoded",new RequestBodyRewrite(objectMapper))
                                    )
                                    .uri("http://localhost:8010/oauth/token"))
                    .build();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    stackoverflow 提供的方式(无效)

    这种办法根本不进Filter,在独立应用的时候也测试了
    可见地址:https://stackoverflow.com/questions/38165131/spring-security-oauth2-accept-json

    包装 oauth/token接口(有效)

    	@PostMapping("oauth/api/token")
    	public OAuth2AccessToken getToken(@Valid @RequestBody AuthTokenReq authTokenReq) {
    		Map<String, String> params = new HashMap<>();
    		params.put("grant_type", authTokenReq.getGrantType());
    		params.put("client_id", authTokenReq.getClientId());
    		params.put("client_secret", authTokenReq.getClientSecret());
    
    		UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(authTokenReq.getClientId(), authTokenReq.getClientSecret(), new ArrayList<>());
    
    		ResponseEntity<OAuth2AccessToken> oAuth2AccessToken = tokenEndpoint.postAccessToken(usernamePasswordAuthenticationToken, params);
    
    		return oAuth2AccessToken.getBody();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    新写一个REST接口,调用TokenEndpoint 的postAccessToken方法,还是这种办法最简单有效。

    这里我通过TokenEndpoint 直接调用了postAccessToken方法,而不是采用Http请求oauth/token再次自我请求的方式,这种方式显得更优雅,性能也更高。

  • 相关阅读:
    SwiftUI 通过 ARKit 使用微笑控制 SwiftUI 视图(教程含源码)
    Windows操作系统下用vmware虚拟ubuntu系统测试USB IC卡读写器的说明
    C#/VB.NET 创建PDF/UA文件
    Java多线程(三)
    kafka生产者和消费者(python版)
    【谷粒学院】Maven加载问题
    linux 的文件权限案列
    太空 5G 在启动板上
    猿创征文|【Webpack】 Vue项目维护难?看完Webpack就不难了!
    通过粒子群优化算法辨识Hammerstein模型参数,并对比最小二乘法(含完整matlab代码)
  • 原文地址:https://blog.csdn.net/weixin_40972073/article/details/125419193