java - 创建并使用 loadUserByEmail 而不是 loadUserByUsername
问题描述
我正在寻找一种方法来创建和使用我自己的方法在 Java Spring Security 中加载用户。
我想不是通过用户名而是通过电子邮件来检索我的用户。
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();
}
}
解决方案
要回答您的直接问题,retrieveUser
不能被覆盖。不仅是它final
,表示它不能被覆盖,它是受保护的,这意味着你不能从org.springframework.security.authentication.dao
包外部访问它。
显然,如果没有解决方案,我不会回答。
Spring 最大的优势在于它的抽象层。很多人对它的使用有误解,但是简单来说,只要一个类扩展了与默认类相同的抽象类,就可以通过使用@Bean
注解来代替。
所以在你的情况下,DaoAuthenticationProvider
extends AbstractUserDetailsAuthenticationProvider
。按照这个逻辑,只要我们创建一个扩展AbstractUserDetailsAuthenticationProvider
并相应配置它的类,我们应该能够替换DaoAuthenticationProvider
.
让我们称之为那个类CustomDaoAuthenticationProvider
。
public class CustomDaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
}
从此处复制 DaoAuthenticationProvider 的所有内容。
唯一的区别是构造函数和类名应该重命名为DaoAuthenticationProvider
to CustomDaoAuthenticationProvider
。
如果您使用的是不同版本的 Spring,您应该能够DaoAuthenticationProvider
从 IDE 导航到源代码。
现在您需要创建一个配置类,我们称之为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
。
祝你好运!
推荐阅读
- python - 如何防止 Django Post_Save 信号触发两次?(dispatch_uid="my_unique_identifier" 对我不起作用)
- javascript - Javascript for 循环参数
- pytorch - Pytorch 模型将 GPU 设置为在 Nvidia gpu 上运行
- comments - 在方案大纲中添加的注释不会出现在 HTML 报告中
- vue.js - Nuxt / Vue - 不要在突变处理程序之外改变 vuex 存储状态
- javascript - 它是哪种格式?倒计时.js
- go - Go 编译器/链接器行为
- android - 如何为 android 项目添加 ga4 分析?
- javascript - wordpress ajax rest api ("code":"rest_invalid_json","message":"Invalid JSON body passed.")
- ssl-certificate - 如何将网站更改为不再需要 SSL 证书