Thymeleaf is a popular templating engine, particularly with Spring projects. Spring Boot has chosen Thymeleaf as the view technology of choice, largely replacing the need for JSP. With old JSPs, custom tag libraries provided integration with various technologies, including Spring Security. A similar library exists to integrate Thymeleaf and Spring Security – the Thymeleaf Spring Security Integration module.
The Thymeleaf Spring Security Integration module is a direct equivalent of the Spring Security JSP taglib. It is intended to allow access to the Spring Security Authentication object (containing username, roles etc.) and can show / hide page fragments based on user authorization.
Accessing authentication and authorization
Integration of Thymeleaf and Spring Security is based largely on two attributes provided by the integration module.
The user Authentication is the details on who the user is: username, authorities and whether or not they’ve been authenticated (logged in). Authentication can be accessed via the sec:authentication attribute.
User authorization determines what the user can do and see: permissions and roles. Authorization details can be accessed via the sec:authorizeattribute.
As an example of both, here’s a fragment that shows a link to the login page if the user is not already logged in. If they are logged in it instead shows their username and a link to the logout page.
<div sec:authorize="!isAuthenticated()"> <li><a th:href="@{/login}">Sign in</a></li> </div> <div sec:authorize="isAuthenticated()"> <li><a th:href="@{/logout}">Logout (<span sec:authentication="principal.username"/>)</a></li> </div>
Role based access
Sections can be shown only if a user has a particular role using the same attributes. As an example, here’s a link that is shown only to ADMIN users.
<div sec:authorize="hasRole('ROLE_ADMIN')"> <li><a th:href="@{/admin/switchUser}">Switch User</a></li> </div>
Restrict access to specific objects
In the spanners demo application, we want to allow users to edit or delete a spanner only if they own it.
This is slightly fiddly to implement but can be done by comparing the spanner owner with the Authentication name:
<tr th:each="spanner: ${spanners}"> <td th:text="${spanner.name}">Name</td> <td th:text="${spanner.size}">Size</td> <td th:text="${spanner.owner}">Owner</td> <td><a th:href="@{/detailSpanner(id=${spanner.id})}">View</a></td> <td th:if="${#authentication.name } == ${spanner.owner}"><a th:href="@{/editSpanner(id=${spanner.id})}">Edit</a></td> <td th:unless="${#authentication.name } == ${spanner.owner}">-</td> <td th:if="${#authentication.name } == ${spanner.owner}"><a th:href="@{/deleteSpanner(id=${spanner.id})}">Delete</a></td> <td th:unless="${#authentication.name } == ${spanner.owner}">-</td> </tr>
Note that each table element has two <td> elements one with td:if and one with td:unless. The td:if outputs the link if the authenticated user is the spanner owner. The td:unless is the corresponding ‘else’ logic that shows an empty cell instead.
Client / Server side security
It is important to note that using Thymeleaf and Spring Security in this way allows us use Spring Security to present our view. Thymeleaf manages display logic only and is not itself a security mechanism. As an example, the URL to delete a spanner is of the format
/deleteSpanner?id=<spanner.id>
Thymeleaf can show or hide this link on a page but cannot prevent a user from simply typing it into a browser. To secure the application, the security mechanism must be implemented on the server-side application code, not on the client side presentation code.
We can block access based on URL patterns using Spring Security Java Configuration http.authorizeRequests() method. For example, to block anything on /admin to everyone except admin users:
http .authorizeRequests() .antMatchers("/css/**", "/img/**", "/js/**", "/signup").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated()
More sophisticated access control can be implemented on individual objects using expression based annotations as described in a previous post. To make this work using Java based configuration, annotate the configuration class with @EnableWebSecurity
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
Then expression based security can be implemented on any method:
@PreAuthorize("hasPermission(#spanner, 'owner')") public void delete(Spanner spanner) {
Should a user attempt to access a URL that calls this method, Spring Security will step in and throw an exception rather than invoke the method. The exception will bubble up and result in a HTTP 403 Forbidden error to the user.
Thymeleaf and Spring Security example code
Examples of Thymeleaf and Spring Security shown in this post are taken from the Spanners Demo application in GitHub, specifically the spanners-mvc project.
Be First to Comment