java - 如何使 UserDetailsManager 可用作 bean

标签 java spring authentication spring-security spring-bean

我正在尝试使用 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/

相关文章:

android - 签名 APK 中的 Linkedin 登录错误(Android)

java - JIS X 0208 转换 : how to handle unified (merged) codepoints

java - 如何在 jpa 查询中使用自定义函数?

使用自定义注释的 Spring Logging

java - 如何将 jersey 2.0 和 jersey mvc 与 Spring Boot 一起使用?

ruby-on-rails - 我需要简单但完整的说明来实现设计身份验证策略

java - ArrayList显示不正确

android - Ubuntu 10.04 64 位上的 Android 开发使用哪个 JDK?

java - 无法使用模式匹配删除java中所有出现的html标签

java - 如何在前端(angularjs)和后端(java 或 c#)之间保持 session ?