Expression-based Access with FreeMarker
05 April 2015
Spring Security 3.0 introduced the ability to use Spring EL expressions as an authorisation mechanism. Expression-based access control is built on the same architecture but allows complicated boolean logic to be encapsulated in a single expression. This comes handy when you want to secure a Controller method or even the entire Controller. JSP has native support for this feature using
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
FreeMarker don't so you need to do more than just importing a tag library.
I followed this article here tech talk, very simple yet precise!
Add JspTagLibs
Consider the first line of the snippet.
<#assign security=JspTaglibs["/WEB-INF/security.tld"]/>
<#include "/user/logout.ftl"/>
<#macro navigation>
<div class="masthead clearfix">
<div class="inner">
<h3 class="masthead-brand"><@spring.message "prototype"/></h3>
<nav>
<ul class="nav masthead-nav">
<#if Session["SPRING_SECURITY_CONTEXT"]?exists>
<li><@logout/></li>
<li><a href="home"><@spring.message "home"/></a></li>
<#assign authorities = Session["SPRING_SECURITY_CONTEXT"].authentication.authorities />
<#if authorities?seq_contains("ROLE_ADMIN")>
<li><a href="listUsers"><@spring.message "users"/></a></li>
</#if>
</#if>
</ul>
</nav>
</div>
</div>
</#macro>
It's a shame I cannot use <security:authorize access="hasRole('ROLE_ADMIN')">
but the end result is the same, you will gain authorisation to specified pages.
Copy security.tld
-
Open your
spring-security-taglibs-x.x.x.RELEASE.jar
.
This is one of the jars inside.m2/repository/org/springframework/security/spring-security-taglibs
if you are using maven. -
Copy
security.tld
fromMETA-INF
folder. -
Paste it into your
src/main/webapp/WEB-INF
folder.
Check your POM
Make sure you have spring-security-taglibs
dependency.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
Check your Spring Security @Configuration
If you are extending WebSecurityConfigurerAdapter
and overriding configure to create your own AuthenticationManagerBuilder, you will have BeanCreationException.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodSecurityInterceptor' defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: An AuthenticationManager is required
The solution is documented in Spring Security docs. You can check my own configuration on Github.
public AuthenticationManager authenticationManagerBean() throws Exception
Override this method to expose the AuthenticationManager from configure(AuthenticationManagerBuilder) to be exposed as a Bean. For example:
@Bean(name name="myAuthenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Returns:
the AuthenticationManager
Throws:
Exception
Finally, annotate your @Controller
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "listUsers", method = RequestMethod.GET)
public ModelAndView listUsers() {
ModelAndView mav = new ModelAndView();
mav.addObject("users", userService.list());
mav.setViewName(ControllerConstant.USER_LIST);
return mav;
}