Я хотел бы отправить пользовательское сообщение об ошибке при сбое аутентификации
Я использую следующий метод, чтобы проверить, существует ли пользователь в базе данных для генерации jwt, если проверка не удалась, она возвращает ошибку 401, но полученный ответ пуст, я хотел бы добавить сообщение с причина в зависимости от ошибки.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, LockedException {
final AppUser user = userService.findByUsername(username);
if (user != null) {
if(user.isEnabled()) {
List<GrantedAuthority> grantedAuthorities = AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_" + user.getRole());
return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
}else{
throw new LockedException("Username: " + username + " is Locked");
}
}
// If user not found. Throw this exception.
throw new UsernameNotFoundException("Username: " + username + " not found");
}
Это конфигурация безопасности:
@EnableWebSecurity // Enable security config. This annotation denotes config for spring security.
public class SecurityCredentialsConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtConfig jwtConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED,"your message goes here"))
.and()
.addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig))
.authorizeRequests()
// allow all POST requests
.antMatchers(HttpMethod.POST, jwtConfig.getUri()).permitAll()
.antMatchers(HttpMethod.POST, jwtConfig.getSignUpUri()).permitAll()
.antMatchers(HttpMethod.GET, jwtConfig.getValidateEmailUri()).permitAll()
// any other requests must be authenticated
.anyRequest().authenticated();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public JwtConfig jwtConfig() {
return new JwtConfig();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
А это jwtfilter:
public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
// We use auth manager to validate the user credentials
private AuthenticationManager authManager;
private final JwtConfig jwtConfig;
public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authManager, JwtConfig jwtConfig) {
this.authManager = authManager;
this.jwtConfig = jwtConfig;
// By default, UsernamePasswordAuthenticationFilter listens to "/login" path.
// In our case, we use "/auth". So, we need to override the defaults.
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(jwtConfig.getUri(), "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try {
// 1. Get credentials from request
UserCredentials creds = new ObjectMapper().readValue(request.getInputStream(), UserCredentials.class);
// 2. Create auth object (contains credentials) which will be used by auth manager
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
creds.getUsername(), creds.getPassword(), Collections.emptyList());
// 3. Authentication manager authenticate the user, and use UserDetialsServiceImpl::loadUserByUsername() method to load the user.
return authManager.authenticate(authToken);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// Upon successful authentication, generate a token.
// The 'auth' passed to successfulAuthentication() is the current authenticated user.
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication auth) throws IOException, ServletException {
Long now = System.currentTimeMillis();
String token = Jwts.builder()
.setSubject(auth.getName())
// Convert to list of strings.
// This is important because it affects the way we get them back in the Gateway.
.claim("authorities", auth.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.setIssuedAt(new Date(now))
.setExpiration(new Date(now + jwtConfig.getExpiration() * 1000)) // in milliseconds
.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret().getBytes())
.compact();
// Add token to header
response.addHeader("Access-Control-Expose-Headers", "authorization");
// response.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
response.addHeader(jwtConfig.getHeader(), jwtConfig.getPrefix() + token);
}
// A (temporary) class just to represent the user credentials
private static class UserCredentials {
private String username, password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
}
Как настроить сообщение для ответа об ошибке?