The background that

Recently, I started to contact a little console application when I wrote the code. The project I contacted was very old, and all the permission management felt not very handy.

So I thought if I could design one from scratch to sort out my thoughts, and of course it doesn’t matter whether I actually use it or not.

Authority management is mainly for security, the project authority management is all put in the front control, feel this point is very insecure.

Front end against gentleman, not against villain.

Of course, this time the wheel is mainly to create a satisfactory permission control framework, so the design adopts MVP mode and adopts a progressive way of development.

You can learn the design and implementation of permission control together.

If production wants to use directly, the current mature framework is also recommended:

spring-security

Houbb. Making. IO / 2017/12/19 /…

shiro

Houbb. Making. IO / 2016/08/11 /…

The basics of permission design

Before we write the code, let’s learn a little about the basics of permission design.

Authority design development so far, the online information is still relatively rich, we excerpted some of the more classic content.

User-permission model

For permission management, the simplest idea would be to assign different permissions to each user.

However, this design has a relatively big problem is not flexible enough, when there are many users, it is difficult to maintain.

RBAC0 model

Therefore, many people think of adding a role between users and permissions.

Role-based Access Control (ROLE-based Access Control) is also the most popular permission design model so far.

There are three main concepts: users, roles, and permissions.

The user is the one we need to control.

The character is the bridge, which is the most clever part of the model. Just like each of us, we are children of our parents, parents of our children, employees of our company, and we play different roles at different times. Different roles have different permissions.

Authority is a relatively broad concept. For the console, it may be menu authority, resource authority, and operation authority of data addition, deletion, change and check.

When designing a database table, there are three real tables:

The user the users table

Role role table

Privilege permission table

Then two mapping tables:

User_role Indicates the user – role relationship

Role_privilege Role – permission relationship table

This is the core design and model analysis of RBAC, also known as RBAC0, which provides extended patterns based on the core concepts.

Including RBAC1,RBAC2,RBAC3 models.

These three types are described below

RBAC1 model

The concept of Hierarchical Role is mainly introduced here.

This design can group and layer roles, which simplifies permission management to a certain extent.

RBAC2 model

On the basis of the core model, the role constraint control is carried out. The RBAC2 model adds the separation of responsibilities, which specifies the mandatory rules that should be followed when the rights are assigned to the role or the role is assigned to the user, and when the user activates a role at a certain moment.

In practice, however, this model is not used much. I’m not going to expand it here.

RBAC3 model

The most comprehensive model, but also the most commonly used by companies.

We usually see authority management, and the company’s organizational structure is generally one-to-one correspondence.

That is, models come from life.

User groups

One of the most common concepts is user groups.

In gitLab, for example, join a project team and then work on the repository.

It’s like joining a project team in a real job, the same thing. Users here are generally horizontal.

Generally, there is a relationship between superiors and subordinates in a company. The organizational chart of a company is as follows:

The advantage of this architecture is that it is easy to change and adjust permissions, and also easy to control permissions.

When we design a table, we can add a user_group table to put some users in the same group.

position

Positions actually correspond to the concept of roles, and different positions have different permissions, even in the same project.

It’s like the difference between project lead and normal development.

The permissions model

The final model would look something like this:

Authorization process

How does this work by giving users roles?

Manual authorization

The most common is manual design.

When the project is initialized, there are some basic roles and administrators.

Then the administrator configures related permissions.

The examination and approval authorization

A user can apply for a role and be assigned a role through the approval process.

For example, we can sometimes request access to a resource for a similar reason.

In fact, so far, it’s just the basics.

If you want to design a good menu, there are many things to consider, such as menu design, page design, how to design more reasonable flow? Can I customize roles? Can users customize menus?

And so on. We won’t go into that for now, but we’ll focus on implementing a simple version of the permission control framework ourselves.

The shortcomings of traditional RBAC

Of course, the above model still has some shortcomings.

As one of the papers I read said:

Although RBAC has been widely used, the traditional RBAC model still has shortcomings, mainly in the following two aspects :(1) access control can not meet the needs of practical applications, and Web application systems need more fine-grained access control. (2) The model only defines the internal mechanism of access control, and does not propose a simple and friendly way to implement access control. For Web system users, friendly and intuitive user interface is an essential part of the system.

The solution is to design an opcode, and then identify through a string such as: 10000 can represent the accessible menu, there is no increase, deletion, change and check permission.

Of course, these need to be combined with our own business to design.

We assume that the design of the product design is complete, the database is also designed to complete the case, how to achieve flexible access control in coding?

Design ideas

The basic idea is something like Shiro’s.

You can specify roles or permissions to access fixed menus.

Design goals

(1) Of course, we hope to be more flexible and specific to a fixed method rather than a fixed Controller request.

(2) it is easy to write and can be specified by annotations in the later stage

(3) From simple to complex, only the control based on permission code is realized in the initial stage, and role-based control is not considered temporarily. Because each login user can obtain the corresponding role and permission code.

The whole process

  1. User page request

  2. Obtain user login information. Distributed systems can obtain all permissions of the current user based on Redis Session or JWT, etc

  3. Verify that the user has request permission

  4. Returns the corresponding page result

Step 1 – Interface design

The core interface

public interface IPrivilege {

    /** * Have permissions ** {@codeTrue} has * {@codeFalse} Does not own * *@paramContext *@returnWhether *@since0.0.1 * /
    boolean hasPrivilege(final IPrivilegeContext context);

}
Copy the code

This is the core of everything, all we care about is whether we have permissions, and the result is a Boolean.

So what’s the content of context?

public interface IPrivilegeContext {

    /** * Has permission context *@returnWith *@since0.0.1 * /
    IPrivilegeOwn own(a);

    /** * Required permission context *@returnWith *@since0.0.1 * /
    IPrivilegeAcquire acquire(a);

}
Copy the code

There are actually two interfaces, one for the permissions you have and the other for the permissions you need.

Permission information

The contents of the two permissions are as follows:

  • Permissions owned
public interface IPrivilegeOwn {

    /** * List of permissions *@returnEncoding list@since0.0.1 * /
    List<IPrivilegeInfo> ownPrivilege(a);

}
Copy the code
  • Required permissions
public interface IPrivilegeAcquire {

    /** * List of required permissions *@returnEncoding list@since0.0.1 * /
    List<IPrivilegeInfo> acquirePrivilege(a);

}
Copy the code

Step 2 – Programmatic implementation

Maven is introduced into

<dependency>
    <group>com.github.houbb</group>
    <artifact>privilege-core</artifact>
    <version>${latest version}</version>
</dependency>
Copy the code

User-defined permissions

Implement the IPrivilegeOwn interface.

Can query databases, files and so on.

public class PrivilegeOwnOne implements IPrivilegeOwn {
    @Override
    public List<IPrivilegeInfo> ownPrivilege(a) {
        IPrivilegeInfo info = PrivilegeInfo.newInstance().code("001");
        returnCollections.singletonList(info); }}Copy the code

Customize required permissions

public class PrivilegeAcquireTwo implements IPrivilegeAcquire {

    @Override
    public List<IPrivilegeInfo> acquirePrivilege(a) {
        IPrivilegeInfo one = PrivilegeInfo.newInstance().code("001");
        IPrivilegeInfo two = PrivilegeInfo.newInstance().code("002");
        returnArrays.asList(one, two); }}Copy the code

Test 1

IPrivilegeOwn own = new PrivilegeOwnOne();
IPrivilegeAcquire acquire = new PrivilegeAcquireTwo();
boolean hasPrivilege = PrivilegeBs.newInstance()
        .own(own)
        .acquire(acquire)
        .hasPrivilege();

Assert.assertFalse(hasPrivilege);
Copy the code

Here we get no permissions, because the default policy is to own all of the specified encodings.

Built-in strategy

The built-in policies can be accessed directly through Privileges.

strategy instructions
all() Only when all required permission codes have been obtained, the permission is considered to have been obtained. (Default policy)
any() If you have any of the required permission codes, you are considered to have the permission.
allow() Whitelist, directly return to have permission.
deny() Blacklist, return directly do not have permission.

Test 2

We specify that if you have one, you can pass

IPrivilegeOwn own = new PrivilegeOwnOne();
IPrivilegeAcquire acquire = new PrivilegeAcquireTwo();
IPrivilege privilege = Privileges.any();

boolean hasPrivilege = PrivilegeBs.newInstance()
        .own(own)
        .acquire(acquire)
        .privilege(privilege)
        .hasPrivilege();

Assert.assertTrue(hasPrivilege);
Copy the code

Step 3 – Annotated programming

The need for annotations

Of course, there is a basic implementation above, but the actual use is definitely not something the developer can specify manually.

That’s a lot of trouble.

Narrative we design can be implemented based on annotations.

Annotations to define

@Inherited
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PrivilegeAcquire {

    /** * Permission code *@returnCode *@since0.0.4 * /
    String[] code() default {};

}
Copy the code

This annotation can be placed on a method to specify the encoding required for access.

Maven is introduced into

Most of our development is done in spring-MVC projects.

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>privilege-mvc</artifactId>
    <version>${latest version}</version>
</dependency>
Copy the code

Custom interceptors

We define our own interceptor that handles permissions for logged-in users.

@Component
public class MyPrivilegeInterceptor extends AbstractPrivilegeInterceptor {

    @Override
    protected void fillSubject(ISubject subject, HttpServletRequest request) {
        String id = request.getParameter("id");
        if("admin".equals(id)) {
            subject.privileges("1001"); }}}Copy the code

We set the permission code to 1001 only for the admin user and do not handle other users.

Register interceptors

Register our interceptor, which will take effect when accessing the /hello address. Of course, it can be adjusted according to the actual situation.

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private MyPrivilegeInterceptor myPrivilegeInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myPrivilegeInterceptor)
            .addPathPatterns("/hello"); }}Copy the code

Business code

Controller

When we specify access to the current method through the @privilegeacquire annotation, the permission code 1001 is required

@Controller
public class HelloController {

    @RequestMapping("/hello")
    @ResponseBody
    @PrivilegeAcquire(code = "1001")
    public String hello(a) {
        return "hello"; }}Copy the code

Application

The startup class is defined as follows:

We start the privilege verification with the @enablePrivilege annotation.

@SpringBootApplication
@EnablePrivilege
public class Application {

    public static void main(String[] args) { SpringApplication.run(Application.class, args); }}Copy the code

test

http://localhost:8080/hello error page access;

There was an unexpected error (type=Internal Server Error, status=500).
Has no privilege! Acquire: <[1001]>, Own: <[]>
Copy the code

Page to http://localhost:8080/hello? Id =admin Normal access.

summary

At this point, the simplest permission design is done.

In fact, when designed, the project is very multi-modular in order to accommodate various scenarios:

(1) API core interface and annotation definition

(2) Basic implementation of core programming, which can exist without Spring

(3) Proxy A proxy that can be used directly without Spring

(4) Spring combined with Spring AOP mode

(5) MVC combined with Spring-MVC is also the most commonly used mode

Further optimizations will be considered to add support for SpringBoot.

Consider also taking a leaf out of Shiro’s book and introducing more features.

Open source address

Github.com/houbb/privi…

Welcome to fork/star ~ ~

vision

Frameworks can support more features, such as role-based, user-based.

Out-of-the-box designs, such as the basic SQL + Mybatis generic permission management framework, are similar to Quartz.

A complete project design, support front and back pages.

Separate service polishing, can provide external authentication services, combined with AuTH, further optimize permission management.