Implementing Permission Management System Based On Spring Security

Slightly more complex background systems will involve user rights management. What are user rights? My understanding is that permission is the centralized management of data (the entity class of the system) and the operations that can be performed on the data (addition, deletion, search, and modification). To build a usable permission management system, three core classes are involved: one is the user User, the other is the role, and the last is the permission Permission. Next, this article will introduce how to build an interface-level permission management system step by step based on Spring Security 4.0.

  1. Related concepts
    Permission = Resource + Privilege
    Role = a set of low-level permissions
    User = collection of roles (high-level roles)
  2. Spring Security’s maven dependencies
    Although the Spring Boot version has reached 2.0, some pits were found when it was used before, so it is recommended to use the relatively stable version 1.5 for the time being.
  1. Define the permission set of the system
    Permissions are a collection of resources and the operations that can be performed on them. For our system, almost all entity classes can be regarded as a resource, and the common operations are adding, deleting, checking, and modifying four categories. Of course, according to our actual business needs, there may be other special operations, such as ours here Added an action to import users. Here is a brief list of two basic permission sets:

In the definition of permissions, the key is the key of resourceId and privileges, and the combination of the two will be used to judge the user’s permissions later. I use the form of resourceId-privilege here to uniquely represent an operation on a resource.

  1. Role-related operations
    The resource and operation permission collection class defines JsonPermissions:

The role class defines Role:

  1. Assign roles to users
    The Spring Security framework provides a basic user interface UserDetails, which provides basic user-related operations, such as obtaining username/password, whether the user account has expired, and whether user authentication has expired, etc. We need to implement this when we define our own User class. interface.
  1. Create initial roles and super administrators for the system
    If we impose access restrictions on all interfaces of the system, who will log in to the system as the initial user and create other users? Therefore, we need to define the initial role and initial user of the system, and automatically enter the initial role and initial user into the system when the system starts, and then use the initial user to log in to the system to create other business-related users. Define the super administrator role of the system: roles.json

Defines the initial admin user for the system: users.json

  1. Load system initialization role and user data
    When the system is deployed, the initialization roles and users of the system need to be automatically loaded into the database, so that they can be used for normal login. Use the @Component and @PostConstruct annotations to automatically import initial roles and users at system startup.
  1. Implement your own UserDetailsService
    Customize user information in UserDetailService, and set all Permissions related to user role role to Authorities of Authentication for PermissionEvaluator to judge user permissions. Note that the form of resourceId-privilege is used here for splicing and storage. My user information here is stored in the MongoDB database, or it can be replaced with other databases.

9.Config UserDetailsService

  1. The back-end interface implements access restrictions based on permissions
    Add the PreAuthorize annotation to the interface method that requires access restrictions. In this annotation, we can use a variety of verification methods, the more common ones are hasPermisson and hasRole. Similar to PreAuthorize, there is the PostAuthorize annotation. It is also better to understand from the literal meaning that PreAuthorize performs verification before accessing the interface, and PostAuthorize performs verification when the result is returned after accessing the interface.

By analogy, corresponding access restrictions can be added to the interfaces that need to restrict user access.

  1. Implement your own PermissionEvaluator
    After adding the PreAuthorize annotation to the interface method, you also need to implement your own PermissionEvaluator. Spring Security will verify the validity of the resource that the currently logged-in user is accessing and the operations performed on the resource in the hasPermission() method.
    Note that the targetDomainObject here is the resourceId we defined before, and the permission is the privilege. When verifying, it should be combined into a format consistent with the format stored in the UserDetailsService. Here we use – dash to connect.
  1. Annotation support
    After implementing PermissionEvaluator, the annotation of globalMethodSecurity must be added, otherwise the permission judgment added on the interface will not take effect. Add this annotation to the inherited class of SpringBootServletInitializer to enable method security.
  1. Access test: 403
    Since the user I am currently logged in has not set the role and access rights for it, I do not have access to the list interface, and the following 403 error occurs when I forcibly access it.
  1. Front-end pages implement personalized pages according to permissions
    It doesn’t end when the backend implements interface-level access restrictions. For the part of the user-visible interface, users with different roles should see different interfaces according to their roles when they log in to the system. Our current experience is that after the user logs in successfully, the user’s permission list is returned to the front-end, and then the front-end judges the permissions. If there is no permission, the corresponding button or function module is hidden. Through the combination of front and back ends, users can only see the operation interface and data within the scope of their permissions. At the same time, even if some users directly modify the interface parameters to obtain data, they will be judged twice at the back end. Make sure that users can see their own data and can only perform operations within the scope of their authority!