Spring 安全角色层次结构使用 Java 配置不起作用

2022-09-04 05:09:10

首先,我是Java Spring Framework的新手。所以请原谅我,如果我没有提供足够的信息。我试图将RoleHierarchy添加到我的应用程序中,但它不起作用。以下是我尝试过的代码。


安全配置.java

// These config is try to set up a user Role Hierarchy
@Bean
public RoleHierarchy roleHierarchy() {
  System.out.println("arrive public RoleHierarchy roleHierarchy()");
  RoleHierarchyImpl r = new RoleHierarchyImpl();
  r.setHierarchy("ROLE_ADMIN > ROLE_STAFF");
  r.setHierarchy("ROLE_STAFF > ROLE_USER");
  r.setHierarchy("ROLE_DEVELOPER > ROLE_USER");
  r.setHierarchy("ROLE_USER > ROLE_GUEST"); 
  return r;
}

@Bean
public AffirmativeBased defaultAccessDecisionManager(RoleHierarchy roleHierarchy){
  System.out.println("arrive public AffirmativeBased defaultAccessDecisionManager()");
  List<AccessDecisionVoter> decisionVoters = new ArrayList<>();

  // webExpressionVoter
  WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
  DefaultWebSecurityExpressionHandler
      expressionHandler = new DefaultWebSecurityExpressionHandler();
  expressionHandler.setRoleHierarchy(roleHierarchy);
  webExpressionVoter.setExpressionHandler(expressionHandler);

  decisionVoters.add(webExpressionVoter);
  decisionVoters.add(roleHierarchyVoter(roleHierarchy));
  // return new AffirmativeBased(Arrays.asList((AccessDecisionVoter) webExpressionVoter));
  return new AffirmativeBased(decisionVoters);
}

@Bean
public RoleHierarchyVoter roleHierarchyVoter(RoleHierarchy roleHierarchy) {
  System.out.println("arrive public RoleHierarchyVoter roleHierarchyVoter");
  return new RoleHierarchyVoter(roleHierarchy);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
  // skipping some codes
  http
    // skipping some codes
    .accessDecisionManager(defaultAccessDecisionManager(roleHierarchy()))
  // skipping some codes
}

方法安全配置.java

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

  @Inject
  private SecurityConfig securityConfig;

  @Override
  protected AuthenticationManager authenticationManager() throws Exception {
    return securityConfig.authenticationManagerBean();
  }

  @Override
  protected MethodSecurityExpressionHandler createExpressionHandler() {
    System.out.println("arrive protected MethodSecurityExpressionHandler createExpressionHandler()");
    DefaultMethodSecurityExpressionHandler d = new DefaultMethodSecurityExpressionHandler();
    d.setRoleHierarchy(securityConfig.roleHierarchy());
    return d;
  }

}

我有一个提供,和UserDetailsServiceImpl implements UserDetailsServiceprincipalAuthenticationGrantedAuthority

最后我有一些API:

@PreAuthorize("hasRole('ROLE_STAFF')")
@RequestMapping(value = "/api/v1/contactUs", method = RequestMethod.GET)

@PreAuthorize("hasRole('ROLE_DEVELOPER')")
@RequestMapping(value = "/api/v1/system", method = RequestMethod.GET)

现在的问题是,如果我以ROLE_STAFF身份登录,ROLE_DEVELOPER,ROLE_ADMIN,我得到了以下结果。

| API       | ROLE_STAFF | ROLE_DEVELOPER | ROLE_ADMIN |
|-----------|------------|----------------|------------|
| contactUs | 200        | 403            | 403        |
| system    | 403        | 200            | 403        |

如您所见,工作得很好。但我想成为两者的超级角色,但它不起作用。ROLE_STAFFROLE_DEVELOPERROLE_ADMIN

仅供参考,我正在使用弹簧安全3.2.5.发布


答案 1

问题出在 RoleHierachy 中,它应该是这样的:

@Bean
public RoleHierarchy roleHierarchy() {
  RoleHierarchyImpl r = new RoleHierarchyImpl();
  r.setHierarchy("ROLE_ADMIN > ROLE_STAFF and ROLE_ADMIN > ROLE_DEVELOPER and ROLE_STAFF > ROLE_USER and ROLE_DEVELOPER > ROLE_USER");
  return r;
}

继续呼叫将覆盖之前的设置setHierarchy()


答案 2

每次我想用Spring Security和Java配置实现角色层次结构时,我都会使用以下方法:

  1. 我们必须将 RoleHierarchyImpl bean 添加到上下文中(你看,我使用多个角色来构建层次结构):

    @Bean
    public RoleHierarchyImpl roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_DBA ROLE_DBA > ROLE_USER ");
        return roleHierarchy;
    }
    
  2. 然后,我们需要创建 Web 表达式处理程序,以获取的层次结构传递给它:

    private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() {
        DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
        defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy());
        return defaultWebSecurityExpressionHandler;
    }
    
  3. 最后一步是将 expressionHandler 添加到 http.authorizeRequests() 中:

            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http
                   .authorizeRequests()
                        .expressionHandler(webExpressionHandler())
                        .antMatchers("/admin/**").access("(hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')) and isFullyAuthenticated()")
                        .antMatchers("/dba").access("hasRole('ROLE_DBA') and isFullyAuthenticated()")
                        .antMatchers("/dba/**").access("hasRole('ROLE_USER')")
                        .and()
                   .requiresChannel()
                        .antMatchers("/security/**").requiresSecure()
                        .anyRequest().requiresInsecure()
                        .and()
                   .formLogin()
                        .loginPage("/login")
                        .failureUrl("/login?auth=fail")
                        .usernameParameter("username")
                        .passwordParameter("password")
                        .defaultSuccessUrl("/admin")
                        .permitAll()
                        .and()
                   .logout()
                            .logoutUrl("/logout")
                            .deleteCookies("remember-me")
                            .invalidateHttpSession(true)
                            .logoutSuccessUrl("/index")
                            .permitAll()
                            .and()
                   .csrf()
                            .and()
                   .rememberMe().tokenValiditySeconds(1209600)
                            .and()
                   .exceptionHandling().accessDeniedPage("/403")
                            .and()
                   .anonymous().disable()
                   .addFilter(switchUserFilter());
            }
    

结果:在此特定示例中,我们使用管理员用户登录后尝试访问 /dba 部分(ROLE_ADMIN)。在创建层次结构之前,我们有一个拒绝访问的结果,但现在我们可以毫无问题地访问此部分。


推荐