【问题标题】:How do I remove the ROLE_ prefix from Spring Security with JavaConfig?如何使用 JavaConfig 从 Spring Security 中删除 ROLE_ 前缀?
【发布时间】:2016-11-03 04:47:05
【问题描述】:

我正在尝试删除 Spring Security 中的“ROLE_”前缀。我尝试的第一件事是:

http.servletApi().rolePrefix("");

这不起作用,所以我尝试按照http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-jc.html#m3to4-role-prefixing-disable 中的建议创建一个BeanPostProcessor。那也没用。

最后,我尝试创建自己的SecurityExpressionHandler

  @Override
  protected void configure(HttpSecurity http) throws Exception {
      http
          .authorizeRequests()
          .expressionHandler(webExpressionHandler())
          .antMatchers("/restricted").fullyAuthenticated()
          .antMatchers("/foo").hasRole("mycustomrolename")
          .antMatchers("/**").permitAll();
  }

  private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() {
      DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
      defaultWebSecurityExpressionHandler.setDefaultRolePrefix("");
      return defaultWebSecurityExpressionHandler;
  }

但是,这也不起作用。如果我使用“hasAuthority(roleName)”而不是hasRole,它会按预期工作。

是否可以从 Spring Security 的 hasRole 检查中删除 ROLE_ 前缀?

【问题讨论】:

  • 奇怪 BeanPostProcessor 对我有用(您确实将其声明为 static bean 方法并包含 PriorityOrdered 以便它运行得非常早?)对于表达式处理程序也是如此。我们还配置了一个DefaultMethodSecurityExpressionHandlerDefaultMethodSecurityExpressionHandler`,其前缀设置为null
  • 是的,我直接从文档中复制了BeanPostProcessor 的代码。我尝试将@Bean 放在我的@Configuration Spring Security 类和我的@SpringBootApplication 类中。我添加了一个System.out.println 以确保它也在 Spring Security 之前进行配置。 hasAuthority 按预期工作,所以我想我会改用它。
  • 我们在非 Spring Boot 应用程序中使用了它。会不会是这样的干扰,或者引导的安全性在某种程度上仍然是较早配置的?

标签: spring spring-security spring-java-config


【解决方案1】:

以下配置对我有用。

@Override
public void configure(WebSecurity web) throws Exception {
    web.expressionHandler(new DefaultWebSecurityExpressionHandler() {
        @Override
        protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
            WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi);
            root.setDefaultRolePrefix(""); //remove the prefix ROLE_
            return root;
        }
    });
}

【讨论】:

    【解决方案2】:

    从 Spring 4.2 开始,您可以使用单个 bean 定义前缀,如下所述:https://github.com/spring-projects/spring-security/issues/4134

    @Bean
    GrantedAuthorityDefaults grantedAuthorityDefaults() {
        return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
    }
    

    XML 版本:

    <beans:bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
        <beans:constructor-arg value="" />
    </beans:bean>
    

    【讨论】:

    • 没有为我工作
    • 不起作用:请参阅有问题的第四条评论stackoverflow.com/questions/46756013
    • GrantedAuthority 和 Role 之间存在区别。此答案适用于 GrantedAuthority。
    • 对于那些有注释异常的人:将@Bean实例从安全配置移动到MVC配置参考:stackoverflow.com/questions/48971937/…
    • 它适用于HttpServletRequest.isUserInRole(String),但不适用于@Secured(String)
    【解决方案3】:

    如果您在 4.2 之前并且使用所谓的选民(如果您使用 @hasRole 等注释),那么您需要在上下文中定义以下 bean:

    @Bean
    public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
        DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
        defaultMethodSecurityExpressionHandler.setDefaultRolePrefix("");
        return defaultMethodSecurityExpressionHandler;
    }
    
    @Bean
    public DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler() {
        DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
        defaultWebSecurityExpressionHandler.setDefaultRolePrefix("");
        return defaultWebSecurityExpressionHandler;
    }
    

    这些 bean 用于为拼写表达式创建评估上下文,它们的 defaultRolePrefix 设置为“ROLE_”。尽管这取决于您的用例。这个对我有用,上面没有。

    编辑:回答有关 xml 配置的问题 -> 当然可以在 xml 中完成。在 java config 中所做的一切都可以写在 xml 配置中。这是示例(尽管我没有对其进行测试,因此可能存在拼写错误或其他问题):

    <bean id="defaultWebSecurityExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
            <property name="defaultRolePrefix" value=""></property>
    </bean>
    
    <bean id="defaultMethodSecurityExpressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
            <property name="defaultRolePrefix" value=""></property>
    </bean>
    

    【讨论】:

    • XML 配置中是否有任何等价物?
    • 我已经用 xml 配置更新了我的答案......但正如我所写,可能存在某种错误 - 我没有测试它。一般规则是 - java config 中的所有内容也可以在 xml 中完成。
    • 在我的 JHipster 应用程序启动期间失败,org.springframework.beans.factory.BeanCreationException: .......; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
    • Petr - 这只是一个疯狂的猜测,但如果您收到此错误,则可能意味着您没有 Web 上下文,因此您不需要第二个 bean - DefaultWebSecurityExpressionHandler。无论如何,每种配置都是不同的,因此很难说出您的问题到底是什么。对我来说,这两个 bean 解决了这个问题。
    【解决方案4】:

    新的GrantedAuthorityDefaults 似乎会更改DefaultWebSecurityExpressionHandlerDefaultMethodSecurityExpressionHandler 的前缀,但不会修改从@EnableGlobalMethodSecurity 设置的RoleVoter.rolePrefix

    RoleVoter.rolePrefix 用于@Secured("ADMIN") 方法安全风格。

    所以除了GrantedAuthorityDefaults,我还必须添加这个CustomGlobalMethodSecurity 类来覆盖RoleVoter 的默认值。

    @Configuration
    @EnableGlobalMethodSecurity(securedEnabled = true)
    public class CustomGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {
    
        protected AccessDecisionManager accessDecisionManager() {
            AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
    
            //Remove the ROLE_ prefix from RoleVoter for @Secured and hasRole checks on methods
            accessDecisionManager.getDecisionVoters().stream()
                    .filter(RoleVoter.class::isInstance)
                    .map(RoleVoter.class::cast)
                    .forEach(it -> it.setRolePrefix(""));
    
            return accessDecisionManager;
        }
    }
    

    【讨论】:

    • 我在WebSecurityConfigurerAdapter中使用@EnableGlobalMethodSecurity,如何访问AccessDecisionManager?
    • @Ninja AccessDecisionManager 必须在一个额外的类中配置,因为我不相信它可以从 WebSecurityConfigurerAdapter 完成。我认为您只需要创建一个扩展 GlobalMethodSecurityConfiguration 的新配置类
    • 但这只有在你使用 @Secured 注释时才有帮助,因为 @Secured 值在 RoleVoter 类中评估,@PreAuthorize 注释表达式在 PreInvocationAuthorizationAdviceVoter 类中评估,发布另一个 bean @987654336 @ 在构造函数中带有前缀参数,这也会影响@PreAuthorize
    【解决方案5】:

    如果你使用 Spring Boot 2,你可以创建这个 bean 来覆盖 RoteVoter 前缀

    @Bean
    public GrantedAuthorityDefaults grantedAuthorityDefaults() {
        return  new GrantedAuthorityDefaults("<anything you want>");
    }
    

    之所以有效,是因为当 GlobalMethodSecurityConfiguration 在方法 GlobalMethodSecurityConfiguration.accessDecisionManager() 中创建 AccessDecisionManager 时。这是代码的 sn-p,注意对 grantedAuthorityDefaults

    的空检查
        protected AccessDecisionManager accessDecisionManager() {
        ....
        RoleVoter roleVoter = new RoleVoter();
        GrantedAuthorityDefaults grantedAuthorityDefaults =
                getSingleBeanOrNull(GrantedAuthorityDefaults.class);
        if (grantedAuthorityDefaults != null) {
            roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
        }
        decisionVoters.add(roleVoter);
        decisionVoters.add(new AuthenticatedVoter());
        return new AffirmativeBased(decisionVoters);
    }
    

    【讨论】:

      【解决方案6】:

      我为我发布了总结工作解决方案:

      @Configuration
      @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
      public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
      
          /**
           * Allow skip ROLE_ when check permission using @Secured, like:
           *  @Secured({AuthorityConstants.ROLE_SYSTEM_ADMIN})
           */
          @Override
          protected AccessDecisionManager accessDecisionManager() {
              AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
              setAuthorityRolePrefix(accessDecisionManager, "");
              return accessDecisionManager;
          }
      
          private void setAuthorityRolePrefix(AffirmativeBased accessDecisionManager, String rolePrefix) {
              accessDecisionManager.getDecisionVoters().stream()
                      .filter(RoleVoter.class::isInstance)
                      .map(RoleVoter.class::cast)
                      .forEach(it -> it.setRolePrefix(rolePrefix));
          }
      
          /**
           * Allow skip ROLE_ when check permission using @PreAuthorize, like:
           * @PreAuthorize("hasAnyRole('USER', 'SYSTEM_ADMIN')")
           */
          @Bean
          GrantedAuthorityDefaults grantedAuthorityDefaults() {
              return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
          }
      }
      

      【讨论】:

        【解决方案7】:

        使用 Spring Boot 2.3 我在启动时遇到了这个异常:

        Error creating bean with name 'resourceHandlerMapping' defined in class path resource 
        [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]:
        Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
        Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: 
        Factory method 'resourceHandlerMapping' threw exception;
        nested exception is java.lang.IllegalStateException: No ServletContext set
        

        这是我的解决方案:

        @Configuration
        @Import(RolePrefixConfiguration.class)
        @EnableWebSecurity
        @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
        public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
        
          public static class RolePrefixConfiguration {
        
            @Bean
            public GrantedAuthorityDefaults grantedAuthorityDefaults() {
              log.debug("remove prefix 'ROLE_' from grantedAuthorityDefaults");
              return new GrantedAuthorityDefaults("");
            }
        
          }
           
           // ... your usual config 
        }
        

        【讨论】:

          猜你喜欢
          • 2012-01-09
          • 2019-12-10
          • 2020-06-10
          • 2020-07-01
          • 2015-07-20
          • 1970-01-01
          • 2016-01-17
          • 2014-03-04
          • 2015-05-28
          相关资源
          最近更新 更多