The so-called permission authentication, the core logic is to determine whether an account has the specified permissions:
If so, you will be allowed to pass.
No? Then access is prohibited!
Going deep into the underlying data, each account will have a set of permission codes, and the framework will verify whether this set contains the specified permission code.
For example: The current account has the permission code set ["user-add", "user-delete", "user-get"]
, then I will verify the permissions "user-update"
, the result is: verification failed, access to is prohibited.
So the core of the problem now is:
How to obtain the set of permission codes owned by an account?
Which permission code needs to be verified for this operation?
Next, we will introduce how to use Sa-Token to complete permission authentication operations in SpringBoot.
Sa-Token is a lightweight java permission authentication framework that mainly solves a series of permission-related issues such as login authentication, permission authentication, single sign-on, OAuth3, and microservice gateway authentication.
First introduce the Sa-Token dependency into the project:
<!-- Sa-Token 权限认证 --> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.34.0</version> </dependency>
Note: If you are using SpringBoot 3.x
, you only need to add sa -token-spring-boot-starter
can be changed to sa-token-spring-boot3-starter
.
Because each project has different needs and its permission design is also ever-changing, therefore [Get the current account permission code set] cannot be built into framework, so Sa-Token exposes this operation to you in the form of an interface, so that you can rewrite it according to your own business logic.
What you need to do is to create a new class and implement the StpInterface
interface, such as the following code:
/** * 自定义权限验证接口扩展 */ @Component // 保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展 public class StpInterfaceImpl implements StpInterface { /** * 返回一个账号所拥有的权限码集合 */ @Override public List<String> getPermissionList(Object loginId, String loginType) { // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限 List<String> list = new ArrayList<String>(); list.add("101"); list.add("user.add"); list.add("user.update"); list.add("user.get"); // list.add("user.delete"); list.add("art.*"); return list; } /** * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验) */ @Override public List<String> getRoleList(Object loginId, String loginType) { // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色 List<String> list = new ArrayList<String>(); list.add("admin"); list.add("super-admin"); return list; } }
Parameter explanation:
loginId: Account id, which is the identification value you write when calling StpUtil.login(id)
.
loginType: Account system identifier, which can be temporarily ignored here. This concept will be explained in detail under the [Multiple Account Authentication] chapter.
Note: The class must be annotated with @Component
to ensure that the component is scanned by Springboot and successfully injected into Sa-Token within the frame.
Startup class:
@SpringBootApplication public class SaTokenCaseApplication { public static void main(String[] args) { SpringApplication.run(SaTokenCaseApplication.class, args); System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig()); } }
Then you can use the following api to authenticate
// 获取:当前账号所拥有的权限集合 StpUtil.getPermissionList(); // 判断:当前账号是否含有指定权限, 返回 true 或 false StpUtil.hasPermission("user.add"); // 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException StpUtil.checkPermission("user.add"); // 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过] StpUtil.checkPermissionAnd("user.add", "user.delete", "user.get"); // 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] StpUtil.checkPermissionOr("user.add", "user.delete", "user.get");
Extension: NotPermissionException
The object can obtain the specific exception which StpLogic
is thrown through the getLoginType()
method
In Sa -In Token, roles and permissions can be independently verified
// 获取:当前账号所拥有的角色集合 StpUtil.getRoleList(); // 判断:当前账号是否拥有指定角色, 返回 true 或 false StpUtil.hasRole("super-admin"); // 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException StpUtil.checkRole("super-admin"); // 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] StpUtil.checkRoleAnd("super-admin", "shop-admin"); // 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] StpUtil.checkRoleOr("super-admin", "shop-admin");
Extension: NotRoleException
The object can be obtained through the getLoginType()
method StpLogic
Throwing exceptions
Some students want to ask, if authentication fails and an exception is thrown, then what? Do you want to display exceptions to the user? Of course not!
You can create a global exception interceptor to unify the format returned to the front end. Reference:
@RestControllerAdvice public class GlobalExceptionHandler { // 全局异常拦截 @ExceptionHandler public SaResult handlerException(Exception e) { e.printStackTrace(); return SaResult.error(e.getMessage()); } }
Sa-Token allows you to Wildcards specify general permissions, for example, when an account has the permissions of art.*
, art.add
, art.delete
, art.update
will be matched by
// 当拥有 art.* 权限时 StpUtil.hasPermission("art.add"); // true StpUtil.hasPermission("art.update"); // true StpUtil.hasPermission("goods.add"); // false // 当拥有 *.delete 权限时 StpUtil.hasPermission("art.delete"); // true StpUtil.hasPermission("user.delete"); // true StpUtil.hasPermission("user.update"); // false // 当拥有 *.js 权限时 StpUtil.hasPermission("index.js"); // true StpUtil.hasPermission("index.css"); // false StpUtil.hasPermission("index.html"); // false
God permissions: When an account has
"*"
permissions, he can authenticate through any permission code (role Same for authentication)
Permissions accurate to the button level means: Permission scope can control whether each button on the page displays .
Idea: Such precise range control is difficult to achieve by relying only on the backend. At this time, the frontend needs to make certain logical judgments.
If it is an integrated front-end and back-end project, you can refer to: Thymeleaf tag dialect. If it is a front-end and back-end separated project, then:
When logging in, the current account has All permission codes are returned to the front end at once.
The front end saves the permission code collection in localStorage
or other global state management objects.
On buttons that require permission control, use js to make logical judgments. For example, in the Vue
framework we can use the following writing:
<button v-if="arr.indexOf('user.delete') > -1">删除按钮</button>
Among them: arr
is the permission code array owned by the current user, user.delete
is the permission code required to display the button, Delete button
It is content that users can only see if they have the permission code.
Note: The above writing method is only to provide a reference example. Different frameworks have different writing methods. You can flexibly encapsulate and call according to the project technology stack.
need!
前端的鉴权只是一个辅助功能,对于专业人员这些限制都是可以轻松绕过的,为保证服务器安全,无论前端是否进行了权限校验,后端接口都需要对会话请求再次进行权限校验!
新建 JurAuthController
,复制以下代码
package com.pj.cases.use; import java.util.List; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; /** * Sa-Token 权限认证示例 * * @author kong * @since 2022-10-13 */ @RestController @RequestMapping("/jur/") public class JurAuthController { /* * 前提1:首先调用登录接口进行登录,代码在 com.pj.cases.use.LoginAuthController 中有详细解释,此处不再赘述 * ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456 * * 前提2:项目实现 StpInterface 接口,代码在 com.pj.satoken.StpInterfaceImpl * Sa-Token 将从此实现类获取 每个账号拥有哪些权限。 * * 然后我们就可以使用以下示例中的代码进行鉴权了 */ // 查询权限 ---- http://localhost:8081/jur/getPermission @RequestMapping("getPermission") public SaResult getPermission() { // 查询权限信息 ,如果当前会话未登录,会返回一个空集合 List<String> permissionList = StpUtil.getPermissionList(); System.out.println("当前登录账号拥有的所有权限:" + permissionList); // 查询角色信息 ,如果当前会话未登录,会返回一个空集合 List<String> roleList = StpUtil.getRoleList(); System.out.println("当前登录账号拥有的所有角色:" + roleList); // 返回给前端 return SaResult.ok() .set("roleList", roleList) .set("permissionList", permissionList); } // 权限校验 ---- http://localhost:8081/jur/checkPermission @RequestMapping("checkPermission") public SaResult checkPermission() { // 判断:当前账号是否拥有一个权限,返回 true 或 false // 如果当前账号未登录,则永远返回 false StpUtil.hasPermission("user.add"); StpUtil.hasPermissionAnd("user.add", "user.delete", "user.get"); // 指定多个,必须全部拥有才会返回 true StpUtil.hasPermissionOr("user.add", "user.delete", "user.get"); // 指定多个,只要拥有一个就会返回 true // 校验:当前账号是否拥有一个权限,校验不通过时会抛出 `NotPermissionException` 异常 // 如果当前账号未登录,则永远校验失败 StpUtil.checkPermission("user.add"); StpUtil.checkPermissionAnd("user.add", "user.delete", "user.get"); // 指定多个,必须全部拥有才会校验通过 StpUtil.checkPermissionOr("user.add", "user.delete", "user.get"); // 指定多个,只要拥有一个就会校验通过 return SaResult.ok(); } // 角色校验 ---- http://localhost:8081/jur/checkRole @RequestMapping("checkRole") public SaResult checkRole() { // 判断:当前账号是否拥有一个角色,返回 true 或 false // 如果当前账号未登录,则永远返回 false StpUtil.hasRole("admin"); StpUtil.hasRoleAnd("admin", "ceo", "cfo"); // 指定多个,必须全部拥有才会返回 true StpUtil.hasRoleOr("admin", "ceo", "cfo"); // 指定多个,只要拥有一个就会返回 true // 校验:当前账号是否拥有一个角色,校验不通过时会抛出 `NotRoleException` 异常 // 如果当前账号未登录,则永远校验失败 StpUtil.checkRole("admin"); StpUtil.checkRoleAnd("admin", "ceo", "cfo"); // 指定多个,必须全部拥有才会校验通过 StpUtil.checkRoleOr("admin", "ceo", "cfo"); // 指定多个,只要拥有一个就会校验通过 return SaResult.ok(); } // 权限通配符 ---- http://localhost:8081/jur/wildcardPermission @RequestMapping("wildcardPermission") public SaResult wildcardPermission() { // 前提条件:在 StpInterface 实现类中,为账号返回了 "art.*" 泛权限 StpUtil.hasPermission("art.add"); // 返回 true StpUtil.hasPermission("art.delete"); // 返回 true StpUtil.hasPermission("goods.add"); // 返回 false,因为前缀不符合 // * 符合可以出现在任意位置,比如权限码的开头,当账号拥有 "*.delete" 时 StpUtil.hasPermission("goods.add"); // false StpUtil.hasPermission("goods.delete"); // true StpUtil.hasPermission("art.delete"); // true // 也可以出现在权限码的中间,比如当账号拥有 "shop.*.user" 时 StpUtil.hasPermission("shop.add.user"); // true StpUtil.hasPermission("shop.delete.user"); // true StpUtil.hasPermission("shop.delete.goods"); // false,因为后缀不符合 // 注意点: // 1、上帝权限:当一个账号拥有 "*" 权限时,他可以验证通过任何权限码 // 2、角色校验也可以加 * ,指定泛角色,例如: "*.admin",暂不赘述 return SaResult.ok(); } }
代码注释已针对每一步操作做出详细解释,大家可根据可参照注释中的访问链接进行逐步测试。
The above is the detailed content of How SpringBoot uses Sa-Token to implement permission authentication. For more information, please follow other related articles on the PHP Chinese website!