Spring Security - User Roles and ThymeLeaf Extras

In the last lesson, we learnt how to use spring security to build a basic login form.

Today, we'll be looking at adding user roles and some nice feature's of the Thymeleaf library to show and hide content based on these roles.

Source code for this example can be found on github:
codenerve-com/spring-security
An introduction to spring security. Contribute to codenerve-com/spring-security development by creating an account on GitHub.

Dependencies

As with all Spring boot application's, there are a number of starter libraries that make it easy to add jars to your classpath. In addition, these can auto-configure various spring beans and behaviours that we can make use of. Building upon the previous lesson we will be adding:

  • thymeleaf-extras-springsecurity4 - this has a number of additional features over the basic thymeleaf jar

WebSecurityConfig

To begin this lesson we'll be adding another user to our application's WebSecurityConfig but this time giving them a new role 'admin':

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .inMemoryAuthentication()
        .withUser("user").password("{noop}pass").roles("USER")
        .and()
        .withUser("admin").password("{noop}pass").roles("ADMIN");
}
WebSecurityConfig.java

This now gives us two available user's to log in with.

Each is configured with a different role that we can now use to hide and show various content.

Thymeleaf: Authorize

Next, we'll be using a Thymeleaf attribute sec:authorize to check a users roles before rendering the div's in our index.html page.

To do this, go to the index.html page and add the following HTML inside the body tags:

<div sec:authorize="hasRole('ADMIN')">
    This content is only shown to administrators.
</div>

<div sec:authorize="hasRole('USER')">
    This content is only shown to users.
</div>
index.html

As you can see the sec:authorize attribute is added to each div, and we use something called the spring security dialect to check users spring security roles. Content will only be rendered if the logged-in user has that role. i.e. hasRole returns true.

It is important to note that content is not just hidden from view but will not be rendered at all when our application server returns the page to the browser.

Thymeleaf: Authentication

Another useful Thymeleaf feature is the sec:authentication attribute. This can return various security-related metadata. In the example below, we can retrieve the user's username and roles and display these in our HTML.

<div>
    User: <span sec:authentication="name">NOT FOUND</span>
    Spring Roles: <span sec:authentication="principal.authorities">NOT FOUND</span>
</div>
index.html

For more useful attribute's see the Thymeleaf documentation

Demo

To run the demo, open the Application class and right click run. In order to start the example, the port 8080 will need to be available on your machine. If it is not you can change this default in the application.properties file using:

server.port=8081

Set this to whatever value you wish.

Unit Testing

We've added a few more unit tests to cover this new functionality:

@Test
@WithMockUser(roles = "USER")
public void loginWithRoleUserThenExpectUserSpecificContent() throws Exception {
	mockMvc.perform(get("/index"))
			.andExpect(status().isOk())
			.andExpect(content().string(containsString("This content is only shown to users.")))
			.andExpect(content().string(doesNotContainString("This content is only shown to administrators.")));
}

@Test
@WithMockUser(roles = "ADMIN")
public void loginWithRoleAdminThenExpectAdminSpecificContent() throws Exception {
	mockMvc.perform(get("/index"))
			.andExpect(status().isOk())
			.andExpect(content().string(containsString("This content is only shown to administrators.")))
			.andExpect(content().string(doesNotContainString("This content is only shown to users.")));
}

private Matcher<String> doesNotContainString(String s) {
	return CoreMatchers.not(containsString(s));
}
ApplicationTests.java

As you can see we've created a utility method doesNotContainString. Using this and Hamcrests containsString method, we can check that content is rendered (or not) based on a particular user role.

Next

Next up, we will be continuing to cover spring security's user roles, but this time we will be redirecting admins to there own admin page (admin.html) and securing this page so that only admin users can access it.

Michael Whyte

Michael Whyte