我正在尝试使用 spring-security 进行身份验证,并希望能够在运行时添加用户。我认为使用 UserDetailsManager 的干扰最小。 如何将其作为 Bean 提供,以便我可以在 Controller 和其他对象中访问它?
我开始的代码如下:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource, PasswordEncoder enc) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource).withDefaultSchema().passwordEncoder(enc)
.withUser("user").password(enc.encode("password")).roles("USER").and()
.withUser("admin").password(enc.encode("password")).roles("USER", "ADMIN");
}
jdbcAuthentication()
创建一个 JdbcUserDetailsManager
,一切正常。但我不知道如何在网络应用程序初始化后访问它。我尝试了两种不起作用的变体:
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource,PasswordEncoder enc) throws Exception {
return new JdbcUserDetailsManagerConfigurer<>().dataSource(dataSource).withDefaultSchema().passwordEncoder(enc)
.withUser("user").password(enc.encode("password")).roles("USER").and()
.withUser("admin").password(enc.encode("password")).roles("USER", "ADMIN").and()
.getUserDetailsService();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsManager userDetailsManager) throws Exception {
auth.userDetailsService(userDetailsManager);
}
填写登录表单时,我得到以下信息:
Table "USERS" not found; SQL statement:
select username,password,enabled from users where username = ? [42102-185]
看来这并没有正确初始化bean。第二次尝试:
@Bean
public UserDetailsManager userDetailsManager(AuthenticationManagerBuilder auth, DataSource dataSource, PasswordEncoder enc) throws Exception {
return auth.jdbcAuthentication().dataSource(dataSource).withDefaultSchema().passwordEncoder(enc)
.withUser("user").password(enc.encode("password")).roles("USER").and()
.withUser("admin").password(enc.encode("password")).roles("USER", "ADMIN").and()
.getUserDetailsService();
}
在初始化期间,我得到:
java.lang.IllegalStateException: Cannot apply ...JdbcUserDetailsManagerConfigurer@3bd97b0d to already built object
因此在 @Bean
方法中使用构建器也不起作用。
最佳答案
jdbcAuthentication()
不仅仅创建一个 JdbcUserDetailsManagerConfigurer
;最重要的是,它使用 apply()
注册配置器,以便以后可以按正确的顺序执行所有配置。
幸运的是,apply()
是公共(public)的,所以我们可以自己调用它。知道了这一点,我们可以使用配置器的备用构造函数将主配置负载移回configureGlobal。最终对我有用的是:
@Bean
public JdbcUserDetailsManager userDetailsManager(DataSource dataSource) {
JdbcUserDetailsManager mgr = new JdbcUserDetailsManager();
mgr.setDataSource(dataSource); // (1)
return mgr;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, JdbcUserDetailsManager userDetailsManager, DataSource dataSource, PasswordEncoder enc) throws Exception {
//set user detail service manually
auth.userDetailsService(userDetailsManager);
JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> conf =
new JdbcUserDetailsManagerConfigurer<>(userDetailsManager);
//apply the configurer
auth.apply(conf);
conf.dataSource(dataSource) // (2)
.withDefaultSchema().passwordEncoder(enc)
.withUser("user").password(enc.encode("password")).roles("USER").and()
.withUser("admin").password(enc.encode("password")).roles("USER", "ADMIN");
}
看起来(1)和(2)中设置数据源是多余的,但忽略其中任何一个都会导致初始化时出现异常:
//without (1)
java.lang.IllegalArgumentException: 'dataSource' or 'jdbcTemplate' is required
//without (2)
java.lang.IllegalStateException: DataSource must be set
第一个错误是对 userDetailsManager()
返回的 bean 进行验证,第二个错误是在 AuthenticationManagerBuilder 执行配置器之前进行验证。
这样配置,我可以写,例如在 Controller 中:
@Autowired
private UserDetailsManager users;
@Autowired
private PasswordEncoder enc;
@RequestMapping(...)
public String handle(Model model) {
users.createUser(new User(username, enc.encode(password), authorities);
return "view";
}
关于java - 如何使 UserDetailsManager 可用作 bean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29447526/