Demonstration of using Solon Auth authentication framework (simpler authentication framework)

Posted by Kemik on Sun, 30 Jan 2022 20:07:47 +0100

Recently, I have seen several authentication frameworks, such as Apache Shiro, SA token, Spring Security... In particular, Spring Security, as the framework of spring boot & cloud, Solon should have its own biological security authentication framework. Therefore, after adapting SA token (satoken solo plugin) and sureness (sureness solo plugin), Solon Auth (solon.extend.auth), Solon's own son, was also developed. The design goal is to be simpler and more direct, and provide users with richer and different authentication framework choices.

Solon Auth (solon.extend.auth)

Coupon network https://www.cps3.cn/

Solon Auth is positioned to do authentication control only. Focus on the adaptation of the verification results and the unified control and Application on this basis. There will be fewer functions, but it will not faint when adapted.

Solon Auth supports two schemes, rule control and annotation control, which have their own advantages and disadvantages and can also be used in combination:

  • Rule control is suitable for overall macro control in one place
  • Annotation control is convenient to accurately grasp the details

1, Start adaptation and complete 2 steps

  • Step 1: build an authentication adapter
@Configuration
public class Config {
    @Bean
    public AuthAdapter init() {
        //
        // Build adapter
        //
        return new AuthAdapter()
                .loginUrl("/login") //Set the login address and automatically jump when not logged in (if not set, 401 error will be output)
                .addRule(r -> r.include("**").verifyIp().failure((c, t) -> c.output("Yours IP Not on the white list"))) //Add rule
                .addRule(b -> b.exclude("/login**").exclude("/run/**").verifyPath()) //Add rule
                .processor(new AuthProcessorImpl()) //Set authentication processor
                .failure((ctx, rst) -> { //Set default validation failure handling
                    ctx.render(rst);
                });
    }
}

//Rule configuration description
//1.include(path) the path range of the rule package, which can be multiple
//2. The path pool circumference sorted by the exclude (path) rule can be more than one
//3.failure(..)    Handling after rule loss
//4.verifyIp()...  Verification scheme to be made by the rule (multiple different verification schemes can be used)

  • Step 2, implement an authentication processor

Let's first understand the interface of AuthProcessor, which is connected with a series of verification action results. Users may have to do more work themselves, but it's intuitive.

//Authentication processor
public class AuthProcessorImpl implements AuthProcessor {

    @Override
    public boolean verifyIp(String ip) {
        //Verify the IP address and whether you have access
    }

    @Override
    public boolean verifyLogined() {
        //Verify the login status and whether the user has logged in
    }

    @Override
    public boolean verifyPath(String path, String method) {
        //Authentication path, user accessible
    }

    @Override
    public boolean verifyPermissions(String[] permissions, Logical logical) {
        //Verify specific permissions. Users have limited permissions
    }

    @Override
    public boolean verifyRoles(String[] roles, Logical logical) {
        //Verify whether the user has a specific role
    }
}

Now, for an adaptation practice, a production environment code is used:

public class AuthProcessorImpl implements AuthProcessor {
    private int puid() {
        return Context.current().session("puid", 0);
    }

    @Override
    public boolean verifyIp(String ip) {
        return true; //The ip address is unlimited and returns true directly
    }

    @Override
    public boolean verifyLogined() {
        return puid() > 0; //If the user id is greater than 0, it indicates that you have logged in
    }

    @Override
    public boolean verifyPath(String path, String method) {
        try {
            if (BcfClient.hasUrlpath(path)) {
                return BcfClient.hasUrlpathByUser(puid(), path);
            } else {
                return true;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean verifyPermissions(String[] permissions, Logical logical) {
        int puid = puid();

        try {
            if (logical == Logical.AND) {
                boolean isOk = true;

                for (String p : permissions) {
                    isOk = isOk && BcfClient.hasResourceByUser(puid, p);
                }

                return isOk;
            } else {
                for (String p : permissions) {
                    if (BcfClient.hasResourceByUser(puid, p)) {
                        return true;
                    }
                }
                return false;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean verifyRoles(String[] roles, Logical logical) {
        int puid = puid();

        try {
            if (logical == Logical.AND) {
                boolean isOk = true;

                for (String p : roles) {
                    isOk = isOk && BcfClient.isUserInGroup(puid, p);
                }

                return isOk;
            } else {
                for (String p : roles) {
                    if (BcfClient.isUserInGroup(puid, p)) {
                        return true;
                    }
                }
                return false;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

2, 2 application modes (generally combined)

Just now we have adapted it, and now we can apply it.

  • The first is to directly configure all or part of the rules in the AuthAdapter (or not)
//Refer to the adapter addRule(...) above

The advantage of configuration is that there is no need to invade business code; At the same time, in a unified place, the macro can be seen; But it's easy to ignore the details.

  • The second type is based on annotation (generally used for specific permissions or specific roles)
@Mapping("/rock/agroup")
@Controller
public class AgroupController {
    @Mapping("")
    public void home() {
        //agroup home page
    }

    @Mapping("inner")
    public void inner() {
        //Internal list page
    }

    
    @AuthPermissions("agroup:edit") //Specific permissions are required
    @Mapping("edit/{id}")
    public void edit(int id) {
        //Edit permission is required to edit the display page
    }

    @AuthRoles("admin")  //Specific roles required
    @Mapping("edit/{id}/ajax/save")
    public void save(int id) {
        //Editing processing interface requires administrator permission
    }
}

The advantage of annotation is that it can be seen microscopically. You can see what permissions or roles it needs in a method, which is not easy to ignore.

  • Combined use mode

Generally, use configuration rules to control all addresses that need to be logged in; Use annotations to control specific permissions or roles.

3, Source code of this case

https://gitee.com/noear/solon_demo/tree/master/demo16.solon_auth

4, Application of other production projects

https://gitee.com/noear/water/tree/master/wateradmin

https://gitee.com/noear/sponge/tree/main/spongeadmin

Attachment: Solon project address

  • Gitee: https://gitee.com/noear/solon
  • Github: https://github.com/noear/solon

Attachment: other introductory examples of Solon

  • Solon Getting Started tutorial example: https://gitee.com/noear/solon_demo
  • Solon Rpc Getting Started tutorial example: https://gitee.com/noear/solon_rpc_demo
  • Solon Cloud Getting Started tutorial example: https://gitee.com/noear/solon_cloud_demo
  • Solon advanced tutorial example: https://gitee.com/noear/solon_advance_demo