首页 > 解决方案 > 如何在spring security中同时配置内存认证和jdbc认证

问题描述

我可以通过以下配置实现内存身份验证

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
{

    @Autowired
    public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.inMemoryAuthentication().withUser("praveen").password("{noop}praveen@123#").roles("ADMIN");
        auth.inMemoryAuthentication().withUser("vedanta").password("{noop}vedanta@123#").roles("USER");
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception
    {
        http
            .authorizeRequests()
                .antMatchers("/resources/**", "/", "/login", "/api/**").permitAll()
                .antMatchers("/app/admin/*").hasRole("ADMIN").antMatchers("/app/user/*")
                .hasAnyRole("ADMIN", "USER")
            .and().exceptionHandling().accessDeniedPage("/403")
            .and().formLogin()
                .loginPage("/login").usernameParameter("userName")
                .passwordParameter("password")
                .defaultSuccessUrl("/app/user/dashboard")
                .failureUrl("/login?error=true")
            .and().logout()
                .logoutSuccessHandler(new CustomLogoutSuccessHandler())
                .invalidateHttpSession(true)
            .and().csrf().disable();

        http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired=true");
    }
}

并通过以下配置进行 jdbc 身份验证(在不同的项目中)

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter 
{

    @Autowired
    DataSource dataSource;  


    @Autowired
    public void configureJdbcAuthentication(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder())
                .usersByUsernameQuery("select username, password, enabled from userdetails where userName=?")
                .authoritiesByUsernameQuery(
                        "select ud.username as username, rm.name as role from userdetails ud INNER JOIN rolemaster rm ON rm.id = ud.roleId  where username = ?");
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception
    {
        http
        .authorizeRequests()
            .antMatchers("/resources/**", "/", "/login")
                .permitAll()
            .antMatchers("/config/*", "/app/admin/*")
                .hasRole("ADMIN")
            .antMatchers("/app/user/*")
                .hasAnyRole("ADMIN", "USER")
            .antMatchers("/api/**")
                .hasRole("APIUSER")
        .and().exceptionHandling()
            .accessDeniedPage("/403")
        .and().formLogin()
            .loginPage("/login")
            .usernameParameter("userName").passwordParameter("password")
            .defaultSuccessUrl("/app/user/dashboard")
            .failureUrl("/login?error=true")
        .and().logout()
            .logoutSuccessHandler(new CustomLogoutSuccessHandler())
            .invalidateHttpSession(true)
        .and().httpBasic()
        .and().csrf()
                .disable();

        http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired=true");
    }

    @Bean
    public PasswordEncoder passwordEncoder() 
    {
        return new BCryptPasswordEncoder();
    }

}

当我尝试在同一个项目中实现这两者时(我刚刚在我的 SpringSecurityConfig 中添加了 configureInMemoryAuthentication 和 configureJdbcAuthentication 方法,如下所示)

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter 
{
... 
    @Autowired
    public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.inMemoryAuthentication()
        .withUser("restapiuser")
        .password("restapiuser@123#")
        .roles("APIUSER");
    }

    @Autowired
    public void configureJdbcAuthentication(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder())
                .usersByUsernameQuery("select username, password, enabled from userdetails where userName=?")
                .authoritiesByUsernameQuery(
                        "select ud.username as username, rm.name as role from userdetails ud INNER JOIN rolemaster rm ON rm.id = ud.roleId  where username = ?");
    }

...    
}

但我无法使用 inMemoryAuthentication 凭据登录成功,我已被重定向到“/login?error=true”页面。

但我能够使用 jdbcAuthentication 凭据成功登录。

但不能两者兼得。
我做错什么了吗?
是否可以结合两个身份验证?

标签: javaspringspring-mvcspring-security

解决方案


错误是,身份验证管理器试图解码我的纯密码并发出此警告。

WARN  - Encoded password does not look like BCrypt

在看到我尝试过的日志后password("{noop}restapiuser@123#")也没有工作可能是因为 jdbcAuthentication 已为 passwordEncoder 注册,身份验证管理器每次都会尝试解密/解码密码。所以没有办法跳过密码解码需要提供编码密码。

由于我没有保留编码密码,它正在尝试对其进行解码并重定向到 failureUrl。

参考解决方案

@Autowired
public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
{
    auth.inMemoryAuthentication()
    .withUser("restapiuser")
    .password(new BCryptPasswordEncoder().encode("restapiuser@123#"))
    .roles("APIUSER");
}

或者,最佳做法是保留编码密码而不是普通密码

.password("$2a$10$GRoNCbeVoBYMcZH7QLX2O.wWxkMtB4qiBY8y.XzvRN/mvktS9GWc6")

选择。只有一种 configureAuthentication 方法,但在内存和 jdbc 身份验证中都配置如下

@Autowired
public void configureBothAuthentication(AuthenticationManagerBuilder auth) throws Exception
{
    auth.inMemoryAuthentication()
        .withUser("restapiuser")
        .password(new BCryptPasswordEncoder().encode("restapiuser@123#"))
        .roles("ADMIN");

    auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder())
            .usersByUsernameQuery("select username, password, enabled from userdetails where userName=?")
            .authoritiesByUsernameQuery(
                    "select ud.username as username, rm.name as role from userdetails ud INNER JOIN rolemaster rm ON rm.id = ud.roleId  where username = ?");
}

因此,
可以在 spring 项目中同时实现 InMemoryAuthentication 和 jdbcAuthentication。


推荐阅读