Wednesday, January 19, 2011

Spring Security: Simple ACL using Expression-Based Access Control (Part 2)

In this tutorial we will continue what we had left off from the Spring Security: Simple ACL using Expression-Based Access Control (Part 1). Last time we set-up a simple ACL configuration. We also implemented the PermissionsEvaluator interface and provided our own custom Permissions list. In this part we will start building the MVC section of the application.

Spring MVC Section

We now move to the MVC section of the application.

Domain Objects

First, let's declare our domain objects:
AdminPost.java
PersonalPost.java
PublicPost.java
Here are the class declarations:

AdminPost.java
package org.krams.tutorial.domain;

import java.util.Date;

public class AdminPost {
 private String id;
 private Date date;
 private String message;
 
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public Date getDate() {
  return date;
 }
 public void setDate(Date date) {
  this.date = date;
 }
 public String getMessage() {
  return message;
 }
 public void setMessage(String message) {
  this.message = message;
 }
}

PersonalPost.java
package org.krams.tutorial.domain;

import java.util.Date;

public class PersonalPost {
 private String id;
 private Date date;
 private String message;
 
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public Date getDate() {
  return date;
 }
 public void setDate(Date date) {
  this.date = date;
 }
 public String getMessage() {
  return message;
 }
 public void setMessage(String message) {
  this.message = message;
 }
}

PublicPost.java
package org.krams.tutorial.domain;

import java.util.Date;

public class PublicPost {
 private String id;
 private Date date;
 private String message;
 
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public Date getDate() {
  return date;
 }
 public void setDate(Date date) {
  this.date = date;
 }
 public String getMessage() {
  return message;
 }
 public void setMessage(String message) {
  this.message = message;
 }
}

The Controllers

Next, we declare the controllers:
AdminController.java
PersonalController.java
PublicController.java

Here are the class declarations:

AdminController.java
package org.krams.tutorial.controller;

import org.apache.log4j.Logger;
import org.krams.tutorial.domain.AdminPost;
import org.krams.tutorial.service.AdminService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.annotation.Resource;

/**
 * Handles Admin-related requests
 */
@Controller
@RequestMapping("/admin")
public class AdminController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="adminService")
 private AdminService adminService;

    /**
     * Retrieves the Edit page
     */
    @RequestMapping(value = "/edit", method = RequestMethod.GET)
    public String getEditPage(Model model) {
     logger.debug("Received request to view edit page");
    
     // Call service. If true, we have appropriate authority
     if (adminService.edit(new AdminPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been edited successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }

     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Admin >> Edit");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
    
    /**
     * Retrieves the Add page
     */
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String getAddPage(Model model) {
     logger.debug("Received request to view add page");
    
     // Call service. If true, we have appropriate authority
     if (adminService.add(new AdminPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been added successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }
     
     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Admin >> Add");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
    
    /**
     * Retrieves the Delete page
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String getDeletePage(Model model) {
     logger.debug("Received request to view delete page");
    
     // Call service. If true, we have appropriate authority
     if (adminService.delete(new AdminPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been deleted successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }
     
     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Admin >> Delete");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
}

PersonalController.java
package org.krams.tutorial.controller;

import org.apache.log4j.Logger;
import org.krams.tutorial.domain.PersonalPost;
import org.krams.tutorial.service.PersonalService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.annotation.Resource;

/**
 * Handles Personal-related requests
 */
@Controller
@RequestMapping("/personal")
public class PersonalController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="personalService")
 private PersonalService personalService;
    
    /**
     * Retrieves the Edit page
     */
    @RequestMapping(value = "/edit", method = RequestMethod.GET)
    public String getEditPage(Model model) {
     logger.debug("Received request to view edit page");
    
     // Call service. If true, we have appropriate authority
     if (personalService.edit(new PersonalPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been edited successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }

     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Personal >> Edit");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
    
    /**
     * Retrieves the Add page
     */
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String getAddPage(Model model) {
     logger.debug("Received request to view add page");
    
     // Call service. If true, we have appropriate authority
     if (personalService.add(new PersonalPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been added successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }
     
     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Personal >> Add");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
    
    /**
     * Retrieves the Delete page
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String getDeletePage(Model model) {
     logger.debug("Received request to view delete page");
    
     // Call service. If true, we have appropriate authority
     if (personalService.delete(new PersonalPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been deleted successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }
     
     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Personal >> Delete");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
}

PublicController.java
package org.krams.tutorial.controller;

import org.apache.log4j.Logger;
import org.krams.tutorial.domain.PublicPost;
import org.krams.tutorial.service.PublicService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.annotation.Resource;

/**
 * Handles Public-related requests
 */
@Controller
@RequestMapping("/public")
public class PublicController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="publicService")
 private PublicService publicService;
    
    /**
     * Retrieves the Edit page
     */
    @RequestMapping(value = "/edit", method = RequestMethod.GET)
    public String getEditPage(Model model) {
     logger.debug("Received request to view edit page");
    
     // Call service. If true, we have appropriate authority
     if (publicService.edit(new PublicPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been edited successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }

     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Public >> Edit");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
    
    /**
     * Retrieves the Add page
     */
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String getAddPage(Model model) {
     logger.debug("Received request to view add page");
    
     // Call service. If true, we have appropriate authority
     if (publicService.add(new PublicPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been added successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }
     
     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Public >> Add");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
    
    /**
     * Retrieves the Delete page
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String getDeletePage(Model model) {
     logger.debug("Received request to view delete page");
    
     // Call service. If true, we have appropriate authority
     if (publicService.delete(new PublicPost()) == true) {
         // Add result to model
         model.addAttribute("result", "Entry has been deleted successfully!");
     } else {
         // Add result to model
         model.addAttribute("result", "You're not allowed to perform that action!");
     }
     
     // Add source to model to help us determine the source of the JSP page
     model.addAttribute("source", "Public >> Delete");
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/resultpage.jsp
     return "resultpage";
 }
}

We also declare a fourth controller that displays all records:

AllController.java
/**
 * 
 */
package org.krams.tutorial.controller;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.krams.tutorial.service.AdminService;
import org.krams.tutorial.service.PersonalService;
import org.krams.tutorial.service.PublicService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles authentication related requests
 */
@Controller
@RequestMapping("/all")
public class AllController {
        
 protected static Logger logger = Logger.getLogger("controller");

 @Resource(name="adminService")
 private AdminService adminService;
 
 @Resource(name="personalService")
 private PersonalService personalService;
 
 @Resource(name="publicService")
 private PublicService publicService;
 
 /**
  * Retrieves the View page. 
  * <p>
* This loads all authorized posts.
  */
    @RequestMapping(value = "/view", method = RequestMethod.GET)
    public String getViewAllPage(Model model) {
     logger.debug("Received request to view all page");
    
     // Retrieve items from service and add to model
     model.addAttribute("adminposts", adminService.getAll());
     model.addAttribute("personalposts", personalService.getAll());
     model.addAttribute("publicposts", publicService.getAll());
     
     // Add our current role and username
     model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities());
     model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
     
     // This will resolve to /WEB-INF/jsp/bulletinpage.jsp
     return "bulletinpage";
 }
}

The Services

Next, we declare the corresponding services:
AdminService.java
PersonService.java
PublicService.java
Here are the class declarations:

AdminService.java
package org.krams.tutorial.service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.krams.tutorial.domain.AdminPost;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service("adminService")
public class AdminService {

 private Map<String, AdminPost> adminPosts = new HashMap<String, AdminPost>();
 
 public AdminService() {
  // Initiliaze our in-memory HashMap list
  init();
 }

 // filterObject refers to the current object in the collection
 @PostFilter("hasPermission(filterObject, 'READ')")
 public List<adminpost> getAll() {
  // Iterate our HashMap list and convert it to an ArrayList
  List<adminpost> adminList = new ArrayList<adminpost>();
  for (String key: adminPosts.keySet()) {
   adminList.add(adminPosts.get(key));
  }
  // Return our new list
  return adminList;
 }
 
 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean add(AdminPost post)  {
  // This will return true if it's accessible
  return true;
 }
 
 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean edit(AdminPost post)  {
  // This will return true if it's accessible
  return true;
 }

 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean delete(AdminPost post)  {
  // This will return true if it's accessible
  return true;
 }
 
 // Initiliazes an in-memory HashMap list
 private void init() {
  // Create new post
  AdminPost post1 = new AdminPost();
  post1.setId(UUID.randomUUID().toString());
  post1.setDate(new Date());
  post1.setMessage("This is admin's post #1");
  
  // Create new post
  AdminPost post2 = new AdminPost();
  post2.setId(UUID.randomUUID().toString());
  post2.setDate(new Date());
  post2.setMessage("This is admin's post #2");
  
  // Create new post
  AdminPost post3 = new AdminPost();
  post3.setId(UUID.randomUUID().toString());
  post3.setDate(new Date());
  post3.setMessage("This is admin's post #3");
  
  // Add to adminPosts
  adminPosts.put(post1.getId(), post1);
  adminPosts.put(post2.getId(), post2);
  adminPosts.put(post3.getId(), post3);
 }
}

PersonalService.java
package org.krams.tutorial.service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.krams.tutorial.domain.PersonalPost;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service("personalService")
public class PersonalService {

 private Map<String, PersonalPost> personalPosts = new HashMap<String, PersonalPost>();
 
 public PersonalService() {
  // Initiliaze our in-memory HashMap list
  init();
 }

 // filterObject refers to the current object in the collection
 @PostFilter("hasPermission(filterObject, 'READ')")
 public List<personalpost> getAll() {
  // Iterate our HashMap list and convert it to an ArrayList
  List<personalpost> personalList = new ArrayList<personalpost>();
  for (String key: personalPosts.keySet()) {
   personalList.add(personalPosts.get(key));
  }
  // Return our new list
  return personalList;
 }
 
 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean add(PersonalPost post)  {
  // This will return true if it's accessible
  return true;
 }
 
 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean edit(PersonalPost post)  {
  // This will return true if it's accessible
  return true;
 }

 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean delete(PersonalPost post)  {
  // This will return true if it's accessible
  return true;
 }
 
 // Initiliazes an in-memory HashMap list
 private void init() {
  // Create new post
  PersonalPost post1 = new PersonalPost();
  post1.setId(UUID.randomUUID().toString());
  post1.setDate(new Date());
  post1.setMessage("This is personal's post #1");
  
  // Create new post
  PersonalPost post2 = new PersonalPost();
  post2.setId(UUID.randomUUID().toString());
  post2.setDate(new Date());
  post2.setMessage("This is personal's post #2");
  
  // Create new post
  PersonalPost post3 = new PersonalPost();
  post3.setId(UUID.randomUUID().toString());
  post3.setDate(new Date());
  post3.setMessage("This is personal's post #3");
  
  // Add to personalPosts
  personalPosts.put(post1.getId(), post1);
  personalPosts.put(post2.getId(), post2);
  personalPosts.put(post3.getId(), post3);
 }
}

PublicService.java
package org.krams.tutorial.service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.krams.tutorial.domain.PublicPost;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service("publicService")
public class PublicService {

 private Map<String, PublicPost> publicPosts = new HashMap<String, PublicPost>();
 
 public PublicService() {
  // Initiliaze our in-memory HashMap list
  init();
 }

 // filterObject refers to the current object in the collection
 @PostFilter("hasPermission(filterObject, 'READ')")
 public List<publicpost> getAll() {
  // Iterate our HashMap list and convert it to an ArrayList
  List<publicpost> publicList = new ArrayList<publicpost>();
  for (String key: publicPosts.keySet()) {
   publicList.add(publicPosts.get(key));
  }
  // Return our new list
  return publicList;
 }
 
 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean add(PublicPost post)  {
  // This will return true if it's accessible
  return true;
 }
 
 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean edit(PublicPost post)  {
  // This will return true if it's accessible
  return true;
 }

 @PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean delete(PublicPost post)  {
  // This will return true if it's accessible
  return true;
 }
 
 // Initiliazes an in-memory HashMap list
 private void init() {
  // Create new post
  PublicPost post1 = new PublicPost();
  post1.setId(UUID.randomUUID().toString());
  post1.setDate(new Date());
  post1.setMessage("This is public's post #1");
  
  // Create new post
  PublicPost post2 = new PublicPost();
  post2.setId(UUID.randomUUID().toString());
  post2.setDate(new Date());
  post2.setMessage("This is public's post #2");
  
  // Create new post
  PublicPost post3 = new PublicPost();
  post3.setId(UUID.randomUUID().toString());
  post3.setDate(new Date());
  post3.setMessage("This is public's post #3");
  
  // Add to publicPosts
  publicPosts.put(post1.getId(), post1);
  publicPosts.put(post2.getId(), post2);
  publicPosts.put(post3.getId(), post3);
 }
}

Observations

Notice the service methods have been annotated with @PreAuthorize and @PostFilter. Also, we're not actually performing any add, edit, or delete actions. Instead we're just returning a Boolean value to test whether the method is accessible.

The following declaration means check the domain object post and see if the current user has WRITE access to this object:
@PreAuthorize("hasPermission(#post, 'WRITE')")
 public Boolean add(PublicPost post)  {
  // This will return true if it's accessible
  return true;
 }

Whereas the following declaration means after returning, check the domain object post and see if the current user has READ access to this object:
@PostFilter("hasPermission(filterObject, 'READ')")
 public List<publicpost> getAll() {
  // Iterate our HashMap list and convert it to an ArrayList
  List<publicpost> publicList = new ArrayList<publicpost>();
  for (String key: publicPosts.keySet()) {
   publicList.add(publicPosts.get(key));
  }
  // Return our new list
  return publicList;
 }

Run the Application

Let's run the application to see the results (To see the remaining XML configuration, please download the source code below):

We'll need to login first. Enter the following URL to login:
http://localhost:8080/spring-security-acl-expression/krams/auth/login

To access the Bulletin Page, enter the following URL:
http://localhost:8080/spring-security-acl-expression/krams/all/view

We will log-in first as an admin using john/admin as the username/password pair.


Next, we'll login as a user using jane/user as the username/password pair.


Then, we'll login as a visitor using mike/visitor as the username/password pair.


Notice the admin can see all posts while the user can see only the Personal and Public posts; whereas the visitor can only see the Public posts. We've also indicated at the top the name of the current user and the associated role.

If any of the three tries to access an unauthorized resource for their role, they will get the following:


This is the benefit of ACL. We're restricting access on the domain level, not just on the URL level. However if we use the normal intercept-url setup, we won't be able to display all three types of posts. It's either we see everything or we get denied. Try clicking on the remaining links, and verify if they are really protected. I'll bet you they are :)

Conclusion

That's it. We've finished our simple ACL application that leverages Spring Security's Expression-Based Access Control. It may look complicated at first but the concepts are really simple. Also, instead of going the heavyweight solution, we opted to use the lightweight solution using the hasPermission() expression and a custom PermissionsEvaluator implementation

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-security-acl-expression/

You can download the project as a Maven build. Look for the spring-security-acl-expression.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run

If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring Security: Simple ACL using Expression-Based Access Control (Part 2) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

8 comments:

  1. Hi,
    Thanks for htis Nice artilce just to add while discussing about HashMap its worth mentioning following questions which frequently asked in Java interviews now days like How HashMap works in Java or How get() method of HashMap works in JAVA very often. on concept point of view these questions are great and expose the candidate if doesn't know deep details.

    Javin
    FIX Protocol tutorial

    ReplyDelete
  2. Thanks for this great tutorial.I was just wondering about how can this approach apply with accesscontrollist taglib. Should I impplement own aclServcie?

    ReplyDelete
  3. Is there a way to change user role(s) after he log-in ? without force the user to re-login?
    I have a scenario which you can become ADMIN(or any other role) for example , how to do it?

    Thanks ! great site

    ReplyDelete
  4. Hi,

    Thanks for your good tutorials.I run the application and not logged into the application if i am using the given password spring-security.xml.

    Like for username john and password 21232f297a57a5a743894a0e4a801fc3 . But the application will giving the message "You have entered an invalid username or password!". Please advise me how can we logged in the application.

    ReplyDelete
    Replies
    1. 21232f297a57a5a743894a0e4a801fc3 = admin

      Delete
  5. Hello,

    I have problem with getall when there are 1000 row de post. Spring call haspermission 1000 and the performance is very low. Do you know how to fix it?

    Thanks

    ReplyDelete
    Replies
    1. The tutorial is designed to demonstrate the feature. I did not consider the performance, and I wouldn't be surprised it slows. I would suggest investigating the full ACL feature of Spring Security. Try reading my blog about it.

      Delete
  6. Great tutorial, Thank you Mark!

    What is your idea of implementing Attribute-based access control with spring security? Spring security doesn't directly support ABAC.

    ReplyDelete