如何使 UserDetailsManager 作为 bean 可用

How to make a UserDetailsManager available as a bean

我正在试验 spring-security 进行身份验证,并希望能够在运行时添加用户。我认为使用 UserDetailsManager 的干扰最小。 如何将它作为 bean 提供,以便我可以在控制器和其他对象中访问它?

我开始的代码如下:

@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 方法中使用生成器也不起作用。

如果我没看错,您想创建一个处理用户的 bean,我建议在 spring.xml 中声明它并使用不断添加到 bean 的函数。 尝试使用 applicationcontext,这将在主页上

ApplicationContext ac;
        ac=new ClassPathXmlApplicationContext("Spring.xml");
        prisontest par=(prisontest)ac.getBean("prison");

这里,applicationcontext就像一个容器,里面装着bean,prisontest是class,定义了datasource,看spring.xml,

就明白了

spring.xml 看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="prison" class="prisontest">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">

        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost/tgmc" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

</beans>

引用数据源的 bean 将如下所示:

public class prisontest {
        DataSource ds;
        public void setDataSource(DataSource ds)
        {
            this.ds=ds;
        }

在此处添加代码以在运行时管理用户 }

您也可以将您的其他代码放在同一个 class 中,希望这对您有所帮助。

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 执行配置器之前的验证。

这样配置,我可以写,例如在控制器中:

@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";
}