写在 前面 本文讲述Sa-token一些常用功能
Sa-Token 是个 轻量化 并且 配置简单 的用户登录鉴权框架
技术佬看文档 不多听我bb
简介
Sa-Token 地址::sa-token.cc
下面项目演示地址 :github.com/lizhe-0423/…
登录
sa-token 登录的核心语句 只需要一句化 非常简单
// 会话登录:参数填写要登录的账号id,建议的数据类型:long | int | String, 不可以传入复杂类型,如:User、Admin 等等
StpUtil.login(Object id);
但是 我们在实际开发中肯定不仅仅如此使用
所以 一般情况下 如下:
// 会话登录接口
@RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
// 第一步:比对前端提交的账号名称、密码
if("zhang".equals(name) && "123456".equals(pwd)) {
// 第二步:根据账号id,进行登录
StpUtil.login(10001);
return SaResult.ok("登录成功");
}
return SaResult.error("登录失败");
}
真实代码:
/**
* 用户登录
*
* @param userLoginRequest
* @param
* @return
*/
@PostMapping("/login")
public BaseResponse<LoginUserVO> userLogin(@RequestBody UserLoginRequest userLoginRequest) {
if (userLoginRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String userAccount = userLoginRequest.getUserAccount();
String userPassword = userLoginRequest.getUserPassword();
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
LoginUserVO loginUserVO = userService.userLogin(userAccount, userPassword);
return ResultUtils.success(loginUserVO);
}
首先 我们看到 在这段代码中 我们接收 UserLoginRequest 这是一个DTO(指前端向后端传递的数据)
然后 执行 userService.userLogin
在userService 中 :
@Override
public LoginUserVO userLogin(String userAccount, String userPassword) {
// 1. 校验
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
}
if (userAccount.length() < 4) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
}
if (userPassword.length() < 8) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
}
// 2. 加密
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
// 查询用户是否存在
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userAccount", userAccount);
queryWrapper.eq("userPassword", encryptPassword);
User user = this.baseMapper.selectOne(queryWrapper);
// 用户不存在
if (user == null) {
log.info("user login failed, userAccount cannot match userPassword");
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
}
// 登录
String userId = String.valueOf(user.getId());
String substring = userId.substring(11);
int newUserID = Integer.parseInt(substring);
StpUtil.login(newUserID);
// 记录状态
StpUtil.getSession().set(USER_LOGIN_STATE, user);
return this.getLoginUserVO(user);
}
StpUtil.login(newUserID) (括号中 是 传入的一个 id 不传也可以)
这里 因为 我数据库 id过长 所以截取了一下 算是投机的一种小方式
StpUtil.getSession().set(USER_LOGIN_STATE, user) 是sa-token 记录session会话的语句
可看下面 session会话部分
会话查询
查询
// 获取当前会话账号id, 如果未登录,则抛出异常:`NotLoginException`
StpUtil.getLoginId();
// 类似查询API还有:
StpUtil.getLoginIdAsString(); // 获取当前会话账号id, 并转化为`String`类型
StpUtil.getLoginIdAsInt(); // 获取当前会话账号id, 并转化为`int`类型
StpUtil.getLoginIdAsLong(); // 获取当前会话账号id, 并转化为`long`类型
// ---------- 指定未登录情形下返回的默认值 ----------
// 获取当前会话账号id, 如果未登录,则返回null
StpUtil.getLoginIdDefaultNull();
// 获取当前会话账号id, 如果未登录,则返回默认值 (`defaultValue`可以为任意类型)
StpUtil.getLoginId(T defaultValue);
session 会话
// 在登录时缓存user对象
StpUtil.getSession().set("user", user);
// 然后我们就可以在任意处使用这个user对象
SysUser user = (SysUser) StpUtil.getSession().get("user");
我们先思考一下 session 指会话 那么如果项目重启的话我们session 是不是就消失了呢?所以我们就需要引入redis
session 获取 对象 的应用场景有限 大多数情况 我们可以使用 鉴权 等方式处理
比如
1.获取当前用户的某个字段信息 符不符合什么标准?
可以通过get session 方式 获取到用户信息
2.比如删除某条消息是不是管理员的时候
如果不鉴权 就是getsession 获取user信息 然后查数据库 判断该user 的角色 是不是 管理员
Redis
引入
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.34.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
对了 别忘记 去application 中配置redis相关信息
# redis配置
redis:
# Redis数据库索引(默认为0)
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
# password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
引入后 session 相关信息 不需要修改 此时 我们存取的信息就自动存放在 redis中了
鉴权
注解鉴权
首先 注册拦截器
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
}
注解鉴权 —— 优雅的将鉴权与业务代码分离!
- @SaCheckLogin: 登录校验 —— 只有登录之后才能进入该方法。
- @SaCheckRole("admin"): 角色校验 —— 必须具有指定角色标识才能进入该方法。
- @SaCheckPermission("user:add"): 权限校验 —— 必须具有指定权限才能进入该方法。
- @SaCheckSafe: 二级认证校验 —— 必须二级认证之后才能进入该方法。
- @SaCheckBasic: HttpBasic校验 —— 只有通过 Basic 认证后才能进入该方法。
- @SaIgnore:忽略校验 —— 表示被修饰的方法或类无需进行注解鉴权和路由拦截器鉴权。
- @SaCheckDisable("comment"):账号服务封禁校验 —— 校验当前账号指定服务是否被封禁。
Sa-Token 使用全局拦截器完成注解鉴权功能,为了不为项目带来不必要的性能负担,拦截器默认处于关闭状态
因此,为了使用注解鉴权,你必须手动将 Sa-Token 的全局拦截器注册到你项目中
然后 我们就可以使用!!!
权限认证
首先 我们必须 新建一个 自定义的权限接口
/**
* 自定义权限验证接口扩展
*/
@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;
}
}
上面 是官网 代码
因为 我们在实际中 一般使用角色 所以我们就用角色鉴权就好了!!!
@Override
public List<String> getRoleList(Object loginId, String loginType) {
//
User user = (User) StpUtil.getSession().get(USER_LOGIN_STATE);
val userRole = user.getUserRole();
if(userRole.equals("admin")){
List<String> list = new ArrayList<String>();
list.add("admin");
return list;
}
if(userRole.equals("user")){
List<String> list = new ArrayList<String>();
list.add("user");
return list;
}
return new ArrayList<>();
}
定义好鉴权接口 我们就可以使用了!!!
比如下述代码 是一个 帖子删除 的操作 删除的话 我们需要判断 是不是管理员 或者 是不是自己的帖子
/**
* 删除
*
* @param deleteRequest
* @param request
* @return
*/
@PostMapping("/delete")
@SaCheckRole(value = {"admin","user"},mode = SaMode.OR)
public BaseResponse<Boolean> deletePost(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
long id = deleteRequest.getId();
// 判断是否存在
Post oldPost = postService.getById(id);
if (oldPost == null){
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
}
boolean b = postService.removeById(id);
return ResultUtils.success(b);
}
补充
Sa-Token 的功能 不止 这些 但如果你是新手 想要自己做项目 或者想要学习新技术 上面教程是最好的入门!!!
如果 你 想 自己 做项目 那么 不妨 试试我 使用此框架 写的
一个初始化的模板 github.com/lizhe-0423/…
当然 上面的模板 还是不完整 的比如 hutool 里一些 第三方技术 我认为也是可以接入的
比如生成 二维码 、 图片验证码登录 、 excel、word 的 导入导出
支付宝 当面付
此时差不多 一个项目 用到的70% 的 通用技术 大概我们都用到了
所以 如果你是新手 那么 我很建议 你 去自己 学习一下 sa-token 和 hutool 使用这两者 足以应对70%项目开发
当然 在项目中 这些还是 不够的 比如 还需要 通用的返回信息 自定义的异常 处理 自己编写的 校验 方法 等等。。。