gateway 集成springsecurity 登录时获取post请求参数
1. 配置一个过滤器,手动转换body数据,修改request,这个过滤器需要放在真正操作post参数的filter之前,这个过滤器也只拦截登录方法
@Component
public class CacheBodyWebFilter extends AbstractMatchWebFilter {
@Override
protected Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain) {
HttpMethod method = exchange.getRequest().getMethod();
if (method == null || method.matches("GET") || method.matches("DELETE")) {
return chain.filter(exchange);
}
return DataBufferUtils.join(exchange.getRequest().getBody()).map(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
return bytes;
}).defaultIfEmpty(new byte[0]).flatMap(bytes -> {
DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory();
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
if (bytes.length > 0) {
return Flux.just(dataBufferFactory.wrap(bytes));
}
return Flux.empty();
}
};
return chain.filter(exchange.mutate().request(decorator).build());
});
}
@Override
protected boolean shouldFilter(ServerWebExchange serverWebExchange) {
String requestUrl = serverWebExchange.getRequest().getURI().getPath();
return super.match(requestUrl, "/login");
}
}
此处是获取参数的工具方法:
public class RequestUtils {
public static Map<String,String> getFormMap(ServerHttpRequest serverHttpRequest){
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
Consumer<? super DataBuffer> c = (b -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(b.asByteBuffer());
DataBufferUtils.release(b);
bodyRef.set(charBuffer.toString());
});
body.subscribe(c);
String rspStr = bodyRef.get();
if(StringUtils.isEmpty(rspStr)){
return null;
}
return getFormMap(rspStr);
}
private static Map<String, String> getFormMap(String body) {
if(StringUtils.isEmpty(body)){
return null;
}
Map<String,String> map = new HashMap<>();
String[] params= body.split("&"); // name=123&age=11 ==> ['name=123','age=11']
for(String param : params){
String[] par = param.split("="); // name=123 ==> ['name','123']
if(par!=null && par.length>1){
map.put(par[0],par[1]);
}
}
return map;
}
}
2.需要获取请求参数的过滤器。(我这里是需要实现控制登录失败的次数)
@Component
public class LoginTimesFilter extends AbstractMatchWebFilter {
@Override
protected Mono<Void> doFilter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain){
//获取post请求的参数map
Map<String, String> formMap = RequestUtils.getFormMap(serverWebExchange.getRequest());
//我这里主要是处理登录失败次数限制。。。。
.....
}
}
3.之前的CacheBodyWebFilter因为已经读取了body,所以后边springsecurity加载用户的ReactiveUserDetailsService.findByUsername就无法自动获取用户名称了
public class ReactiveUserDetailsServiceImpl implements ReactiveUserDetailsService {
@Override
public Mono<UserDetails> findByUsername(String username) {
//此处username 为空
}
}
此时需要重写 ServerAuthenticationConverter ,手动获取请求参数
public class FormLoginAuthenticationConverter extends ServerFormLoginAuthenticationConverter {
private ServerHttpBasicAuthenticationConverter serverHttpBasicAuthenticationConverter = new ServerHttpBasicAuthenticationConverter();
@Override
public Mono<Authentication> convert(ServerWebExchange exchange) {
String uri = exchange.getRequest().getURI().getPath();
if("/login".equals(uri)){ //登录操作才对body做特殊操作,其他请求直接调用原有请求
UsernamePasswordAuthenticationToken authentication = createAuthentication(exchange);
if(authentication==null){
return Mono.empty();
}
return Mono.just(authentication);
}else{ //非登录操作,基本不用在网关里读取body,默认方法就行
return serverHttpBasicAuthenticationConverter.convert(exchange);
}
}
private UsernamePasswordAuthenticationToken createAuthentication(ServerWebExchange exchange) {
//获取post请求的参数map
Map<String, String> formMap = RequestUtils.getFormMap(exchange.getRequest());
if(CollectionUtils.isEmpty(formMap)){
return null;
}
String username = formMap.get("username");
String password = formMap.get("password");
return new UsernamePasswordAuthenticationToken(username, password);
}
}
把ServerAuthenticationConverter放入AuthenticationWebFilter
SecurityWebFilterChain chain = http.build();
Iterator<WebFilter> weIterable = chain.getWebFilters().toIterable().iterator();
while (weIterable.hasNext()) {
WebFilter f = weIterable.next();
if (f instanceof AuthenticationWebFilter) {
AuthenticationWebFilter webFilter = (AuthenticationWebFilter) f;
//将自定义的AuthenticationConverter添加到过滤器中
webFilter.setServerAuthenticationConverter(new FormLoginAuthenticationConverter());
}
}
这样登录的请求就完成了获取post请求参数,处理,登录的过程,而其他的请求则不受影响