Sep

10

While lots of sites on line describe adding the @Secured tag to your Spring Security-enabled web app, and some even describe role hierarchies, I was unable to find any that did so with JavaConfig. Most of them wanted to give me XML, which isn’t where I wanted to go today. Here’s what I’ve learned, in the hopes it saves you some time.

My application has two application contexts, one for MVC, and a root application context. In order to make use of Spring Security in both JSPs and server-side in components, my SecurityConfig is included in the root context via an @Import annotation:


@Configuration
@ComponentScan("com.adeptechllc.control")
@Import({ DBConfig.class, SecurityConfig.class })
public class AppConfig {
...

The security config class in turn enables Web Security and Global Method Security to turn on annotations too. It also defines a role hierarchy. I found lots of examples of configuring such a hierarchy in XML, but none in JavaConfig. Turns out you just do it via the constructor, with a string.


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
private RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();

public SecurityConfig() {
roleHierarchy.setHierarchy(
“ROLE_ADMINISTRATOR_2 > ROLE_ADMINISTRATOR ” +
“ROLE_ADMINISTRATOR > ROLE_ACCOUNT_HOLDER “+
“ROLE_ACCOUNT_HOLDER > ROLE_USER ”
);
}

This code defines a simple hierarchy where “administrator_2” has all the rights of “administrator”, which in turn has all the rights of “account_holder” etc.

For HTTP security, this config also defines the security configuration:


@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.accessDecisionManager(getAccessDecisionManager())
.antMatchers("/login", "/login/**", "/logout", "/register").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMINISTRATOR")
.antMatchers("/account/**").hasAnyRole("ACCOUNT_HOLDER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.authenticationDetailsSource(authenticationDetailsSource)
.and()
.logout()
.logoutUrl("/logout") .logoutSuccessUrl("/login")
;
}

The only thing left to do is to hook up the access decision manager, and point it to the role hierarchy.


@Bean
public AffirmativeBased getAccessDecisionManager() {
DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);

WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
webExpressionVoter.setExpressionHandler(expressionHandler);

List<AccessDecisionVoter<? extends Object>> voters = new ArrayList<>();

voters.add(webExpressionVoter);
return new AffirmativeBased(voters);
}

With that, I simply add “@Secured(“ROLE_ADMINISTRATOR”)” to my methods on classes decorated with @Component or @Controller, and access roles are enforced by Spring on my behalf. Here’s a simple example:


@RequestMapping("admin/system")
@Secured("ROLE_ADMINISTRATOR_2")
public String adminSystemMenu(Principal princpal) {
return "/admin/system";
}


Comments

RSS feed | Trackback URI

1 Comment »

Comment by Alex Tsilingiris
2016-04-19 04:24:51

Spent the last few hours looking for a java config approach. Thanks a lot for this post!

 
Name (required)
E-mail (required - never shown publicly)
URI
Your Comment (smaller size | larger size)
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> in your comment.

Blogroll