![](/img/trans.png)
[英]Custom failureHandler throws 401 in Spring security login
[英]Reactive Spring Security throws 500 and console stacktrace instead of 401
我將反應式安全與 JWT 一起使用。 此構建有效,盡管當提供無效憑據時它返回 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"
}
並在控制台中打印堆棧跟蹤:
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]
這是登錄controller:
@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()
有異常拋出,但它不起作用。
先感謝您!
所以解決方案是創建新的身份驗證管理器 class 擴展內置的一個,如下所示:
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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.