目录 一、简介与配置 二、实现登录注销操作 三、权限认证构成与鉴权方法 <— 你在这里 ( •̀ ω •́ )y 四、RBAC 结构实现
Sa-Token(三)权限认证构成与鉴权方法
[!NOTE]
博主前言:Spring Security 的学习真是惊掉我下巴,就一个鉴权功能,搞那么多复杂概念,又是过滤器链又是注入的,花了我一个星期左右,实际上还很不好用(未知错误给前端返回 401 是真绷不住)。Sa-Token 作为国产鉴权,性能更强的同时简化开发的程度相比 Spring Security 起码有十倍不止。最近我还注意到对标 Spring 生态的 Solon 框架,也是国产,性能提升3倍多,内存占用减少 50%+,打包还更小,真不知道这些工作为什么老外都做的这么复杂。
有了登录,接下来就要对接口配置拦截,进一步地还能设置权限。
在这之前,我们先来了解一下 Sa-Token 权限认证的构造。
一、权限类型简介 Sa-Token 的权限是经典的RBAC
结构,分为两种类型:
权限码:具体的某种权限,如新增 ,修改 等
角色:用户的抽象概念,或者说是权限码的集合,如管理员拥有所有权限 ,普通用户只拥有浏览和提交权限等
[!IMPORTANT]
实际上,Sa-Token 中的角色与权限码并没有强关联,为用户注入权限时,需要手动将二者均注入一遍
[!TIP]
关于什么是RBAC
结构,参考这篇文章:Spring Security(六)RBAC 结构实现
对于权限码 :为程序预先制定,可定义为常量 调用。
对于角色 :可由指定权限的用户添加/修改,则需定义数据表存储 。
二、三种鉴权方法 先了解鉴权方法是如何调用的,之后再实际上手。
[!IMPORTANT]
一般情况下,验证不通过时会抛出NotPermissionException
(权限码)和NotRoleException
(角色)这两种异常。
静态方法鉴权:
调用StpUtil
的静态方法即可实现鉴权,可以调用鉴权失败时返回false
而不抛出异常的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 StpUtil.getPermissionList(); StpUtil.hasPermission("user.add" ); StpUtil.checkPermission("user.add" ); StpUtil.checkPermissionAnd("user.add" , "user.delete" , "user.get" ); StpUtil.checkPermissionOr("user.add" , "user.delete" , "user.get" ); StpUtil.getRoleList(); StpUtil.hasRole("super-admin" ); StpUtil.checkRole("super-admin" ); StpUtil.checkRoleAnd("super-admin" , "shop-admin" ); StpUtil.checkRoleOr("super-admin" , "shop-admin" ); StpUtil.hasPermission("art.add" ); StpUtil.hasPermission("art.update" ); StpUtil.hasPermission("goods.add" ); StpUtil.hasPermission("art.delete" ); StpUtil.hasPermission("user.delete" ); StpUtil.hasPermission("user.update" ); StpUtil.hasPermission("index.js" ); StpUtil.hasPermission("index.css" ); StpUtil.hasPermission("index.html" );
注解鉴权:
我们可以使用注解在方法上标注,没有权限的用户调用该方法会抛出对应的权限异常。
[!WARNING]
不建议在Controller
层以外的方法使用注解鉴权,虽然 Sa-Token 提供了额外包实现其他位置的注解鉴权,但这样做会徒增设计复杂度,后续难以维护。
首先,在Web
层配置类注册 Sa-Token 拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Slf4j @Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { @Override public void addInterceptors (InterceptorRegistry registry) { log.info("注册 Sa-Token 拦截器" ); registry.addInterceptor(new SaInterceptor ()).addPathPatterns("/**" ); } }
然后就可以使用注解鉴权了,使用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 @SaCheckLogin @RequestMapping("info") public String info () { return "查询用户信息" ; } @SaCheckRole("super-admin") @RequestMapping("add") public String add () { return "用户增加" ; } @SaCheckPermission("user-add") @RequestMapping("add") public String add () { return "用户增加" ; } @SaCheckSafe() @RequestMapping("add") public String add () { return "用户增加" ; } @SaCheckHttpBasic(account = "sa:123456") @RequestMapping("add") public String add () { return "用户增加" ; } @SaCheckHttpDigest(value = "sa:123456") @RequestMapping("add") public String add () { return "用户增加" ; } @SaCheckDisable("comment") @RequestMapping("send") public String send () { return "查询用户信息" ; } @RequestMapping("atJurOr") @SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR) public SaResult atJurOr () { return SaResult.data("用户信息" ); } @RequestMapping("userAdd") @SaCheckPermission(value = "user.add", orRole = "admin") @SaCheckPermission(value = "user.add", orRole = {"admin", "manager", "staff"}) @SaCheckPermission(value = "user.add", orRole = {"admin, manager, staff"}) public SaResult userAdd () { return SaResult.data("用户信息" ); } @SaIgnore @RequestMapping("getList") public SaResult getList () { return SaResult.ok(); } @SaCheckOr( login = @SaCheckLogin, role = @SaCheckRole("admin"), permission = @SaCheckPermission("user.add"), safe = @SaCheckSafe("update-password"), httpBasic = @SaCheckHttpBasic(account = "sa:123456"), disable = @SaCheckDisable("submit-orders") ) @RequestMapping("test") public SaResult test () { return SaResult.ok(); } @SaCheckOr( login = { @SaCheckLogin(type = "login"), @SaCheckLogin(type = "user") } ) @RequestMapping("test") public SaResult test () { return SaResult.ok(); } @SaCheckLogin @SaCheckRole("admin") @SaCheckPermission("user.add") @RequestMapping("test") public SaResult test () { return SaResult.ok(); }
路由拦截鉴权:
实际开发中,我们常有登录拦截相关的需求,即项目中所有接口均需要登录认证,只有 “登录接口” 本身对外开放,在这个需求中我们真正需要的是一种基于路由拦截的鉴权模式。
我们在注解鉴权中注册了 Sa-Token 拦截器,而路由拦截鉴权正是在该拦截器上进行编辑配置的:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public void addInterceptors (InterceptorRegistry registry) { log.info("注册 Sa-Token 拦截器" ); registry.addInterceptor( new SaInterceptor (handle -> StpUtil.checkLogin())) .addPathPatterns("/**" ) .excludePathPatterns("/login" ); }
如此,即配置了最基础的登录路由拦截,除登录接口以外均需要登录才可访问。
读者亦可采用更复杂的配置,以此实现一些特殊的逻辑功能(如多端多权):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Configuration public class SaTokenConfigure implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new SaInterceptor (handler -> { SaRouter.match("/**" , "/user/doLogin" , r -> StpUtil.checkLogin()); SaRouter.match("/admin/**" , r -> StpUtil.checkRoleOr("admin" , "super-admin" )); SaRouter.match("/user/**" , r -> StpUtil.checkPermission("user" )); SaRouter.match("/admin/**" , r -> StpUtil.checkPermission("admin" )); SaRouter.match("/goods/**" , r -> StpUtil.checkPermission("goods" )); SaRouter.match("/orders/**" , r -> StpUtil.checkPermission("orders" )); SaRouter.match("/notice/**" , r -> StpUtil.checkPermission("notice" )); SaRouter.match("/comment/**" , r -> StpUtil.checkPermission("comment" )); SaRouter.match("/**" , r -> System.out.println("----啦啦啦----" )); SaRouter.match("/**" ).check(r -> System.out.println("----啦啦啦----" )); })).addPathPatterns("/**" ); } }