CalendarUserAuthenticationProvider
Throughout the rest of this section, we are going to create a custom AuthenticationProvider object named CalendarUserAuthenticationProvider that will replace CalendarUserDetailsService. Then, we will use CalendarUserAuthenticationProvider to consider an additional parameter to support authenticating users from multiple domains.
We must use an AuthenticationProvider object rather than UserDetailsService, because the UserDetails interface has no concept of a domain parameter.
Create a new class named CalendarUserAuthenticationProvider, as follows:
//src/main/java/com/packtpub/springsecurity/authentication/
CalendarUserAuthenticationProvider.java
// … imports omitted ...
@Component
public class CalendarUserAuthenticationProvider implements
AuthenticationProvider {
private final CalendarService calendarService;
@Autowired
public CalendarUserAuthenticationProvider
(CalendarService calendarService) {
this.calendarService = calendarService;
}
public Authentication authenticate(Authentication
authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken token =
(UsernamePasswordAuthenticationToken)
authentication;
String email = token.getName();
CalendarUser user = null;
if(email != null) {
user = calendarService.findUserByEmail(email);
}
if(user == null) {
throw new UsernameNotFoundException("Invalid
username/password");
}
String password = user.getPassword();
if(!password.equals(token.getCredentials())) {
throw new BadCredentialsException("Invalid
username/password");
}
Collection<? extends GrantedAuthority> authorities =
CalendarUserAuthorityUtils.createAuthorities(user);
return new UsernamePasswordAuthenticationToken(user, password,
authorities);
}
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken
.class.equals(authentication);
}
}
Remember that you can use Shift+Ctrl+O within Eclipse to easily add the missing imports. Alternatively, you can copy the implementation from chapter03.05-calendar.
Before Spring Security can invoke the authenticate method, the supports method must return true for the Authentication class that will be passed in. In this case, AuthenticationProvider can authenticate a username and password. We do not accept subclasses of UsernamePasswordAuthenticationToken, since there may be additional fields that we do not know how to validate.
The authenticate method accepts an Authentication object as an argument that represents an authentication request. In practical terms, it is the input from the user that we need to attempt to validate. If authentication fails, the method should throw an o.s.s.core.AuthenticationException exception. If authentication succeeds, it should return an Authentication object that contains the proper GrantedAuthority objects for the user. The returned Authentication object will be set on SecurityContextHolder. If authentication cannot be determined, the method should return null.
The first step in authenticating the request is to extract the information from the Authentication object that we need to authenticate the user. In our case, we extract the username and look up CalendarUser by email address, just as CalendarUserDetailsService did. If the provided username and password match CalendarUser, we will return a UsernamePasswordAuthenticationToken object with proper GrantedAuthority. Otherwise, we will throw an AuthenticationException exception.
Remember how the login page leveraged SPRING_SECURITY_LAST_EXCEPTION to explain why login failed? The message for the AuthenticationException exception thrown in AuthenticationProvider is the last AuthenticationException exception and will be displayed on our login page in the event of a failed login.