检查 Spring 网关预过滤器中的数据库访问
Check database for access in Spring Gateway Pre filter
我正在使用 Spring 网关,我需要使用数据库调用通过请求路径进一步检查用户访问。我的仓库是这样的。
public Mono<ActionMapping> getByUri(String url)
....
这是我当前使用自定义 UsernamePasswordAuthenticationToken
实现的过滤器。
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> exchange
.getPrincipal()
.filter(principal -> principal instanceof UserAuthenticationToken) // Custom implementation of UsernamePasswordAuthenticationToken
.cast(UserAuthenticationToken.class)
.map(userAuthenticationToken -> extractAuthoritiesAndSetThatToRequest(exchange, userAuthenticationToken))
.defaultIfEmpty(exchange)
.flatMap(chain::filter);
}
private ServerWebExchange extractAuthoritiesAndSetThatToRequest(ServerWebExchange exchange, UserAuthenticationToken authentication) {
var uriActionMapping = uriActionMappingRepository.findOneByUri(exchange.getRequest().getPath().toString()).block();
if ((uriActionMapping == null) || (authentication.getPermission().containsKey(uriActionMapping.getName()))) {
ServerHttpRequest request = exchange.getRequest()
.mutate()
.header("X-Auth", authentication.getName())
.build();
return exchange.mutate().request(request).build();
}
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.setComplete();
return exchange.mutate().response(response).build();
}
但是,这里有几个问题,首先是阻塞调用。此外,我不确定是否需要将 exchange
更改为 return 这样的响应。无论如何,在 Spring 云网关中使用过滤器可以实现这一点。
是的,这是一个阻塞调用。
首先,SpringWebFlux是基于Reactor的。在 Reactor 中,大多数处理方法不会从 Mono
emit 接收到 null
,例如map
、flatMap
。当然也有反例,比如doOnSuccess
,又见javadoc of Mono
.
所以,我们可以只使用处理方法来过滤结果,而不是 block
。当收到 null
值时,这些处理方法将 return 为空 Mono
。
其次,当授权失败时,我们应该return一个空的Mono而不是调用chain.filter
。 chain.filter
的意思是“没关系!在过滤器之后做点什么!”。另请参阅 RequestRateLimiterGatewayFilterFactory
,它也会变异 response
。
因此,我们应该将 response
设置为已完成,如果授权失败,则 return 设置为空 Mono
。
试试这个:
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> exchange
.getPrincipal()
.filter(principal -> principal instanceof UserAuthenticationToken) // Custom implementation of UsernamePasswordAuthenticationToken
.cast(UserAuthenticationToken.class)
.flatMap(userAuthenticationToken -> extractAuthoritiesAndSetThatToRequest(exchange, userAuthenticationToken))
.switchIfEmpty(Mono.defer(() -> exchange.getResponse().setComplete().then(Mono.empty())))
.flatMap(chain::filter);
}
// Maybe return empty Mono, e.g. findOneByUri not found, or Permissions does not containing
private Mono<ServerWebExchange> extractAuthoritiesAndSetThatToRequest(ServerWebExchange exchange, UserAuthenticationToken authentication) {
return uriActionMappingRepository.findOneByUri(exchange.getRequest().getPath().toString())
.filter(it -> authentication.getPermission().containsKey(it.getName()))
.map(it -> exchange.mutate()
.request(builder -> builder.header("X-Auth", authentication.getName()))
.build());
}
关于变异 request
,另请参阅 RewritePathGatewayFilterFactory
。
我正在使用 Spring 网关,我需要使用数据库调用通过请求路径进一步检查用户访问。我的仓库是这样的。
public Mono<ActionMapping> getByUri(String url)
....
这是我当前使用自定义 UsernamePasswordAuthenticationToken
实现的过滤器。
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> exchange
.getPrincipal()
.filter(principal -> principal instanceof UserAuthenticationToken) // Custom implementation of UsernamePasswordAuthenticationToken
.cast(UserAuthenticationToken.class)
.map(userAuthenticationToken -> extractAuthoritiesAndSetThatToRequest(exchange, userAuthenticationToken))
.defaultIfEmpty(exchange)
.flatMap(chain::filter);
}
private ServerWebExchange extractAuthoritiesAndSetThatToRequest(ServerWebExchange exchange, UserAuthenticationToken authentication) {
var uriActionMapping = uriActionMappingRepository.findOneByUri(exchange.getRequest().getPath().toString()).block();
if ((uriActionMapping == null) || (authentication.getPermission().containsKey(uriActionMapping.getName()))) {
ServerHttpRequest request = exchange.getRequest()
.mutate()
.header("X-Auth", authentication.getName())
.build();
return exchange.mutate().request(request).build();
}
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.setComplete();
return exchange.mutate().response(response).build();
}
但是,这里有几个问题,首先是阻塞调用。此外,我不确定是否需要将 exchange
更改为 return 这样的响应。无论如何,在 Spring 云网关中使用过滤器可以实现这一点。
是的,这是一个阻塞调用。
首先,SpringWebFlux是基于Reactor的。在 Reactor 中,大多数处理方法不会从 Mono
emit 接收到 null
,例如map
、flatMap
。当然也有反例,比如doOnSuccess
,又见javadoc of Mono
.
所以,我们可以只使用处理方法来过滤结果,而不是 block
。当收到 null
值时,这些处理方法将 return 为空 Mono
。
其次,当授权失败时,我们应该return一个空的Mono而不是调用chain.filter
。 chain.filter
的意思是“没关系!在过滤器之后做点什么!”。另请参阅 RequestRateLimiterGatewayFilterFactory
,它也会变异 response
。
因此,我们应该将 response
设置为已完成,如果授权失败,则 return 设置为空 Mono
。
试试这个:
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> exchange
.getPrincipal()
.filter(principal -> principal instanceof UserAuthenticationToken) // Custom implementation of UsernamePasswordAuthenticationToken
.cast(UserAuthenticationToken.class)
.flatMap(userAuthenticationToken -> extractAuthoritiesAndSetThatToRequest(exchange, userAuthenticationToken))
.switchIfEmpty(Mono.defer(() -> exchange.getResponse().setComplete().then(Mono.empty())))
.flatMap(chain::filter);
}
// Maybe return empty Mono, e.g. findOneByUri not found, or Permissions does not containing
private Mono<ServerWebExchange> extractAuthoritiesAndSetThatToRequest(ServerWebExchange exchange, UserAuthenticationToken authentication) {
return uriActionMappingRepository.findOneByUri(exchange.getRequest().getPath().toString())
.filter(it -> authentication.getPermission().containsKey(it.getName()))
.map(it -> exchange.mutate()
.request(builder -> builder.header("X-Auth", authentication.getName()))
.build());
}
关于变异 request
,另请参阅 RewritePathGatewayFilterFactory
。