Spring security jdbcAuthentication 不适用于默认角色处理

Spring security jdbcAuthentication does not work with default roles processing

使用

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
         auth.inMemoryAuthentication().withUser("dba").password("root123").roles("ADMIN","DBA");

我的示例运行良好。例如

      http.authorizeRequests()
        // ...
        .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
        .and().formLogin()
        .and().exceptionHandling().accessDeniedPage("/Access_Denied");

如果我将 inMemoryAuthentication 更改为 spring jdbc 默认值 - 我遇到了一个角色问题。

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
         auth.jdbcAuthentication().dataSource(dataSource);

我确定我使用 spring 建议配置了数据库和架构(以便能够使用默认的 jdbc 身份验证)。

在调试模式下,我可以在

中看到从数据库加载的结果
org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
    #loadUserByUsername(username)[line 208]
    return createUserDetails(username, user, dbAuths);

它 returns 与内存配置类似的结果:

org.springframework.security.core.userdetails.User@183a3:
     Username: dba;
     Password: [PROTECTED];
     Enabled: true;
     AccountNonExpired: true;
     credentialsNonExpired: true;
     AccountNonLocked: true;
     Granted Authorities: ADMIN,DBA

如您所见,它加载了相应的授权机构,但 http 请求将我重定向到 .accessDeniedPage("/Access_Denied")。我很困惑,因为它应该像以前一样为用户工作。

我没有在我的项目中使用 spring 引导。 我的日志不包含任何 jdbc 错误的配置。 我花了很多时间调查细节,我的想法才刚刚完成。 你认为我需要添加以构建一些缓存库或其他东西吗?

这里有 2 个陷阱。

首先是当使用 hasRole('ADMIN') 时,首先检查它是否以角色前缀(默认为 ROLE_)开头,如果不是,则传入的角色前缀为 with它(另见 reference guide)。所以在这种情况下,实际检查的权限是 ROLE_ADMIN 而不是 ADMIN,就像你 expect/assume。

第二个是当使用内存选项时,roles 方法与此处提到的相同。它检查传入的角色是否以角色前缀开头,如果不是则添加它。因此,在内存中的示例中,您最终获得了权限 ROLE_ADMINROLE_DBA

然而,在您的 JDBC 选项中,您拥有权限 ADMINDBA,因此 hasRole('ADMIN') 检查失败,因为 ROLE_ADMIN 不等于 ADMIN

要修复您有多种选择。

  1. 而不是 hasRole 使用 hasAuthority 后者不添加角色前缀并且对于内存选项使用 authorities 而不是 roles
  2. 在 JDBC 选项中,数据库中的权限前缀为 ROLE_
  3. 将默认角色前缀设置为空。

使用hasAuthority

首先将内存数据库的配置更改为使用 authorities 而不是 roles

auth.inMemoryAuthentication()
    .withUser("dba").password("root123")
    .authorities("ADMIN","DBA");

接下来也改变你的表情

.antMatchers("/db/**").access("hasAuthority('ADMIN') and hasAuthority('DBA')")

前缀为ROLE_

在插入权限的脚本中,权限前缀为 ROLE_

删除默认角色前缀

这有点棘手,在 [迁移指南] 中有详细描述。

没有简单的配置选项,需要 BeanPostProcessor

public class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {

        // remove this if you are not using JSR-250
        if(bean instanceof Jsr250MethodSecurityMetadataSource) {
            ((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);
        }

        if(bean instanceof DefaultMethodSecurityExpressionHandler) {
            ((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
        }
        if(bean instanceof DefaultWebSecurityExpressionHandler) {
            ((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
        }
        if(bean instanceof SecurityContextHolderAwareRequestFilter) {
            ((SecurityContextHolderAwareRequestFilter)bean).setRolePrefix("");
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }

    @Override
    public int getOrder() {
        return PriorityOrdered.HIGHEST_PRECEDENCE;
    }
}

您可以看到启用日志记录发生了什么。在您的 application.properties 添加:

# ==============================================================
# = Logging springframework
# ==============================================================
logging.level.org.springframework.jdbc=DEBUG
logging.level.org.springframework.security=DEBUG
logging.level.org.springframework.web=DEBUG
logging.level.org.springframework.http=DEBUG