@PreFilter and @PostFilter in Spring Security

By Arvind Rai, November 27, 2019
The @PreFilter and @PostFilter is a strong feature in Spring Security that filter collections or arrays on the basis of authorization. This is achieved using expression-based access control in Spring Security. The @PreFilter filters the collections or arrays before executing method. The @PostFilter filters the returned collection or arrays after executing the method. Spring security provides a built-in object named as filterObject using which @PreFilter and @PostFilter performs the filtering task. The @PreFilter and @PostFilter can be used on service layer with @PreAuthorize and @PostAuthorize annotations.

Using @PreFilter and @PostFilter

Find the example.
IBookService.java
package com.concretepage.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.concretepage.bean.Book;
public interface IBookService {
	@PreAuthorize ("hasRole('ROLE_READ')")
	@PostFilter ("filterObject.owner == authentication.name")
	public List<Book> getBooks();
	
	@PreFilter("filterObject.owner == authentication.name")
	public void addBook(List<Book> books);
} 
The filterObject is built-in object on which filter operation is performed. In the above code, for the first method getBooks(), we have used @PreAuthorize and @PostFilter annotations. 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 addBook() is only using @PreFilter on the basis of owner. Find the implementation class of IBookService file.
BookService.java
package com.concretepage.service;
import java.util.ArrayList;
import java.util.List;
import com.concretepage.bean.Book;
public class BookService implements IBookService {
	@Override
	public List<Book> getBooks() {
		Book b1 = new Book("A","rahim");
		Book b2 = new Book("B","ram");
		Book b3 = new Book("C","rahim");
		Book b4 = new Book("D","ram");
		List<Book> books = new ArrayList<Book>();
		books.add(b1);
		books.add(b2);
		books.add(b3);
		books.add(b4);
		return books;
	}
	@Override
	public void addBook(List<Book> books) {
		System.out.println("Books Added.");
		for(Book b: books){
			System.out.println(b.getName()+":"+b.getOwner());
		}
	}
} 

Enable pre-post Annotations

Find the Spring Security XML configuration to enable @PreFilter and @PostFilter annotation.
<global-method-security pre-post-annotations="enabled"/>
 
In case we are using Java configuration, then use @EnableGlobalMethodSecurity as given below.
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   ------
}

Complete Example

We have configured two users ram and rahim. The ram has ROLE_READ and ROLE_WRITE whereas rahim has ROLE_READ.
security-config.xml
  <http auto-config="true" use-expressions="true">
   <intercept-url pattern="/login" access="hasAnyRole('ROLE_READ','ROLE_WRITE')" />
   <logout logout-success-url="/login" />
  </http>
  <authentication-manager>
  <authentication-provider>
   <user-service>
	<user name="ram" password="con1234" authorities="ROLE_READ,ROLE_WRITE" />
	<user name="rahim" password="con1234" authorities="ROLE_READ" />
  </user-service>
  </authentication-provider>
  </authentication-manager>
  <global-method-security pre-post-annotations="enabled"/>
  <beans:bean name="bookService" class="com.concretepage.service.BookService"/> 
In the controller, on the basis of role we are calling BookService method, so that no access denied error is caught.
LoginController.java
package com.concretepage.security.controller;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
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 com.concretepage.bean.Book;
import com.concretepage.service.IBookService;
@Controller
@RequestMapping("/login")
public class LoginController {
	@Autowired
	public IBookService bookService;
	@RequestMapping(method = RequestMethod.GET)
	public String success(ModelMap map) {		
		Book b1 = new Book("A","rahim");
		Book b2 = new Book("B","ram");
		Book b3 = new Book("C","rahim");
		Book b4 = new Book("D","ram");
		List<Book> books = new ArrayList<Book>();
		books.add(b1);
		books.add(b2);
		books.add(b3);
		books.add(b4);
		if(hasRole("ROLE_WRITE")){
		  bookService.addBook(books);
		}
		if(hasRole("ROLE_READ")){
		  List<Book> bks = bookService.getBooks();
		  System.out.println("Books we got.");
		  for(Book b: bks){
				System.out.println(b.getName()+":"+b.getOwner());
		  }
		}
		map.addAttribute("msg", "Done Successfully");
		return "success";
	}  
	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;
	  }
} 
Book.java
package com.concretepage.bean;
public class Book {
	private String name;
	private String owner;
	public Book(String name,String owner){
		this.name = name;
		this.owner = owner;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getOwner() {
		return owner;
	}
	public void setOwner(String owner) {
		this.owner = owner;
	}
} 
Now check the output by running the application. Download the source code which we are given at the end of the blog. Use the URL as http://localhost:8080/SpringSecurity/login.
@PreFilter and @PostFilter in Spring Security
Login with ram/con1234, you will get below log on your console. What we need to pay attention is that list contains books for ram and rahim but ram will add and get only his books.
Books Added.
B:ram
D:ram
Books we got.
B:ram
D:ram 
Login with rahim/con1234, you will get below log on your console. List contains books for ram and rahim, but it is filtered and rahim can only get his books.
Books we got.
A:rahim
C:rahim 

Download Complete Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us