如何使用 JavaConfig 从 Spring Security 中删除 ROLE_ 前缀?

问题描述 投票:0回答:9

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

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

那不起作用,所以我尝试按照

http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3中的建议创建一个
BeanPostProcessor -to-4-jc.html#m3to4-角色前缀-禁用。那也没用。

最后,我尝试创建自己的

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_ 前缀?

spring spring-security spring-java-config
9个回答
34
投票

从 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>

8
投票

以下配置对我有用。

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

7
投票

看来新的

GrantedAuthorityDefaults
将更改
DefaultWebSecurityExpressionHandler
DefaultMethodSecurityExpressionHandler
的前缀,但它不会修改从
RoleVoter.rolePrefix
设置的
@EnableGlobalMethodSecurity

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

5
投票

如果您在 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配置中所做的一切都可以写在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>

5
投票

如果您使用 Spring Boot 2,您可以创建此 bean 来覆盖 RoteVoter 前缀

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

它之所以有效,是因为当 GlobalMethodSecurityConfiguration 在方法 GlobalMethodSecurityConfiguration.accessDecisionManager() 中创建 AccessDecisionManager 时。这是代码片段,请注意对 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);
}

1
投票

使用 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 
}

0
投票

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

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

0
投票

这对我有用。

public SimpleAuthorityMapper grantedAuthority()                             
{     
    SimpleAuthorityMapper mapper = new SimpleAuthorityMapper();       
    mapper.setPrefix("");      
    return mapper;      
}

@Override   
public void configure(AuthenticationManagerBuilder auth)  
{  
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthority());
auth.authenticationProvider(keycloakAuthenticationProvider);   
}

0
投票

我见过的最好的答案:

    @Bean
    GrantedAuthorityDefaults grantedAuthorityDefaults() {
        return new GrantedAuthorityDefaults(); // Remove the ROLE_ prefix
    }
package com.senla.course.hotelapp.security;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.PriorityOrdered;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;

public class GrantedAuthorityDefaults 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;
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.