I have somewhat special case in my spring boot RESTfull project, not the standard customization of error message on auth exception. I need a different message depending if the username or password is wrong, or if username doesn't exists, or if the user was deactivated in the database. Currently I can only get message "Bad credentials"
and I haven't found any solutions how to customize message depending on some user properties or special cases.
I currently have custom authentication provider like this:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication)
throws org.springframework.security.core.AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userDetails = userDetailsService.loadUserByUsername(name);
if(passwordEncoder.matches(password, userDetails.getPassword())) {
return new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(),
userDetails.getAuthorities());
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
And I have custom user details service like this:
@Service
public class CustomUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService{
@Autowired
UserService userService; //my custom user service
@Override
public UserDetails loadUserByUsername(String username) {
try {
User user = userService.getUserByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("Username doesn't exist");
} else if(user.isDeactivated()) {
throw new UsernameNotFoundException("User deactivated");
}
List<Authority> listOfAuthorities = userService.getAllAuthoritiesFromUser(user.getUserId());
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for(Authority authority : listOfAuthorities) {
grantedAuthorities.add(new SimpleGrantedAuthority(authority.getName()));
}
org.springframework.security.core.userdetails.User userNew =
new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
return userNew;
}
catch(Exception ex) {
throw new UsernameNotFoundException("Username or password not correct");
}
}
}
Where can I process the message from throw new UsernameNotFoundException
and return it as "error_description"
?
EDIT Here is also my SecurityConfig
and ResourceServerConfig
:
@Configuration
@EnableWebSecurity
@Order(Ordered.LOWEST_PRECEDENCE)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
CustomUserDetailsService userDetailsService;
@Autowired
private CustomAuthenticationProvider authProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.addFilterBefore(new AuthenticationTokenFilter(authenticationManager()), BasicAuthenticationFilter.class);
}
// must be overriden and exposed as @Bean, otherwise boot's AuthenticationManagerConfiguration will take precedence
@Bean @Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
@Autowired
private AuthExceptionEntryPoint myEntryPoint;
@Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().and().authorizeRequests().antMatchers("/**")
.authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(myEntryPoint).accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
This generic message on Spring Security has a purpose, and it's to obfuscate what's the actual reason for the failure in the login.
Once you give specific messages as you want, eg Username doesn't exist
, User deactivated
, Password incorrect
, and so on, you're starting to give too much information for a malicious user.
Update
If you still want to go that way, you can implement your own AuthenticationFailureHandler
, something like this should work:
public class DefaultAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
super.onAuthenticationFailure(request, response, exception);
if (exception.getClass().isAssignableFrom(UsernameNotFoundException.class)) {
response.sendRedirect("User not found")
} else if (exception.getClass().isAssignableFrom(LockedException.class)) {
response.sendRedirect("User Locked")
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.