Spring MVC Security @PreFilter and @PostFilter annotations

@PreFilter and @PostFilter are designated to use with Spring security to be able to filter collections or arrays based on the authorization.

To have this working, you need to use expression-based access control in spring security

@PreFilter - filters the collection or arrays before executing method.

@PostFilter - filters the returned collection or arrays after executing the method.

Spring security provides a built-in object named as filterObject at which @PreFilter and @PostFilter performs filtering task. @PreFilter and @PostFilter can be used on service layer with @PreAuthorize and @PostAuthorize. Use interface to declare the filter operation.

STEP 1:- Open Eclipse and Create Dynamic Web Project named SpringSecurityFilterAnnotation

STEP 2:- Make sure you use Target Runtime as Apache Tomcat 7.0. STEP 3:- copy below jars to WEB-INF/lib folder.
  • aopalliance-1.0.jar
  • commons-logging-1.2.jar
  • spring-aop-4.1.4.RELEASE.jar
  • spring-beans-4.1.4.RELEASE.jar
  • spring-context-4.1.4.RELEASE.jar
  • spring-core-4.1.4.RELEASE.jar
  • spring-expression-4.1.4.RELEASE.jar
  • spring-security-config-4.0.2.RELEASE.jar
  • spring-security-core-4.0.2.RELEASE.jar
  • spring-security-taglibs-4.0.2.RELEASE.jar
  • spring-security-web-4.0.2.RELEASE.jar
  • spring-web-4.1.4.RELEASE.jar
  • spring-webmvc-4.1.4.RELEASE.jar
STEP 4:- Create Spring Configuration Bean file. /WebContent/WEB-INF/dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:mvc="http://www.springframework.org/schema/mvc" 
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/mvc 
 http://www.springframework.org/schema/mvc/spring-mvc.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd">
 
 <context:component-scan base-package="com.tutorialsdesk.controller" />
 
 <bean id="viewResolver"
 class="org.springframework.web.servlet.view.UrlBasedViewResolver">
 <property name="viewClass"
 value="org.springframework.web.servlet.view.JstlView" />
 <property name="prefix" value="/WEB-INF/views/" />
 <property name="suffix" value=".jsp" />
 </bean>
 <mvc:annotation-driven/>
</beans>

STEP 5:- Create Spring security configuration file. /WebContent/WEB-INF/spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
 xmlns:beans="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/security 
 http://www.springframework.org/schema/security/spring-security.xsd">
 
 <http auto-config="true" >
 <intercept-url pattern="/home" access="hasAnyRole('ROLE_READ','ROLE_WRITE','ROLE_NONE')" />
<!-- access denied page-->
 <access-denied-handler error-page="/Access_Denied" />
 
 <form-login 
 login-processing-url="/login"
 login-page="/login" 
 default-target-url="/home" 
 username-parameter="username"
 password-parameter="password"
 authentication-failure-url="/login?error"/> 
 
 </http>
 
 <authentication-manager >
 <authentication-provider>
 <user-service>
 <user name="superman" password="123456" authorities="ROLE_READ,ROLE_WRITE" />
 <user name="spiderman" password="123456" authorities="ROLE_READ" />
 <user name="user" password="123456" authorities="ROLE_NONE" />
 </user-service>
 </authentication-provider>
 </authentication-manager>
 
 <!-- <global-method-security secured-annotations="enabled"/> -->
 <global-method-security pre-post-annotations="enabled"/>
 
 <beans:bean id="customService" class="com.tutorialsdesk.service.CustomServiceImpl" />
 
</beans:beans>

In order to enable Spring Method level Security, we need to add <global-method-security pre-post-annotations="enabled"/> in security context file, as shown above.

STEP 6 :- Map Spring configuration files in /WebContent/WEB-INF/web.xml file as below :-
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
 <display-name>SpringSecurityFilterAnnotation</display-name>
 <servlet>
 <servlet-name>dispatcher</servlet-name>
 <servlet-class>
 org.springframework.web.servlet.DispatcherServlet
 </servlet-class>
 <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
 <servlet-name>dispatcher</servlet-name>
 <url-pattern>/</url-pattern>
 </servlet-mapping>
 <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>
 /WEB-INF/spring-security.xml
 </param-value>
 </context-param>
 <listener>
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 <filter>
 <filter-name>springSecurityFilterChain</filter-name>
 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
 </filter>
 <filter-mapping>
 <filter-name>springSecurityFilterChain</filter-name>
 <url-pattern>/*</url-pattern>
 </filter-mapping>
</web-app>

STEP 7 :- Create Model Class.
  • Package: com.tutorialsdesk.model
  • Filename: Folder.java
package com.tutorialsdesk.model;

public class Folder {

 private String folderName;
 private String owner;
 public Folder(String folderName, String owner) {
 super();
 this.folderName = folderName;
 this.owner = owner;
 }
 public String getFolderName() {
 return folderName;
 }
 public void setFolderName(String folderName) {
 this.folderName = folderName;
 }
 public String getOwner() {
 return owner;
 }
 public void setOwner(String owner) {
 this.owner = owner;
 }
}

STEP 8 :- Create Service Interface.
  • Package: com.tutorialsdesk.service
  • Filename: CustomService.java
package com.tutorialsdesk.service;

import java.util.List;

import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;

import com.tutorialsdesk.model.Folder;

public interface CustomService {
 
 @PreAuthorize ("hasRole('ROLE_READ')")
 @PostFilter ("filterObject.owner == authentication.name")
 public List<Folder> getFolders();
 
 @PreFilter("filterObject.owner == authentication.name")
 public void addFolder(List<Folder> folders);
}

filterObject is built-in object on which filter operation is performed. In this declaration, for the first method getFolders() we have used @PreAuthorize and @PostFilter. Before executing method, user is authorized on the basis of role and then after executing, the returned object is filtered on the basis of owner. Second method addFolder() is only using @PreFilter on the basis of owner

STEP 9 :- Create Service Impl Class.
  • Package: com.tutorialsdesk.service
  • Filename: CustomServiceImpl.java
package com.tutorialsdesk.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Service;

import com.tutorialsdesk.model.Folder;

@Service
public class CustomServiceImpl implements CustomService {

 @Override
 public List<Folder> getFolders() {
 
 List<Folder> folders = new ArrayList<Folder>();
 folders.add(new Folder("ABC","superman"));
 folders.add(new Folder("XYZ","spiderman"));
 folders.add(new Folder("JKL","spiderman"));
 folders.add(new Folder("MNP","superman"));
 
 return folders;
 }

 @Override
 public void addFolder(List<Folder> folders) {
 System.out.println("Folders Added.");
 for(Folder f: folders){
 System.out.println(f.getFolderName()+":"+f.getOwner());
 }
 
 }

}

STEP 10 :- Create Controller Class.
  • Package: com.tutorialsdesk.controller
  • Filename: IndexController.java
package com.tutorialsdesk.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.tutorialsdesk.model.Folder;
import com.tutorialsdesk.service.CustomService;

@Controller
public class IndexController {

 @Autowired
 private CustomService customService;
 
 @RequestMapping(value = {"/", "/login" }, method = RequestMethod.GET)
 public String loginPage(ModelMap model, @RequestParam(value = "error", required = false) String error) {
 
 if (error != null) {
 model.addAttribute("error", "Invalid username and password!");
 }
 return "login";
 }
 
 @RequestMapping(value = { "/home" }, method = RequestMethod.GET)
 public String homePage(ModelMap model) {

 List<Folder> folders = new ArrayList<Folder>();
 folders.add(new Folder("ABC","superman"));
 folders.add(new Folder("XYZ","spiderman"));
 folders.add(new Folder("JKL","spiderman"));
 folders.add(new Folder("MNP","superman"));
 
 if(hasRole("ROLE_WRITE")){
 customService.addFolder(folders);
 }
 if(hasRole("ROLE_READ")){
 List<Folder> foldersList = customService.getFolders();
 System.out.println("Folders we got.");
 for(Folder f: foldersList){
 System.out.println(f.getFolderName()+":"+f.getOwner());
 }
 }
 model.addAttribute("msg", "Done Successfully");
 
 return "welcome";
 }
 
 
 @RequestMapping(value="/logout", method = RequestMethod.GET)
 public String logoutPage (ModelMap model,HttpServletRequest request, HttpServletResponse response) {
 Authentication auth = SecurityContextHolder.getContext().getAuthentication();
 if (auth != null){ 
 new SecurityContextLogoutHandler().logout(request, response, auth);
 }
 model.addAttribute("msg", "You've been logged out successfully.");
 return "login";
 }
 
 @RequestMapping(value = "/secure", method = RequestMethod.GET)
 public String securePage(ModelMap model) {
 model.addAttribute("user", getPrincipal());
 return "secure";
 }

 @RequestMapping(value = "/Access_Denied", method = RequestMethod.GET)
 public String accessDeniedPage(ModelMap model) {
 model.addAttribute("user", getPrincipal());
 return "accessDenied";
 }
 
 private String getPrincipal(){
 String userName = null;
 Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
 
 if (principal instanceof UserDetails) {
 userName = ((UserDetails)principal).getUsername();
 } else {
 userName = principal.toString();
 }
 return userName;
 }
 
 private boolean hasRole(String role) {
 Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>)SecurityContextHolder.getContext().getAuthentication().getAuthorities();
 boolean hasRole = false;
 for (GrantedAuthority authority : authorities) {
 hasRole = authority.getAuthority().equals(role);
 if (hasRole) {
 break;
 }
 }
 return hasRole;
 }
}
STEP 11 :- Create jsp files in /WebContent/WEB-INF/views folder
  • Filename: login.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="true"%>
<html>
<head>
<title>Login Page</title>
<style>
.error {
 padding: 15px;
 margin-bottom: 20px;
 border: 1px solid transparent;
 border-radius: 4px;
 color: #a94442;
 background-color: #f2dede;
 border-color: #ebccd1;
}

.msg {
 padding: 15px;
 margin-bottom: 20px;
 border: 1px solid transparent;
 border-radius: 4px;
 color: #31708f;
 background-color: #d9edf7;
 border-color: #bce8f1;
}

#login-box {
 width: 300px;
 padding: 20px;
 margin: 100px auto;
 background: #fff;
 -webkit-border-radius: 2px;
 -moz-border-radius: 2px;
 border: 1px solid #000;
}
</style>
</head>
<body onload='document.loginForm.username.focus();'>

 <h1>Spring Security Login Form (Database Authentication)</h1>

 <div id="login-box">

 <h2>Login with Username and Password</h2>

 <c:if test="${not empty error}">
 <div class="error">${error}</div>
 </c:if>
 <c:if test="${not empty msg}">
 <div class="msg">${msg}</div>
 </c:if>

 <form name='loginForm'
 action="<c:url value='/login' />" method='POST'>

 <table>
 <tr>
 <td>User:</td>
 <td><input type='text' name='username'></td>
 </tr>
 <tr>
 <td>Password:</td>
 <td><input type='password' name='password' /></td>
 </tr>
 <tr>
 <td colspan='2'><input name="submit" type="submit"
 value="submit" /></td>
 </tr>
 </table>

 <input type="hidden" name="${_csrf.parameterName}"
 value="${_csrf.token}" />

 </form>
 </div>

</body>
</html>

  • Filename: welcome.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <title>Welcome page</title>
</head>
<body>
 Dear <strong>${user}</strong>, Welcome to Home Page.
 <a href="<c:url value="/logout" />">Logout</a>
 
 <br/>
 <br/>
 <div>
 <label>View all information| This part is visible to Everyone</label>
 </div>
 <br/>
 <br/>
 <div>
 Please click this to visit 
 <a href="<c:url value="/secure" />">Secure</a> Page.
 </div>
 <br/>
 <div>
 <sec:authorize access="hasRole('ADMIN')">
 <label><a href="#">Edit this page</a> | This part is visible only to ADMIN</label>
 </sec:authorize>
 </div>
 <br/>
 <div>
 <sec:authorize access="hasRole('API')">
 <label><a href="#">Start backup</a> | This part is visible only to one who has API rights.</label>
 </sec:authorize>
 </div>
 
 <br/>
 <div>
 <sec:authorize access="hasRole('ADMIN') and hasRole('API')">
 <label><a href="#">Start backup</a> | This part is visible only to one who is both ADMIN & API</label>
 </sec:authorize>
 </div>
</html>

  • Filename: secure.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <title>Welcome page</title>
</head>
<body>
 Dear <strong>${user}</strong>, Welcome to Secured Page.
 <a href="<c:url value="/logout" />">Logout</a>
 
 <br/>
 <br/>
 <div>
 <label>This is secured</label>
 </div>
 </body>
 </html>

  • Filename: accessDenied.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <title>AccessDenied page</title>
</head>
<body>
 Dear <strong>${user}</strong>, You are not authorized to access this page
 <a href="<c:url value="/logout" />">Logout</a>
</body>
</html>

STEP 12 :- Run your project enter below URL in your browser

http://localhost:8080/SpringSecurityFilterAnnotation/

Keep visiting TutorialsDesk for more tutorials and practical programming examples on Spring MVC. Hope we are able to explain you Spring MVC Security @PreFilter and @PostFilter annotation Example, if you have any questions or suggestions please write to us using contact us form.

Please share us on social media if you like the tutorial.
SHARE
    Blogger Comment
    Facebook Comment