我想在身份验证失败时发送自定义错误消息
我正在使用以下方法来验证用户是否存在于数据库中以生成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;
}
}
}
如何为错误响应自定义消息?
答案 0 :(得分:0)
您可以自定义UsernamePasswordAuthenticationFilter
的内部AuthenticationFailureHandler
。当onAuthenticationFailure()
未能通过身份验证并抛出attemptAuthentication()
时,将调用其AuthenticationException
。
默认实现为SimpleUrlAuthenticationFailureHandler
,它仅将响应状态设置为401。您可以简单地扩展它,并将响应消息添加到HttpServletResponse
,如下所示:
public MySimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler{
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
if (defaultFailureUrl == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Your Response Message blablala");
}else{
super.onAuthenticationFailure(request,response, exception);
}
}
}
当然,您必须设置为在创建AuthenticationFailureHandler
之后使用自定义的JwtUsernameAndPasswordAuthenticationFilter
。
您还需要更改以将AuthenticationException
而不是RuntimeException
扔到attemptAuthentication()
中。