【问题标题】:Spring reactive securitySpring 反应式安全性
【发布时间】:2020-07-26 13:47:45
【问题描述】:

我正在尝试响应式安全性,未经身份验证的调用不会转到身份验证管理器。

@Configuration
@EnableWebFluxSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig{
    @Autowired
    private WebAuthenticationManager authenticationManager;

    @Autowired
    private ServerSecurityContextRepository securityContextRepository;

    private static final String[] AUTH_WHITELIST = {
            "/login/**",
            "/logout/**",
            "/authorize/**",
            "/favicon.ico",
    };

    @Bean
    public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
        return http.exceptionHandling().authenticationEntryPoint((swe, e) -> {
            return Mono.fromRunnable(() -> {
                swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            });
        }).accessDeniedHandler((swe, e) -> {
            return Mono.fromRunnable(() -> {
                swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            });
        }).and().csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .authenticationManager(authenticationManager)
                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
                .authorizeExchange().pathMatchers(HttpMethod.OPTIONS).permitAll()
                .pathMatchers(AUTH_WHITELIST).permitAll()
                .anyExchange().authenticated().and().build();
    }

    @Bean
    public PBKDF2Encoder passwordEncoder() {
        return new PBKDF2Encoder();
    }


}

WebAuthentication 管理器,

@Component
public class WebAuthenticationManager implements ReactiveAuthenticationManager {

    @Autowired
    private JWTUtil jwtUtil;

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        String authToken = authentication.getCredentials().toString();

        String username;
        try {
            username = jwtUtil.getUsernameFromToken(authToken);
        } catch (Exception e) {
            username = null;
        }
        if (username != null && jwtUtil.validateToken(authToken)) {
            Claims claims = jwtUtil.getAllClaimsFromToken(authToken);
            List<String> rolesMap = claims.get("role", List.class);
            List<Role> roles = new ArrayList<>();
            for (String rolemap : rolesMap) {
                roles.add(Role.valueOf(rolemap));
            }
            UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                username,
                null,
                roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList())
            );
            return Mono.just(auth);
        } else {
            return Mono.empty();
        }
    }
}

在这里,我已经在 Securityconfig 中注册了我的 WebAuthentication 管理器。但是,未经身份验证的调用仍然不会转到 WebAuthenticationManager。

当受保护的 URL 被命中时,它应该会转到 AuthenticationManager。例如,

http://localhost:8080/api/v1/user.

不确定,为什么调用不转到 AuthManager。

在非响应式中,我们有 OncePerRequestFilter 并且正在那里处理身份验证。不确定,如何为响应式实现相同的。

【问题讨论】:

    标签: spring-boot spring-security spring-reactive


    【解决方案1】:

    您禁用了所有身份验证机制,因此没有调用您的身份验证管理器。正如您所提到的,您可以通过过滤器实现身份验证流程。

    身份验证过滤器的示例实现:

        @Bean
        public AuthenticationWebFilter webFilter() {
        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(authenticationManager);
        authenticationWebFilter.setServerAuthenticationConverter(tokenAuthenticationConverter());
        authenticationWebFilter.setRequiresAuthenticationMatcher(serverWebExchangeMatcher());
        authenticationWebFilter.setSecurityContextRepository(NoOpServerSecurityContextRepository.getInstance());
        return authenticationWebFilter;
    }
    

    然后将此过滤器添加到ServerHttpSecurity:http.addFilterBefore(webFilter(),SecurityWebFiltersOrder.HTTP_BASIC)

    然后最终将调用您的身份验证管理器。


    您必须提供一些额外的东西才能使其正常工作。
    匹配器检查是否将Authorization 标头添加到请求中:

        @Bean
        public ServerWebExchangeMatcher serverWebExchangeMatcher() {
        return exchange -> {
            Mono<ServerHttpRequest> request = Mono.just(exchange).map(ServerWebExchange::getRequest);
            return request.map(ServerHttpRequest::getHeaders)
                    .filter(h -> h.containsKey(HttpHeaders.AUTHORIZATION))
                    .flatMap($ -> ServerWebExchangeMatcher.MatchResult.match())
                    .switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch());
        };
    }
    

    令牌转换器负责从请求中获取令牌并准备基本的AbstractAuthenticationToken

        @Bean
        public ServerAuthenticationConverter tokenAuthenticationConverter() {
        return exchange -> Mono.justOrEmpty(exchange)
                .map(e -> getTokenFromRequest(e))
                .filter(token -> !StringUtils.isEmpty(token))
                .map(token -> getAuthentication(token));
    }
    

    我故意省略了getTokenFromRequestgetAuthentication 的实现,因为有很多可用的示例。

    【讨论】:

    • tokenAuthenticationConverter 为每个请求调用两次。另外,我怎样才能在我的休息控制器中获得索赔?
    猜你喜欢
    • 2018-10-30
    • 2021-07-16
    • 2020-07-19
    • 2020-10-27
    • 2019-07-26
    • 2019-06-13
    • 1970-01-01
    • 1970-01-01
    • 2018-01-17
    相关资源
    最近更新 更多