我正在寻找一种方法来创建和使用我自己的方法来加载Java Spring Security中的用户。
我想通过UserName而不是通过电子邮件检索我的用户。
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);
}
但是在DaoAuthenticationProvider类中的此方法中调用了loadUserByUsername。我该如何覆盖这种行为?
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;
}
}
我的WebSecurityConfig,其中包含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();
}
}
答案 0 :(得分:2)
要回答您的直接问题,retrieveUser
无法覆盖。它不仅final
,表示它不能被覆盖,它受到保护,这意味着您无法从org.springframework.security.authentication.dao
包之外访问它。
显然,如果没有解决方案,我就不会回答。
Spring最大的特色是它的抽象层。很多人误解了它的用法,但简单来说,只要一个类扩展了与默认类相同的抽象类,就可以使用@Bean
注释替换它。
因此,在您的情况下,DaoAuthenticationProvider
会延伸AbstractUserDetailsAuthenticationProvider
。遵循该逻辑,只要我们创建一个扩展AbstractUserDetailsAuthenticationProvider
并相应配置它的类,我们就应该能够替换DaoAuthenticationProvider
。
我们称之为CustomDaoAuthenticationProvider
课程。
public class CustomDaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
}
从DaoAuthenticationProvider here复制所有内容。
唯一的区别是构造函数和类名应该从DaoAuthenticationProvider
重命名为CustomDaoAuthenticationProvider
。
如果您使用的是不同版本的Spring,则应该可以从IDE导航到DaoAuthenticationProvider
的源代码。
现在您需要创建一个配置类,我们称之为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());
}
// ...
}
以下配置应告诉Spring使用CustomDaoAuthenticationProvider
而不是DaoAuthenticationProvider
。
我在最后对其进行了简要的测试,它应该可行。在那里,您可以根据需要直接在retrieveUser
修改CustomDaoAuthenticationProvider
。