环境搭建
sa-token官网文档
redis安装很简单,写入配置文件下面有实例,token存再redis中
AOP依赖注解实现方式再最后一个
实现:可以在具体方法中使用注解鉴权,不仅仅在controller上使用
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.28.0</version>
</dependency>
<!-- redis依赖包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Sa-Token-Redis 集成包的版本尽量与 Sa-Token-Starter 集成包的版本一致-->
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.28.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Sa-Token整合SpringAOP实现注解鉴权 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-aop</artifactId>
<version>1.28.0</version>
</dependency>
application-dev.yml
server:
port: 30991
servlet:
context-path: /jay-admin
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/kuMing?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456789
redis:
# Redis服务器地址
host: 127.0.0.1
port: 6379
# Redis数据库索引(默认为0)
database: 1
timeout: 5000
# 线程安全
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
jta:
atomikos:
datasource:
xa-data-source-class-name:
# 两种redis方式,下面的线程不安全
# jedis:
# pool:
# max-active: 50
# max-wait: 3000
# max-idle: 20
# min-idle: 2
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
# Sa-Token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: satoken
# token有效期,单位s 默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# token风格
token-style: random-128
# 是否输出操作日志
is-log: false
sso:
auth-url: http://sa-sso-server.com:9000/sso/auth
启动类:System.out.println(“启动成功:Sa-Token配置如下:” + SaManager.getConfig());
全局拦截规则
路由拦截规则
/**
* @author Jay
* @version V1.0
* @Package com.shanghai.test1114.config
* @date 2022/1/27 3:55 下午
* 拦截器
* 在高版本 SpringBoot (≥2.6.x) 版本下,需要额外添加 @EnableWebMvc 注解才可以使注册拦截器生效。
* 拦截器模式和AOP模式不可同时集成
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册Sa-Token的注解拦截器,打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册注解拦截器,并排除不需要注解鉴权的接口地址 (与登录拦截器无关)
//registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
// 注册Sa-Token的路由拦截器
//下面这种是只有登录后才可以访问其他接口,swagger放开可访问 路由拦截规则
registry.addInterceptor(new SaRouteInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/user/doLogin","/swagger-ui.html/**","/swagger-resources/**")
;
}
功能实现(登录,注销,封禁,踢下线)
@GetMapping("/doLogin")
StpUtil.login(user.getUsername()); 登录
StpUtil.logout(); 注销
StpUtil.kickout(RoolId); 踢下线
// 标记当前会话登录的账号id
// 建议的参数类型:long | int | String, 不可以传入复杂类型,如:User、Admin等等
StpUtil.login(Object id);
// 当前会话注销登录
StpUtil.logout();
// 获取当前会话是否已经登录,返回true=已登录,false=未登录
StpUtil.isLogin();
// 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`
StpUtil.checkLogin()
方法 | 返回值 | 释义 | |
---|---|---|---|
登录: | StpUtil.login(🆔) | void | 标记登录账号 |
StpUtil.login(🆔,true) | void | 记住我功能(在浏览器写入一个永久Cookie储存 Token,此时用户即使重启浏览器 Token 依然有效) | |
StpUtil.login(🆔,false) | void | 不记住我功能(在浏览器写入一个临时Cookie储存 Token,此时用户在重启浏览器后 Token 便会消失,导致会话失效) | |
StpUtil.login(10001, new SaLoginModel().setDevice(“PC”).setIsLastingCookie(true).setTimeout(60 * 60 * 24 * 7) ) | void | 此次登录的客户端设备标识, 用于[同端互斥登录]时指定此次登录的设备名称;是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在);指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的timeout值 | |
注销: | StpUtil.logout() | void | 强制注销当前会话 |
StpUtil.logout(🆔) | void | 强制指定账号注销 | |
StpUtil.logout(🆔, “PC”) | void | 强制指定账号指定端注销 | |
StpUtil.logoutByTokenValue(“token”) | void | 强制指定 Token 注销下线 | |
踢下线: | StpUtil.kickout(user.getUsername()) | void | 踢人下线 |
StpUtil.kickout(10001, “PC”) | void | 指定账号指定端踢下线 | |
StpUtil.kickoutByTokenValue(“token”) | void | 指定 Token 踢下线 | |
封禁: | StpUtil.disable(🆔, 86400) | void | 账号及封禁时间s,-1永久 |
StpUtil.isDisable(🆔) | boolean | 账号是否已被封禁{true=已被封禁, false=未被封禁} | |
StpUtil.getDisableTime(🆔) | long | 账号剩余封禁时间s | |
StpUtil.untieDisable(🆔) | void | 账号解封 |
强制注销 和 踢人下线 的区别:
强制注销等价于对方主动调用了注销方法,再次访问会提示:Token无效
踢人下线不会清除Token信息,而是将其打上特定标记,再次访问会提示:Token已被踢下线
正在登录的账号封禁:
封禁并不会掉线
先踢使其掉线,并封禁
StpUtil.kickout(🆔);
StpUtil.disable(🆔, 86400);
System.out.println("获取账号的tokenValue值:"+StpUtil.getTokenValueByLoginId(username));
System.out.println("获取账号的Session对象:若🈚️返回null:"+StpUtil.getSessionByLoginId(username, false));
System.out.println("获取账号的Session对象:"+StpUtil.getSessionByLoginId(username));
System.out.println("获取账号是否有指定角色:"+StpUtil.hasRole(username, "admin"));
System.out.println("当前账号是否有指定全部权限:"+StpUtil.hasPermissionAnd( "save","select","delete","update"));
注解鉴权
前面有导入AOP包,可实现方法鉴权
@GetMapping("/doSelect/account")
@ApiOperation("所有用户")
// @SaCheckLogin //登录认证:只有登录之后才能进入该方法
// @SaCheckRole("admin") //角色认证:必须具有指定角色才能进入该方法
// @SaCheckPermission("user-get") //权限认证:必须具有指定权限才能进入该方法
// @SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR) 是否只要一个权限放行
// @SaCheckPermission(value = "user-add", orRole = "admin") 权限和角色都有
// orRole = {"admin", "manager", "staff"} 只有其中一个角色
// orRole = {"admin, manager, staff"} 代表必须同时具有三个角色
// @SaCheckSafe() //二级认证:必须二级认证之后才能进入该方法
// @SaCheckBasic(account = "sa:123456") //Http Basic 认证:只有通过 Basic 认证后才能进入该方法
public ResponseWeb<List<UserResp>> doSelectAccount() {
List<User> user = userService.doSelectAccount();
List<UserResp> respList = userDtoToMapper.userToUserResp(user);
return ResponseUtils.success(respList);
}
拦截规则(进阶版)
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册路由拦截器,自定义认证规则
registry.addInterceptor(new SaRouteInterceptor((req, res, handler)->{
// 根据路由划分模块,不同模块不同鉴权
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"));
})).addPathPatterns("/**");
}
}
补充(切换账号)
// 将当前会话[身份临时切换]为其它账号
StpUtil.switchTo(10044);
// 此时再调用此方法会返回 10044 (我们临时切换到的账号id)
StpUtil.getLoginId();
// 结束 [身份临时切换]
StpUtil.endSwitch();