Spring Security role hierarchy not working with Java Config

First of all, I'm new to the Java Spring Framework. So forgive me if I did not provide enough information. I tried adding RoleHierarchy to my application, but it did not work. Below are the codes I tried.


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

And I have UserDetailsServiceImpl implements UserDetailsService that provide principal , Authentication and GrantedAuthority

Finally, I have some APIs:

 @PreAuthorize("hasRole('ROLE_STAFF')") @RequestMapping(value = "/api/v1/contactUs", method = RequestMethod.GET) @PreAuthorize("hasRole('ROLE_DEVELOPER')") @RequestMapping(value = "/api/v1/system", method = RequestMethod.GET) 

The problem now, if I log in as ROLE_STAFF, ROLE_DEVELOPER, ROLE_ADMIN, I got the following result.

 | API | ROLE_STAFF | ROLE_DEVELOPER | ROLE_ADMIN | |-----------|------------|----------------|------------| | contactUs | 200 | 403 | 403 | | system | 403 | 200 | 403 | 

As you can see, ROLE_STAFF and ROLE_DEVELOPER are working fine. But I want ROLE_ADMIN as a super-role of both, and that didn't work.

FYI, I am using spring-security 3.2.5.RELEASE

+5
source share
5 answers

The problem is RoleHierachy, which should be like this:

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

save the call to setHierarchy() override the parameter until

+13
source

Every time I want to implement a role hierarchy using Spring Security Configuration and Java, I use the following approach:

  • We need to add the RoleHierarchyImpl bean to the context (you see that I use several roles to create the hierarchy):

     @Bean public RoleHierarchyImpl roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_DBA ROLE_DBA > ROLE_USER "); return roleHierarchy; } 
  • Then we need to create a web expression handler to pass the resulting hierarchy:

     private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() { DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy()); return defaultWebSecurityExpressionHandler; } 
  • The final step is to add the Handler expression to 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()); } 

Result: in this particular example, we will try to visit the / dba partition after logging in using the admin user (ROLE_ADMIN). Before we created the hierarchy, we had the result of refusing access, but now we can easily visit this section.

+11
source

Override the createExpressionHandler method to return a customized global expression handler

  @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 {} } } 
0
source

For me, the solution had the correct bean name for the instance of DefaultWebSecurityExpressionHandler . The name must be 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()) ... } 
0
source

I do not use Spring RoleHierarchy - because it doea does not work for me. But I like Ussualy: Defining a Role Interface

 public static interface Role { String getName(); List<String> getHierarchy(); } 

List of my roles (for storage in the database):

 public interface AuthStates { // Spring security works fine only with ROLE_*** prefix String ANONYMOUS = "ROLE_ANONYMOUS"; String AUTHENTICATED = "ROLE_AUTHENTICATED"; String ADMINISTRATOR = "ROLE_ADMINISTRATOR"; } 

Define the role of anonymous as the main class of roles:

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

Identify the validated role (common user role):

 public static class Authenticated extends Anonymous { public Authenticated() { this(AUTHENTICATED); } protected Authenticated(String name) { super(name); addHierarchy(AUTHENTICATED); } } 

Define the administrator role (at the top of the evolution):

 public static class Administrator extends Authenticated { public Administrator() { this(ADMINISTRATOR); } protected Administrator(String name) { super(name); addHierarchy(ADMINISTRATOR); } } 

Optional - static factory class:

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

In my CustomUserDetailsService (which implements UserDetailsService) I use the following role:

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

authority

In controllers:

 @PreAuthorize("hasRole('ROLE_AUTHENTICATED')") 

Allows the user to register as ROLE_AUTHENTICATED and ROLE_ADMINISTRATOR.

-2
source

All Articles