Spring MVC Method level security using @PreAuthorize and @PostAuthorize

Spring security provides method level security using @PreAuthorize and @PostAuthorize. This is expression-based access control. @PreAuthorize can check for authorization before entering into method. @PreAuthorize is checked on the basis of role or the argument which is passed to the method. @PostAuthorize checks for authrorisation after method execution. @PostAuthorize can be authorized on the basis of logged in roles, return object by method and passed argument to the method. For the returned object spring security provides built-in keyword i.e. returnObject.

We need to define @PreAuthorize and @PostAuthorize in the interface of the service layer.

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

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="admin" password="123456"
authorities="ROLE_READ,ROLE_WRITE" />
 <user name="reader" 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>SpringSecurityMethodLevelXMLConfig</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 org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;

import com.tutorialsdesk.model.Folder;

public interface CustomService {
 
 @PreAuthorize ("hasRole('ROLE_WRITE')")
 public void addFolder(Folder folder);

 @PostAuthorize ("hasRole('ROLE_READ')")
 public Folder getFolder();
 
 @PostAuthorize ("returnObject.owner == authentication.name")
 public Folder getFolderByOwnerName();

 @PreAuthorize ("#folder.owner == authentication.name")
 public void deleteFolder(Folder folder);
 
}

Look at the interface how to define @PreAuthorize and @PostAuthorize. authentication and principal keyword can directly be used to access user information. # is used to access argument of the method. Take attention on @PostAuthorize, built-in keyword returnObject has been used. Here returnObject is equivalent to Book instance returned by the method.

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

import org.springframework.stereotype.Service;

import com.tutorialsdesk.model.Folder;

@Service
public class CustomServiceImpl implements CustomService {

 @Override
 public void addFolder(Folder folder) {
 System.out.println("You have successfully added Folder.");
 
 }

 @Override
 public Folder getFolder() {
 Folder folder = new Folder("PQR","reader");
 return folder;
 }

 @Override
 public Folder getFolderByOwnerName() {
 Folder folder = new Folder("XYZ","admin");
 return folder;
 }
 
 @Override
 public void deleteFolder(Folder folder) {
 System.out.println("Folder deleted");
 
 }

}

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

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
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) {
 return "welcome";
 }
 
 @RequestMapping(value = { "/secureRead" }, method = RequestMethod.GET)
 public String secureReadPage(ModelMap model) {
 
 Folder folder=customService.getFolder();
 
 model.addAttribute("folder", folder.getFolderName());
 
 return "secure";
 }
 
 @RequestMapping(value = { "/secureWrite" }, method = RequestMethod.GET)
 public String secureWritePage(ModelMap model) {

 customService.getFolder();
 
 Folder folder=customService.getFolderByOwnerName();
 
 model.addAttribute("folder", folder.getFolderName());
 
 Folder folder1 = new Folder("ABC","reader");
 customService.addFolder(folder1);
 
 model.addAttribute("addedfolder", folder1.getFolderName());
 
 Folder folder2 = new Folder("XYZ","admin");
 customService.deleteFolder(folder2);
 
 model.addAttribute("deletedfolder", folder2.getFolderName());
 
 return "secure";
 }
 
 @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 = "/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;
 }
}

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 (Method Level Security)</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 to Read 
 <a href="<c:url value="/secureRead"
/>">Secure</a> Page.
 </div>
 <br/>
 <div>
 Please click this to visit to Write
 <a href="<c:url value="/secureWrite"
/>">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="/home" />">Home</a>
 <a href="<c:url value="/logout" />">Logout</a>
 
 <br/>
 <br/>
 <div>
 <sec:authorize access="hasRole('READ')">
 <label>Folder : ${folder} </label>
 </sec:authorize>
 <br/>
 </div>
 <div>
 <sec:authorize access="hasRole('WRITE')">
 <c:if test="${not empty folder}">
 <label>Folder : ${folder} </label>
 </c:if>
 <br/>
 <c:if test="${not empty addedfolder}">
 <label>Added Folder : ${addedfolder} </label>
 </c:if>
 <br/>
 <c:if test="${not empty deletedfolder}">
 <label>Deleted Folder : ${deletedfolder} </label>
 </c:if>
 </sec:authorize>
 </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
 <br/><br/><a href="<c:url value="/home"
/>">Home</a> | <a href="<c:url
value="/logout" />">Logout</a>
</body>
</html>
STEP 12 :- Run your project enter below URL in your browser

http://localhost:8080/SpringSecurityMethodLevelXMLConfig/

Keep visiting TutorialsDesk for more tutorials and practical programming examples on Spring MVC. Hope we are able to explain you Spring MVC Method level security using @PreAuthorize and @PostAuthorize 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.