问题
I m looking a way to create and use my own method to load user in Java Spring Security.
I would like to retrieve my user not by UserName but by email.
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
@Autowired
UserRepository userRepository;
private static final Logger logger = LoggerFactory.getLogger(UserDetailsService.class);
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
Optional<User> oUser = userRepository.findByUserName(userName);
if(!oUser.isPresent()){
throw new UsernameNotFoundException(userName);
} else {
logger.info("user found");
}
User user = oUser.get();
return this.buildUserDetails(user);
}
But loadUserByUsername is called in this method in class DaoAuthenticationProvider. How can i override this behavior ?
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
UserDetails loadedUser;
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
} catch (UsernameNotFoundException var6) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
this.passwordEncoder.isPasswordValid(this.userNotFoundEncodedPassword, presentedPassword, (Object)null);
}
throw var6;
} catch (Exception var7) {
throw new InternalAuthenticationServiceException(var7.getMessage(), var7);
}
if (loadedUser == null) {
throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
} else {
return loadedUser;
}
}
Solution
My WebSecurityConfig here with customDaoAuthenticationProvider
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
UserDetailsServiceExtended userDetailsServiceExtended;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
protected TokenAuthenticationService tokenAuthenticationService;
@Value("${web.security.debug}")
private boolean debug;
public WebSecurityConfig() { super(false);}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
http
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint);
http
.addFilterBefore(customEmailPasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), CustomEmailPasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public CustomEmailPasswordAuthenticationFilter customEmailPasswordAuthenticationFilter() throws Exception {
CustomEmailPasswordAuthenticationFilter filter = new CustomEmailPasswordAuthenticationFilter();
filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
filter.setAuthenticationFailureHandler(authenticationFailureHandler);
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/users/authenticate", "POST"));
filter.setAuthenticationManager(authenticationManager());
return filter;
}
@Bean
public CustomDaoAuthenticationProvider daoAuthenticationProvider() {
CustomDaoAuthenticationProvider authenticationProvider = new CustomDaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(this.passwordEncoder);
authenticationProvider.setUserDetailsService(userDetailsServiceExtended);
return authenticationProvider;
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
auth.userDetailsService(this.userDetailsServiceExtended).passwordEncoder(this.passwordEncoder);
}
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
回答1:
To answer your direct question, retrieveUser cannot be overriden. Not only is it final, indicating that it cannot be overriden, it's protected, meaning that you can't access it from outside the org.springframework.security.authentication.dao package.
Obviously, I wouldn't be answering without a solution.
Spring's biggest forte is its layer of abstraction. A lot of people misunderstand its use, but in simple terms, as long as a class extends the same abstract class as the default class, it can be replaced by using the @Bean annotation.
So in your case, DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider. Following that logic, as long as we make a class that extends AbstractUserDetailsAuthenticationProvider and configure it accordingly, we should be able to replace DaoAuthenticationProvider.
Let's call that class CustomDaoAuthenticationProvider.
public class CustomDaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
}
Copy everything from DaoAuthenticationProvider here.
The only difference is that the constructor and the class name should be renamed from DaoAuthenticationProvider to CustomDaoAuthenticationProvider.
If you're using a different version of Spring, you should be able to just navigate to the source code of DaoAuthenticationProvider from your IDE.
Now you'll need to create a configuration class, let's call it SecurityConfiguration:
@Configuration
@WebSecurity // optional?
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userService; // can be replaced by whatever service implements UserDetailsService
@Bean
public CustomDaoAuthenticationProvider daoAuthenticationProvider() {
CustomDaoAuthenticationProvider authenticationProvider = new CustomDaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(passwordEncoder());
authenticationProvider.setUserDetailsService(userService);
System.out.println("Using my custom DaoAuthenticationProvider");
return authenticationProvider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
// ...
}
The following configuration should tell Spring to use CustomDaoAuthenticationProvider instead of DaoAuthenticationProvider.
I briefly tested it on my end, and it should work. From there, you can modify retrieveUser directly in CustomDaoAuthenticationProvider as you wish.
Good luck!
来源:https://stackoverflow.com/questions/50099192/create-and-user-loaduserbyemail-instead-of-loaduserbyusername