Sa-Token介绍
Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。
相关链接
Github地址:https://github.com/dromara/sa-token
框架应用原理
接入权限框架
sa-token Maven依赖
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.33.0</version>
</dependency>
添加配置文件
具体配置文件含义请前往:https://sa-token.cc/doc.html#/use/config?id=%e6%89%80%e6%9c%89%e5%8f%af%e9%85%8d%e7%bd%ae%e9%a1%b9
sa-token.token-name=satoken
sa-token.timeout=2592000
sa-token.activity-timeout=-1
sa-token.is-concurrent=true
sa-token.is-share=true
sa-token.token-style=uuid
sa-token.is-log=false
配置全局异常捕获,以便于处理非认证用户的提示
@ControllerAdvice // 如果需要关闭全局异常拦截,直接注释此注解即可
public class GlobalExceptionHandler {
/**
* Sa-Token没有登陆异常
*
* @param req 请求:可以记录一些穿参的内容,这里没用到。
* @return 返回一个自定义字符串
*/
@ExceptionHandler({NotLoginException.class})
@ResponseBody
@ResponseStatus(value = HttpStatus.UNAUTHORIZED) // 错误码:401
public String NotLoginException(HttpServletRequest req, NotLoginException e) {
logger.error("【Sa-Token】:没有登陆异常,{}", e.getMessage());
return "宝贝,你当前没有登陆哦,请登录后重试!";
}
/**
* Sa-Token账号某功能被封禁
*
* @param req 请求:可以记录一些穿参的内容,这里没用到。
* @return 返回一个自定义字符串
*/
@ExceptionHandler({DisableServiceException.class})
@ResponseBody
@ResponseStatus(value = HttpStatus.UNAUTHORIZED) // 错误码:401
public String NotLoginException(HttpServletRequest req, DisableServiceException e) {
logger.error("【Sa-Token】:账号某功能被封禁,{}", e.getMessage());
return "宝贝,你当前账号此功能被封禁!";
}
/**
* Sa-Token没有权限
*
* @param req 请求:可以记录一些穿参的内容,这里没用到。
* @return 返回一个自定义字符串
*/
@ExceptionHandler({NotPermissionException.class})
@ResponseBody
@ResponseStatus(value = HttpStatus.UNAUTHORIZED) // 错误码:401
public String NotLoginException(HttpServletRequest req, NotPermissionException e) {
logger.error("【Sa-Token】:账号没有此权限,{}", e.getMessage());
return "宝贝,你当前账号没有此权限的能力!";
}
}
开启Sa-Token注解鉴权
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
}
添加SaToken的事件监听器!
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel;
import org.springframework.stereotype.Component;
/**
* 自定义侦听器的实现
*/
@Component
public class MySaTokenListener implements SaTokenListener {
/** 每次登录时触发 */
@Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
System.out.println("---------- 自定义侦听器实现 doLogin");
}
/** 每次注销时触发 */
@Override
public void doLogout(String loginType, Object loginId, String tokenValue) {
System.out.println("---------- 自定义侦听器实现 doLogout");
}
/** 每次被踢下线时触发 */
@Override
public void doKickout(String loginType, Object loginId, String tokenValue) {
System.out.println("---------- 自定义侦听器实现 doKickout");
}
/** 每次被顶下线时触发 */
@Override
public void doReplaced(String loginType, Object loginId, String tokenValue) {
System.out.println("---------- 自定义侦听器实现 doReplaced");
}
/** 每次被封禁时触发 */
@Override
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
System.out.println("---------- 自定义侦听器实现 doDisable");
}
/** 每次被解封时触发 */
@Override
public void doUntieDisable(String loginType, Object loginId, String service) {
System.out.println("---------- 自定义侦听器实现 doUntieDisable");
}
/** 每次二级认证时触发 */
@Override
public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
System.out.println("---------- 自定义侦听器实现 doOpenSafe");
}
/** 每次退出二级认证时触发 */
@Override
public void doCloseSafe(String loginType, String tokenValue, String service) {
System.out.println("---------- 自定义侦听器实现 doCloseSafe");
}
/** 每次创建Session时触发 */
@Override
public void doCreateSession(String id) {
System.out.println("---------- 自定义侦听器实现 doCreateSession");
}
/** 每次注销Session时触发 */
@Override
public void doLogoutSession(String id) {
System.out.println("---------- 自定义侦听器实现 doLogoutSession");
}
/** 每次Token续期时触发 */
@Override
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
System.out.println("---------- 自定义侦听器实现 doRenewTimeout");
}
}
添加角色认证、授权
import cn.dev33.satoken.stp.StpInterface;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义权限验证,对用户进行授权、认证
*/
@Slf4j
@Component // 保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {
/**
* 给用户授权
* @param loginId 账号id
* @param loginType 账号类型
* @return
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// todo 假装从数据查询loginId的权限列表有 book:read、book:write、eat.*
List<String> list = new ArrayList<String>();
list.add("book.read");
list.add("book.write");
list.add("eat.*");
log.info("对用户{},进行授权",loginId);
return list;
}
/**
* 给用户认证角色,没有角色就不写呗
* @param loginId 账号id
* @param loginType 账号类型
* @return
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// todo 假装从数据查询loginId的认证列表有 baoan、siji
List<String> list = new ArrayList<String>();
list.add("baoan");
list.add("siji");
log.info("对用户{},进行认证",loginId);
return list;
}
}
接口使用Sa-Token 限制Demo
import cn.dev33.satoken.annotation.SaCheckDisable;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SaCheckLogin // 开启所有登陆鉴权:下面接口必须登陆后才能访问
@RestController
@RequestMapping("/satoken")
public class SaTokenTestController {
/**
* 登陆
* @return
*/
@SaIgnore // 登陆接口肯定不能受到鉴权限制
@GetMapping("/login")
public String login() {
String account = "1001";
if (StpUtil.isDisable(account)) {
long disableTime = StpUtil.getDisableTime(account);
return "账号当前已封停,请等待解封!剩余解封时间:" + disableTime;
}
// 如果设置的是同端互斥,则需要配置文件中,将 isConcurrent 配置为false。然后调用login传入设备信息即可。
StpUtil.login(account, 60);
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
return "登陆成功!tokenInfo:" + tokenInfo;
}
/**
* 退出登陆
* @return
*/
@GetMapping("/logout")
public String logout() {
StpUtil.logout("1001");
return "退出成功!";
}
/**
* 踢号下线
* @return
*/
@GetMapping("/kickout")
public String kickout() {
StpUtil.kickout("1001");
return "踢人成功!";
}
/**
* 顶号
* @return
*/
@GetMapping("/replaced")
public String replaced() {
// todo 这里我是想判断某个设备是否登陆的,没找到API.
String account = "1001";
StpUtil.replaced(account, "必须指定设备信息!");
return "顶号成功!";
}
/**
* 封停。
* 1、可对账号封停
* 2、可对账号部分功能封停。
* 3、还可以根据封停次数,对账号进行封禁时间增长。具体前往官网查看
*
* @return
*/
@SaIgnore
@GetMapping("/jail")
public String jail() {
String account = "1001";
if (StpUtil.isDisable(account)) {
long disableTime = StpUtil.getDisableTime(account);
return "操作失败,账号已经封停过了!剩余解封时间" + disableTime;
}
StpUtil.logout(account);
StpUtil.disable(account, 10); // 注意封停不会强制退出,建议封停后,手动退出
return "账号封停10秒成功!";
}
/**
* 给账号解封,这里我做测试。添加忽略校鉴权注解。
* @return
*/
@SaIgnore
@SaCheckDisable("release")
@GetMapping("/release")
public String jailTest() {
StpUtil.untieDisable("1001");
return "账号解封成功";
}
/**
* 用户只要满足有权限且功能没被封禁就能访问
* @return
*/
@GetMapping("/sayhello")
@SaCheckDisable({"hello", "go"}) // 如果账号限制了hello、go功能,即便账号是超管,也无法访问!
@SaCheckPermission(value = {"book.read"}, orRole = {"baoan"}) // 指定接口权限 book.read或是baoan的Role就可以访问
public String sayHello() {
return "此接口可以正常访问!";
}
/**
* 此方法无法访问,没有任何权限、与角色可访问。
* @return
*/
@GetMapping("/notallow")
@SaCheckPermission(value = {"notallow"}, orRole = {"notallow"})
public String notallow() {
return "此接口可以正常访问!";
}
}
完结,自己测试吧!
引入Redis持久化信息
持久化方案:框架的鉴权记录默认只记录在内存中,每次配置的鉴权记录重启就消失了,我们引入Redis持久化方案!使得我们鉴权记录持久化!
官方说:Jackson有可能兼容性差,但是方便阅读,同时提供另一种序列化的Redis方案:https://sa-token.cc/doc.html#/up/integ-redis
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.33.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
Redis配置
spring.redis.host=XXXX
spring.redis.port=XXXX
spring.redis.password=XXXX
spring.redis.database=0
# Redis连接池配置(Spring默认Redis连接池是lettuce,如果使用jedis的连接池还需要引入其他依赖) 依次是:最大连接、最大空闲、最小连接数、连接等待时间
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-wait=100
框架会自动同步的Redis中。无需人工干预!
特殊说明:
上述文章均是作者实际操作后产出。烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤