问题
Trying to understand what's the correct way of implementing OpenID authentication with Spring Security.
public class OpenIDUserDetailsService implements
UserDetailsService,
AuthenticationUserDetailsService {
@Override
public UserDetails loadUserByUsername(String openId) throws
UsernameNotFoundException, DataAccessException {
// I either want user email here
// or immediately delegate the request to loadUserDetails
}
@Override
public UserDetails loadUserDetails(Authentication token) throws
UsernameNotFoundException {
// This never gets called if I throw from loadUserByUsername()
}
private MyCustomUserDetails registerUser(String openId, String email) {
...
}
}
I'm considering the scenario when user is not yet registered within my application. To register the user, I need to know its OpenID and email.
When OpenID provider redirects the user back to my application, loadUserByUsername()
is called, but in this case I'm only aware about user's OpenID. So, I'm throwing UsernameNotFoundException
and then loadUserDetails()
never gets called, so I can't register user.
What's the common solution here? What if I return something like FakePartialUserDetails
from loadUserByUsername()
and then, when loadUserDetails()
is called, I register the user and then return the real MyCustomUserDetails
?
I'm using Spring Security 3.0.7.RELEASE
回答1:
That's funny, but managed to resolve it by moving to Spring Security 3.1.0.RELEASE.
For the same scenario, behavior is absolutely different - loadUserByUsername()
is not called and loadUserDetails()
is called instead.
回答2:
I solved the same situation by implementing
AuthenticationUserDetailsService<OpenIDAuthenticationToken>
in my UserDetailsService.
public class OpenIdUserDetailsService implements UserDetailsService,
AuthenticationUserDetailsService<OpenIDAuthenticationToken> {
@Autowired(required = true)
@Qualifier(value = "jdbcUserDetailsService")
private UserDetailsService localUserDetailsService;
/**
* @return the localUserDetailsService
*/
public UserDetailsService getLocalUserDetailsService() {
return localUserDetailsService;
}
/**
* @param localUserDetailsService
* the localUserDetailsService to set
*/
public void setLocalUserDetailsService(
UserDetailsService localUserDetailsService) {
this.localUserDetailsService = localUserDetailsService;
}
@Override
public UserDetails loadUserDetails(OpenIDAuthenticationToken token)
throws UsernameNotFoundException {
String email = getEmail(token);
return loadUserByUsername(email);
}
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return localUserDetailsService.loadUserByUsername(username);
}
private String getEmail(OpenIDAuthenticationToken token) {
for (OpenIDAttribute attribute : token.getAttributes()) {
if (attribute.getName().equals("email")) {
return attribute.getValues().get(0);
}
}
return null;
}
}
Just make sure to use above service as the UserDetailsService while configuring openid-form. Also, configure attribute exchange for 'email' attribute while configuring openid. The email returned after successfull authentication can then be fetched from 'OpenIDAuthenticationToken' which eventually gets passed as paramter to loadUserByUsername function.
If you have user registered with that email, authentication is complete. Else you can suggest user to get registered by that email or show login failure page.
来源:https://stackoverflow.com/questions/10927633/spring-security-openid-userdetailsservice-authenticationuserdetailsservice