为了统一接口请求格式,要将Spring Security获取token接口改成接收JSON格式,如下是我的几种尝试,最后一种为简单有效办法。
在Spring Cloud Gateway处理JSON转application/x-www-form-urlencoded(无效)
代码是这样的
代码语言:javascript复制@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;
}
}
原以为将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 路由跳转形式(无效)
代码语言:javascript复制@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();
}
}
stackoverflow 提供的方式(无效)
这种办法根本不进Filter,在独立应用的时候也测试了 可见地址:https://stackoverflow.com/questions/38165131/spring-security-oauth2-accept-json
包装 oauth/token接口(有效)
代码语言:javascript复制 @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();
}
新写一个REST接口,调用TokenEndpoint 的postAccessToken方法,还是这种办法最简单有效。
这里我通过TokenEndpoint 直接调用了postAccessToken方法,而不是采用Http请求oauth/token再次自我请求的方式,这种方式显得更优雅,性能也更高。