被动spring安全抛出500和控制台stacktrace,而不是401

koaltpgm  于 2021-07-15  发布在  Java
关注(0)|答案(1)|浏览(343)

我在jwt使用React式安全。此构建可以工作,但当提供无效凭据时,它会返回json:

{
    "timestamp": "2021-03-24T12:44:44.540+00:00",
    "path": "/auth/login",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Invalid Credentials",
    "requestId": "cfa7b741-1"
}

并在控制台中打印stacktrace:

org.springframework.security.authentication.BadCredentialsException: Invalid Credentials
    at org.springframework.security.authentication.AbstractUserDetailsReactiveAuthenticationManager.lambda$authenticate$1(AbstractUserDetailsReactiveAuthenticationManager.java:99) ~[spring-security-core-5.4.5.jar:5.4.5]

以下是登录控制器:

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthAPI {
    private final JwtTokenProvider tokenProvider;
    private final ReactiveAuthenticationManager authenticationManager;

    @PostMapping("/login")
    public Mono<ResponseEntity> login(@RequestBody Mono<AuthRequest> authRequest) {
        return authRequest
                .flatMap(login -> authenticationManager
                        .authenticate(new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword()))
                        .map(tokenProvider::createToken)
                )
                .map(jwt -> {
                            HttpHeaders httpHeaders = new HttpHeaders();
                            httpHeaders.add(HttpHeaders.AUTHORIZATION, "Bearer " + jwt);
                            var tokenBody = Map.of("token", jwt);
                            return new ResponseEntity<>(tokenBody, httpHeaders, HttpStatus.OK);
                        }
                );
    }
}

安全配置:

@Configuration
public class SecurityConfig {

    @Bean
    SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http,
                                                JwtTokenProvider tokenProvider,
                                                ReactiveAuthenticationManager reactiveAuthenticationManager) {

        return http
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
                .authenticationManager(reactiveAuthenticationManager)
                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
                .authorizeExchange(it -> it
                        .pathMatchers("/me").authenticated()
                        .pathMatchers("/users/{user}/**").access(this::currentUserMatchesPath)
                        .anyExchange().permitAll()
                )
                .addFilterAt(new JwtTokenAuthenticationFilter(tokenProvider), SecurityWebFiltersOrder.HTTP_BASIC)
                .build();
    }

    private Mono<AuthorizationDecision> currentUserMatchesPath(Mono<Authentication> authentication,
                                                               AuthorizationContext context) {

        return authentication
                .map(a -> context.getVariables().get("user").equals(a.getName()))
                .map(AuthorizationDecision::new);

    }

    @Bean
    public ReactiveUserDetailsService userDetailsService(UserRepository users) {

        return username -> users.findByUsername(username)
                .map(u -> User
                        .withUsername(u.getUsername()).password(u.getPasswordHash())
                        .authorities(u.getRoles().toArray(new String[0]))
                        .accountExpired(!u.getIsActive())
                        .credentialsExpired(!u.getIsActive())
                        .disabled(!u.getIsActive())
                        .accountLocked(!u.getIsActive())
                        .build()
                );
    }

    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager(ReactiveUserDetailsService userDetailsService,
                                                                       PasswordEncoder passwordEncoder) {
        var authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
        authenticationManager.setPasswordEncoder(passwordEncoder);
        return authenticationManager;
    }
}

jwt过滤器:

@RequiredArgsConstructor
public class JwtTokenAuthenticationFilter implements WebFilter {

    public static final String HEADER_PREFIX = "Bearer ";

    private final JwtTokenProvider tokenProvider;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String token = resolveToken(exchange.getRequest());
        if (StringUtils.hasText(token) && this.tokenProvider.validateToken(token)) {
            Authentication authentication = this.tokenProvider.getAuthentication(token);
            return chain.filter(exchange)
                    .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));
        }
        return chain.filter(exchange);
    }

    private String resolveToken(ServerHttpRequest request) {
        String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(HEADER_PREFIX)) {
            return bearerToken.substring(7);
        }
        return null;
    }

}

我想接收正确的错误代码并编写自己的消息,但找不到配置它的方法。
我试图补充

.exceptionHandling()
.authenticationEntryPoint()
.accessDeniedHandler()

除了投手,但它不起作用。
提前谢谢!

vngu2lb8

vngu2lb81#

因此,解决方案是创建新的身份验证管理器类,扩展内置的身份验证管理器类,如下所示:

public class CustomReactiveAuthManager extends UserDetailsRepositoryReactiveAuthenticationManager {

    public CustomReactiveAuthManager(ReactiveUserDetailsService userDetailsService) {
        super(userDetailsService);
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return super
                .authenticate(authentication)
                ;
    }
}

在安全配置中使用它 var authenticationManager = new CustomReactiveAuthManager(userDetailsService); 或者如@toerktumlare所说,在authapi中提供令牌之前处理错误:

.authenticate(new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword()))
                        .onErrorMap(BadCredentialsException.class, err ->
                                CustomError.authError())
                        .map(tokenProvider::createToken)

相关问题