Написав собственный AuthenticationProvider (который вызывает сервис, после которого тот вызывает внешний URL-адрес для аутентификации заданных учетных данных), я хотел настроить сообщение об ошибке, которое люди получают при сбое аутентификации, на основе сообщения, которое я передаю экземпляр AuthenticationException (поэтому e.getMessage()
передано BadCredentialsExceptoin
в приведенном ниже коде).
@Component
public class TapasAuthenticationProvider implements AuthenticationProvider {
@Override
public TapasAuthentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = authentication.getName();
String password = authentication.getCredentials().toString();
try {
AuthenticationResponse authResponse = authenticationService.authenticate(userName, password);
return new TapasAuthentication(mapToAuthenticatedUser(authResponse, userName, password), password);
} catch (AuthenticationFailedException e) {
// Note that AuthenticationFailedException is a self-written exception, thrown by the authenticationService.
log.error("Authentication failed: ", e);
throw new BadCredentialsException(e.getMessage());
}
}
}
Теперь я посмотрел, как отобразить AuthenticationException
s, и обнаружил, что для этого следует использовать AuthenticationEntryPoint
. Поэтому я создал его и добавил в свой SecuritySetup:
@Component
public class TapasAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
System.out.println(authException.getMessage());
// More code to be added once exception is what I expect.
}
}
@Autowired
private TapasAuthenticationEntryPoint authenticationEntryPoint;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.authorizeRequests().anyRequest().authenticated().and().httpBasic()
.and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf().disable();
}
Это успешно вызывает AuthenticationEntryPoint
, но вместо BadCredentialsException
я получаю InsufficientAuthenticationException
. Я проверил происхождение этого исключения, и оно исходит из исключения ExceptionTranslationFilter
handleSpringSecurityException. Здесь исключение оказывается AccessDeniedException
вместо AuthenticationException
.
private void handleSpringSecurityException(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, RuntimeException exception)
throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
// I would except to enter this if-statement, but exception is not what I expect
sendStartAuthentication(request, response, chain,
(AuthenticationException) exception);
}
else if (exception instanceof AccessDeniedException) {
....
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {
// Instead code comes here, and creates an InsufficientAuthenticationException.
sendStartAuthentication(
request,
response,
chain,
new InsufficientAuthenticationException(
messages.getMessage(
"ExceptionTranslationFilter.insufficientAuthentication",
"Full authentication is required to access this resource")));
}
...
}
}
Почему исключение не совпадает с моим исключением, выданным в AuthenticationProvider
? И как я смогу передать данные из AuthenticationProvider
обратно пользователю?