Spring Security Role Hierarchy 无法使用 Java Config
Posted
技术标签:
【中文标题】Spring Security Role Hierarchy 无法使用 Java Config【英文标题】:Spring Security Role Hierarchy not working using Java Config 【发布时间】:2015-07-05 11:25:44 【问题描述】:首先,我是 Java Spring 框架的新手。如果我没有提供足够的信息,请原谅我。我试图将 RoleHierarchy 添加到我的应用程序中,但它不起作用。以下是我尝试过的代码。
SecurityConfig.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
MethodSecurityConfig.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 UserDetailsService
提供principal
、Authentication
和GrantedAuthority
我终于有了一些 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_STAFF
和 ROLE_DEVELOPER
工作正常。但我希望ROLE_ADMIN
成为两者的超级角色,但它不起作用。
仅供参考,我正在使用 spring-security 3.2.5.RELEASE
【问题讨论】:
【参考方案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()
会覆盖之前的设置
【讨论】:
请注意,使用“and”是可选的。对于 Spring,""ROLE_ADMIN > ROLE_STAFF and ROLE_ADMIN" 等价于 ""ROLE_ADMIN > ROLE_STAFF ROLE_ADMIN"。我更喜欢你的符号,但只是说 这实际上是有效的,即使它使用可选的语法。如果它抛出某种语法异常会很好,因为我有A > B > C
对 Spring-security 5.6.1 使用 "..and
.." 不适用于男性。应该是A > B > C
【参考方案2】:
每当我想使用 Spring Security 和 Java 配置实现角色层次结构时,我都会使用以下方法:
我们必须在上下文中添加一个 RoleHierarchyImpl bean(你看,我使用 多个角色 来构建层次结构):
@Bean
public RoleHierarchyImpl roleHierarchy()
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_DBA ROLE_DBA > ROLE_USER ");
return roleHierarchy;
然后我们需要创建 web 表达式处理程序来将获得的层次结构传递给它:
private SecurityExpressionHandler<FilterInvocation> webExpressionHandler()
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy());
return defaultWebSecurityExpressionHandler;
最后一步是将 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());
结果:在这个特定示例中,我们尝试在使用管理员用户 (ROLE_ADMIN) 登录后访问 /dba 部分。在我们创建层次结构之前,我们有一个拒绝访问的结果,但现在我们可以毫无问题地访问此部分。
【讨论】:
感谢您的回答 - 这比我在 google 上搜索时得到的任何一半尝试要好得多。另请注意,为此,您需要授予ROLE_ADMIN
的权限,而不是像我试图做的那样只是ADMIN
。【参考方案3】:
注意:接受的答案在最新版本的 Spring 安全性中不起作用(我认为自 5.2.1 版起)。 这是因为'and' (ROLE_1 > ROLE_2 and ROLE_2 > ROLE_3) 符号从来都不是官方标准。您可以写下每个单词而不是“和”,并且在过去的版本中仍然可以使用。
相反,在新版本中,您现在应该使用 '\n'(新行),例如ROLE_1 > ROLE_2\nROLE2 > ROLE_3 ...
【讨论】:
【参考方案4】:对我来说,解决方案是为 DefaultWebSecurityExpressionHandler
的实例设置正确的 bean 名称。名称应为webSecurityExpressionHandler
。
@Bean
public RoleHierarchyImpl roleHierarchy()
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(Roles.getRoleHierarchy());
return roleHierarchy;
@Bean
public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler()
DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy());
return expressionHandler;
@Override
protected void configure(HttpSecurity http) throws Exception
http.authorizeRequests()
.expressionHandler(webSecurityExpressionHandler())
...
【讨论】:
【参考方案5】:覆盖createExpressionHandler
方法,使其返回配置的全局表达式处理程序
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration
@Autowired
private RoleHierarchy roleHierarchy;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler()
return methodSecurityExpressionHandler();
private DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler()
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
return expressionHandler;
@Bean
public RoleHierarchyImpl roleHierarchy()
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_OWNER > ROLE_USER");
return roleHierarchy;
@Bean
public RoleHierarchyVoter roleVoter()
return new RoleHierarchyVoter(roleHierarchy);
@Configuration
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
【讨论】:
【参考方案6】:正确的格式是:ROLE_A > ROLE_B and ROLE_B > ROLE_C.
见doc。
@Bean
public RoleHierarchy roleHierarchy()
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(UserRole.ROLE_HIERARCHY);
return roleHierarchy;
【讨论】:
【参考方案7】:我不使用 Spring RoleHierarchy - 因为它对我不起作用。 但通常我喜欢这样: 定义角色接口
public static interface Role
String getName();
List<String> getHierarchy();
我的角色列表(存储在数据库中):
public interface AuthStates
// Spring security works fine only with ROLE_*** prefix
String ANONYMOUS = "ROLE_ANONYMOUS";
String AUTHENTICATED = "ROLE_AUTHENTICATED";
String ADMINISTRATOR = "ROLE_ADMINISTRATOR";
将匿名角色定义为基本角色类:
public static class Anonymous implements Role
private final String name;
private final List<String> hierarchy = Lists.newArrayList(ANONYMOUS);
public Anonymous()
this(ANONYMOUS);
protected Anonymous(String name)
this.name = name;
@Override
public String getName()
return name;
@Override
public List<String> getHierarchy()
return hierarchy;
protected void addHierarchy(String name)
hierarchy.add(name);
Define Authenticated role(普通用户角色):
public static class Authenticated extends Anonymous
public Authenticated()
this(AUTHENTICATED);
protected Authenticated(String name)
super(name);
addHierarchy(AUTHENTICATED);
定义管理员角色(在进化的顶端):
public static class Administrator extends Authenticated
public Administrator()
this(ADMINISTRATOR);
protected Administrator(String name)
super(name);
addHierarchy(ADMINISTRATOR);
可选 - 静态工厂类:
public static Role getRole(String authState)
switch (authState)
case ANONYMOUS: return new Anonymous();
case AUTHENTICATED: return new Authenticated();
case ADMINISTRATOR: return new Administrator();
default: throw new IllegalArgumentException("Wrong auth state");
在我的 CustomUserDetailsService(实现 UserDetailsService)中,我使用这样的角色:
private Collection<GrantedAuthority> createAuthority(User user)
final List<GrantedAuthority> authorities = new ArrayList<>();
AuthStates.Role userAuthState = AuthStates.getRole(user.getAuthState());
for (String role : userAuthState.getHierarchy())
authorities.add(new SimpleGrantedAuthority(role));
return authorities;
authorities
在控制器中:
@PreAuthorize("hasRole('ROLE_AUTHENTICATED')")
将允许用户以 ROLE_AUTHENTICATED 和 ROLE_ADMINISTRATOR 身份登录。
【讨论】:
以上是关于Spring Security Role Hierarchy 无法使用 Java Config的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security 中 Role 和 GrantedAuthority 的区别
不再需要 Spring Security ROLE_ 前缀?
Grails spring security LDAP 从 LDAP 基础获取 ROLE
带有 ROLE_ANONYMOUS 的 AngularJS 和 Spring Security 仍然返回 401