最近项目中使用到了新的权限框架,整理一下,做个记录,也做分享,如有错误,请指正。
文章目录
- sa-token介绍
- 功能及优势
- 登录认证
- 权限认证
- 鉴权
- 注解鉴权
- 注册拦截器
- 路由鉴权
- redis集成
sa-token介绍
以下是官网的介绍
Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。
功能及优势
多设备登录,单点登录,分布式session会话,微服务网关鉴权,记住我模式,二级认证,身份互换,账号封禁,多账号体系密码加密
相比于spring security,shiro,似乎是更简单了,shiro我个人几乎没有接触过,security之前有自己写过,相比之下,虽然security可能更适配spring,但security感觉略带繁琐,也可能是我接触的少,不过相比之下使用sa-token更快捷。因为我接入sa-token花费的时间更少。
登录认证
以下是官方示例代码,调用登录方法执行登录后,不会向前端发送token,因为StpUtil.login(id)
利用cookie自动注入的特新实现了,但严格来说,还是需要手动设置token
// 会话登录:参数填写要登录的账号id,建议的数据类型:long | int | String, 不可以传入复杂类型,如:User、Admin 等等
StpUtil.login(Object id);
StpUtil.login(123);
StpUtil.login("admin");
假设业务逻辑为(简化):
1.校验用户名,密码
2.校验通过执行登录操作
public R doLogin (adminLoginBo bo, String ip) {
if (bo.getUsername () == null) {
return R.fail ("参数不能为空");
}
//查询用户信息
HyAdminUser adminUser = adminUserDao.selectOne (new LambdaQueryWrapper<HyAdminUser> ().eq (HyAdminUser :: getUsername, bo.getUsername ()));
//校验密码
if (Objects.equals (adminUser.getPassword (), bo.getPassword)) {
//校验通过
//调用sa-token的登录方法
StpUtil.login (adminUser.getId ());
//获取token并返回
return R.ok (StpUtil.getTokenInfo ());
}
return R.fail ("登录失败");
}
return R.fail ("用户名或密码错误");
此时登录和使用数据库用户登录都以及实现了,注销登录同理。
权限认证
使用权限认证需要掌握RBAC技术,我使用的是粗粒度的权限控制,做的比较简单只控制到页面,所以没有设计权限码。
权限校验分为权限校验,角色校验
我使用的是基于角色校验,在role表中,admin为最高权限,user为普通用户权限,在数据库数据关联正确的情况下,可以通过以下方法查询当前用户角色实现代码鉴权,但是这里不需要鉴权,而是配合鉴权实现自动校验角色,并配置sa-token拦截器作出不同处理,将鉴权逻辑于业务逻辑分离。
// 获取:当前账号所拥有的角色集合
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");
鉴权
注解鉴权
-
@SaCheckLogin
: 登录校验 —— 只有登录之后才能进入该方法。 -
@SaCheckRole("admin")
: 角色校验 —— 必须具有指定角色标识才能进入该方法。 -
@SaCheckPermission("user:add")
: 权限校验 —— 必须具有指定权限才能进入该方法。 -
@SaCheckSafe
: 二级认证校验 —— 必须二级认证之后才能进入该方法。 -
@SaCheckBasic
: HttpBasic校验 —— 只有通过 Basic 认证后才能进入该方法。 -
@SaIgnore
:忽略校验 —— 表示被修饰的方法或类无需进行注解鉴权和路由拦截器鉴权。 -
@SaCheckDisable("comment")
:账号服务封禁校验 —— 校验当前账号指定服务是否被封禁。
注册拦截器
新建配置类SaTokenConfigure.java
,实现WebMvcConfigurer
,添加@Configuration
注解,保证此类被springboot
启动类扫描到
/*代表当前路径下,/**代表多级路径
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
}
然后就可以使用注解鉴权了,以下是官方案例
// 登录校验:只有登录之后才能进入该方法
@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 "用户增加";
}
// Http Basic 校验:只有通过 Basic 认证后才能进入该方法
@SaCheckBasic(account = "sa:123456")
@RequestMapping("add")
public String add() {
return "用户增加";
}
// 校验当前账号是否被封禁 comment 服务,如果已被封禁会抛出异常,无法进入方法
@SaCheckDisable("comment")
@RequestMapping("send")
public String send() {
return "查询用户信息";
}
路由鉴权
此时依旧需要对接口手动添加大量@SaCheckLogin
注解,依旧不方便,对配置类SaTokenConfigure.java
,按照sa-token的提示稍加修改,排除掉登录接口,匹配带有api的路径,并拦截,拦截后校验是否登录,即可实现除登录外全部接口的登录校验,代码如下:
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
registry.addInterceptor (new SaInterceptor (handler -> {
// 指定一条 match 规则
SaRouter
.match ("/api/**") // 拦截的 path 列表,可以写多个 */
.notMatch ("/api/login/doLogin") // 排除掉的 path 列表,可以写多个
.check (r -> StpUtil.checkLogin ()); // 要执行的校验动作,可以写完整的 lambda 表达式
})).addPathPatterns ("/**");
}
}
SaRouter.match() 匹配函数有两个参数:
- 参数一:要匹配的path路由。
- 参数二:要执行的校验函数。
在校验函数内不只可以使用 StpUtil.checkPermission("xxx")
进行权限校验,你还可以写任意代码
redis集成
sa-token默认将数据存储在内存中,速度快,避免了序列化和反序列化,但是也有缺点:
1.重启后数据会丢失
2.无法在分布式环境中共享数据
所以需要引入redis来做存储
除了需要引入redis依赖以外,还需要引入sa-token的两个依赖
提供redis实例化方案
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
集成redis方案
分为jdk默认序列化和jackson序列化方式两种,对应不同的依赖
jdk序列化依赖
<!-- Sa-Token 整合 Redis (使用 jdk 默认序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis</artifactId>
<version>1.37.0</version>
</dependency>
jackson序列化依赖
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.37.0</version>
</dependency>
ncy>
**jackson序列化依赖**
```xml
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.37.0</version>
</dependency>
引入redis依赖后只需要配置好redis的连接信息,sa-token可以自动实现登录数据保存。