我正在尝试删除 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 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>
以下配置对我有用。
@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;
}
});
}
看来新的
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;
}
}
如果您在 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>
如果您使用 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);
}
使用 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
}
我为我发布了总结的工作解决方案:
@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
}
}
这对我有用。
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);
}
我见过的最好的答案:
@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;
}
}