[CustomerWebFilter类]在筛选器类中,我需要重新检查不需要任何身份验证或授权的url。我不希望对指定的url(如登录等)调用筛选器方法。筛选器应仅在成功登录后调用。
[使用ServerHttpSecurity配置安全类]这里我已经放置了http.addFilterAfter(customerWebFilter,SecurityWebFiltersOrder.AUTHORIZATION);
P.S.下面描述的流程是工作,但我想即兴发挥。
@Log4j2
@Component
@RequiredArgsConstructor
public class CustomerWebFilter implements WebFilter {
private final AccountRoleMenuViewEntityService accountRoleMenuViewEntityService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
final List<String> ALLOWED_ENDPOINTS = Arrays.asList("/assets/", "/bootstrap/", "/plugins/");
final List<String> ALLOWED_URL = Arrays.asList("/login", "/logout",
"/forgotpassword", "/sendcode", "/verifycode", "/newpassword", "/razorpay-webhook");
if (ALLOWED_ENDPOINTS.parallelStream().anyMatch(allowedWords ->
StringUtils.startsWith(request.getPath().pathWithinApplication().value(), allowedWords))) {
return chain.filter(exchange);
}
if (ALLOWED_URL.parallelStream().anyMatch(allowedWords ->
StringUtils.equalsIgnoreCase(request.getPath().pathWithinApplication().value(), allowedWords))) {
return chain.filter(exchange);
}
log.debug("Inside filter request: {} ", request.getPath().pathWithinApplication());
String allowedPrefix = request.getPath().pathWithinApplication().value();
if (allowedPrefix != "/" && allowedPrefix.length() > 1) {
String[] urlComponents = request.getPath().pathWithinApplication().value().split("/");
allowedPrefix = "/" + urlComponents[1] + "/" + urlComponents[2];
}
AtomicReference<String> value = new AtomicReference<>(allowedPrefix);
return exchange.getPrincipal()
.flatMap(principal -> {
log.debug("principal.getName(): {}", principal.getName());
AtomicInteger runCount = new AtomicInteger(0);
if (value.get().equalsIgnoreCase("/")) {
value.set("/customer/dashboard");
}
return accountRoleMenuViewEntityService.findByUsername(principal.getName())
.flatMap(accountRoleMenuView -> {
log.debug("controller: {}", accountRoleMenuView.getController());
if (accountRoleMenuView.getController().contains("/")) {
String[] controllerComponents = accountRoleMenuView.getController().split("/");
String controllerPrefix = "/" + controllerComponents[1] + "/" + controllerComponents[2];
log.debug("controllerPrefix: {} value: {}", controllerPrefix, value.get());
if (controllerPrefix.equalsIgnoreCase(value.get())) {
runCount.getAndIncrement();
}
}
return Mono.just(runCount);
}).count()
.flatMap(count -> {
if (runCount.get() == 0) {
response.setStatusCode(HttpStatus.FORBIDDEN);
}
return chain.filter(exchange);
});
});
}
}
@Configuration
@EnableWebFluxSecurity
@RequiredArgsConstructor
public class ConfigurationSecurity {
private final CustomerWebFilter customerWebFilter;
private final RememberMeSuccessHandler rememberMeSuccessHandler;
@Bean
public SecurityWebFilterChain configure(ServerHttpSecurity http) {
http.csrf().disable();
http.headers().frameOptions().disable();
http.authorizeExchange().pathMatchers("/login", "/logout", "/assets/**", "/bootstrap/**", "/plugins/**",
"/forgotpassword", "/sendcode", "/verifycode", "/newpassword", "/razorpay-webhook").permitAll();
http.authorizeExchange().pathMatchers("/customer/**").hasRole("CUSTOMER");
http.authorizeExchange().anyExchange().authenticated();
http.addFilterAfter(customerWebFilter, SecurityWebFiltersOrder.AUTHORIZATION);
http.formLogin()
.loginPage("/login")
.authenticationSuccessHandler(rememberMeSuccessHandler)
.authenticationFailureHandler(new AuthenticationFailureHandler())
.and()
.exceptionHandling()
.accessDeniedHandler(new AccessDeniedHandler())
.and()
.logout()
.logoutUrl("/logout")
.requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout"))
.and()
.httpBasic();
return http.build();
}
@Bean
ReactiveAuthenticationManager reactiveAuthenticationManager(AccountDataService accountDataService) {
return new UserDetailsRepositoryReactiveAuthenticationManager(accountDataService);
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
1条答案
按热度按时间oprakyz71#
我认为可能对过滤器链的工作原理有误解。
通过注册一个
SecurityWebFilterChain
的bean,您已经创建了一个带有特定ServerHttpSecurity
对象的过滤器链,该对象应用了为该链定义的规则,并且这些规则在默认情况下 * 应用于每个请求,但是可以使用securityMatcher(ServerWebExchangeMatcher)
进行限制(您可以在此处查看有关此方法的文档)。当安全上下文保持器中存在
Authentication
对象时,在ServerHttpSecurity.authorizeExchange()
调用之后定义的规则将在过滤器链之后应用,可以检查角色的存在,以决定是否允许访问特定端点。因此,如果您希望在应用程序中为不同的url设置不同的安全过滤器链,您有两种选择:
1.定义多个
SecurityWebFilterChain
Bean:一个用于需要身份验证和自定义筛选器的路径,另一个用于不需要身份验证和自定义筛选器的路径。您可以在此处查看有关多个筛选器链支持的文档和示例,以及securityMatcher
方法的用法。1.在过滤器内创建规则以定义是否必须应用该规则。
我想你已经实现了第二种方法。改进建议--提取一个类似
shouldFilter
的方法,使用ServerWebExchangeMatcher
将两个String数组合并为一个,并使用相同的方法比较交换路径与匹配器,包括带有/**通配符的路径,例如: