使用 Spring Security 3.2.5 和 Spring 4.1.2,100% Java config
我们的 Web 应用程序启用了全局方法安全性,并使用 @PreAuthorize
注释了服务方法 - 一切都按预期工作。 我正在尝试添加角色层次结构,但根本没有成功。 这是我试图实现的层次结构:
- ROLE_ADMIN可以访问ROLE_USER可以访问的所有方法。
- ROLE_USER可以访问ROLE_DEFAULT可以访问的所有方法。
尽管我尽了最大努力,但具有 ROLE_ADMIN 的用户在执行导致调用用 @PreAuthorized("hasAuthority('ROLE_DEFAULT')")
注释的方法的操作时会收到 403
以下是相关的配置代码:
应用初始值设定项
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
@Override
protected Class<?>[] getRootConfigClasses()
{
return new Class[]
{
AppConfig.class, SecurityConfig.class
};
}
@Override
protected Class<?>[] getServletConfigClasses()
{
return new Class[]
{
MvcConfig.class
};
}
// other methods not shown for brevity
}
应用配置.java
@Configuration
@ComponentScan(basePackages={"myapp.config.profile", "myapp.dao", "myapp.service", "myapp.security"})
public class AppConfig
{
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> detailSvc) throws Exception
{
PreAuthenticatedAuthenticationProvider authProvider = new PreAuthenticatedAuthenticationProvider();
authProvider.setPreAuthenticatedUserDetailsService(detailSvc);
auth.authenticationProvider(authProvider);
}
// other methods not shown for brevity
}
安全配置.java
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception
{
PKIAuthenticationFilter pkiFilter = new PKIAuthenticationFilter();
pkiFilter.setAuthenticationManager(authenticationManagerBean());
http.authorizeRequests()
.antMatchers("/app/**").fullyAuthenticated()
.and()
.anonymous().disable()
.jee().disable()
.formLogin().disable()
.csrf().disable()
.x509().disable()
.addFilter(pkiFilter)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(WebSecurity web) throws Exception
{
// ignore everything but /app/*
web.ignoring().regexMatchers("^(?!/app/).*");
}
}
MvcConfig.java
@Configuration
@EnableWebMvc
@ComponentScan({"myapp.controller"})
public class MvcConfig extends WebMvcConfigurerAdapter
{
// resource handlers, content negotiation, message converters configured here
}
在与SecurityConfig
相同的包中(因此它是AppConfig
组件扫描的一部分(,我有这个类:
GlobalMethodSecurityConfig.java
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration
{
@Bean
public RoleHierarchy roleHierarchy()
{
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER > ROLE_DEFAULT");
return roleHierarchy;
}
@Bean
public RoleVoter roleVoter()
{
return new RoleHierarchyVoter(roleHierarchy);
}
@Bean
@Override
protected AccessDecisionManager accessDecisionManager()
{
return new AffirmativeBased(Arrays.asList(roleVoter()));
}
// The method below was added in an attempt to get things working but it is never called
@Override
protected MethodSecurityExpressionHandler createExpressionHandler()
{
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setRoleHierarchy(roleHierarchy());
return handler;
}
}
在另一次尝试中,我AppConfig
扩展GlobalMethodSecurityConfiguration
但具有ROLE_ADMIN的用户无法调用需要ROLE_DEFAULT访问权限的方法。
我确定我在某处配置错误了一些东西,但尽管阅读了我可以找到的有关使用角色层次结构配置全局方法安全性的所有内容,但我无法弄清楚我哪里出了问题。 使用 XML 配置似乎这很简单,但 Java 配置解决方案让我无法理解。
我会覆盖GlobalMethodSecurityConfiguration#accessDecisionManager
方法。您可以看到 RoleVoter 使用的源代码。
这是我建议的覆盖源代码。
@Override
protected AccessDecisionManager accessDecisionManager() {
var roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_SUPER > ROLE_ADMIN");
var expressionHandler = (DefaultMethodSecurityExpressionHandler) getExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
var expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(expressionHandler);
return new AffirmativeBased(List.of(
new RoleHierarchyVoter(roleHierarchy),
new PreInvocationAuthorizationAdviceVoter(expressionAdvice),
new AuthenticatedVoter(),
new Jsr250Voter()
));
}
由于这个问题不断获得浏览量,我想我会发布后续内容。问题似乎出在生产线上
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER > ROLE_DEFAULT");
我不记得为什么我这样写层次结构,但这是不正确的。该方法的 API 因此处理相同的情况:
角色层次结构:ROLE_A> ROLE_B和ROLE_B> ROLE_C。
直接分配的权限:ROLE_A。
可联系的机构:ROLE_A、ROLE_B ROLE_C。
最终,很明显分层模型不适合我们的角色,因此我们实现了一组映射到角色的更细粒度的权限,如 Spring 安全参考中所述:
对于更复杂的要求,您可能希望在应用程序所需的特定访问权限和分配给用户的角色之间定义逻辑映射,在加载用户信息时在两者之间进行转换。