Sa-Token – 轻量级权限认证框架!

2023-10-26

Sa-Token介绍

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证权限认证单点登录OAuth2.0分布式Session会话微服务网关鉴权 等一系列权限相关问题。

相关链接

官网:https://sa-token.cc/

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("/**");
    }
}

添加事件监听器

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中。无需人工干预!

特殊说明: 以上文章,均是我实际操作,写出来的笔记资料,不会盗用别人文章!烦请各位,请勿直接盗用!转载记得标注来源!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Sa-Token – 轻量级权限认证框架! 的相关文章

  • Cordova 环境搭建+打包Android APK

    一 环境搭建 1 JDK 1 1 下载JDK http www oracle com technetwork java javase downloads jdk8 downloads 2133151 html 在上面的网址中选择符合自己操作
  • 正则表达式匹配案例

    匹配案例 1 判断变量的名称 由数字 字母 下划线组成 不能以数字作为开头 a zA Z a zA Z0 9 注意 如果不加 中间如果有不符合字符如abc 123就会匹配成功 import re result re match r a zA
  • 《ReactNative系列讲义》进阶篇---06.FlatList(三)

    版权声明 本文为博主原创文章 未经博主允许不得转载 一 简介 截止到上篇文章 关于FlatList无论是简单的还是高级的属性用法都已经介绍完毕 今天我们一起来看看FlatList更高级的玩法 相关方法的调用 二 基础知识 获取FlatLis
  • Elasticsearch的关键词搜索

    返回给前端的实体类 Data AllArgsConstructor NoArgsConstructor public class PageResult private Long total private List
  • el-table纵向垂直表头

    参考 https www jianshu com p 1f38eaffd070
  • facenet采坑之旅,主要记录一些用facenet过程中遇到的大大小小的问题

    问题1 Unable to run align dataset mtcnn py getting an attribute error module facenet has no attribute store revision info
  • 封装一个带el-form的,带el-table的,带分页的,带搜索查询的dialog组件,很使用的二次封装组件。

    封装dialog小案例 提示 这是我工作中封装的代码 很使用 需要的可以拿去 在我们的代码中往往会出现点击按钮出现弹窗进行操作 那么我们就需要对dialog进行一个二次封装 下边是大概的一个样式 对组件进行二次封装
  • burpsuite与sqlmap结合使用之CO2

    在使用sqlmap时 对一个页面进行注入时需要认证信息 如果将页面信息每次都保存到本地比较麻烦 使用命令行指令也需要cookie值 burpsuite中有一个快速sqlmap扫描的工具 CO2是一个burp插件 burp将拦截的请求直接发给
  • C++设计模式之适配器模式(adapter)(结构型)

    一 结构型模式概述 结构型模式 Structural Pattern 描述如何将类或者对象结合在一起形成更大的结构 就像搭积木 可以通过简单积木的组合形成复杂的 功能更为强大的结构 结构型模式可以分为类结构型模式和对象结构型模式 类结构型模
  • 低通,高通,带通,带阻滤波器的定义

    1 低通 Low pass filter 是容许低于截止频率的信号通过 但高于截止频率的信号不能通过的电子滤波装置 2 高通 是一种让某一频率以上的信号分量通过 而对该频率以下的信号分量大大抑制的电容 电感与电阻等器件的组合装置 其特性在时
  • 个人域名备案有什么要求?需要准备什么?

    现在域名是非常火爆的 越来越多的人开始注册域名 有个人域名 有企业域名 域名都是需要备案的 很多人不知道个人域名备案有哪些要求 需要准备些什么 下面我们一起来看看 个人域名备案的要求 1 国内主机都要求备案 2 网站访问者大多是国内人时国内
  • JDK的下载及配置JAVE_HOME

    初学JAVA的很多朋友的第一步就是下载JDK 首先 我来解释一下什么是JDK JDK是JAVA语言的软件开发工具包 目前已经到了JDK9的版本 在此提醒各位初学的朋友 下载JDK的话下载JDK8就够了 因为JDK9版本还不稳定 JDK就已经
  • 原生ajax 设置get请求参数和请求头信息和发送 post请求

    get请求发送参数直接在路径后面加问号即可 xhr open GET http 127 0 0 1 8000 server a 100 b 50 设置请求头信息 1 请求头中本来就有这个属性 只是修改该属性的值 xhr setRequest
  • ElasticSearch 查询语法

    环境 ElasticSearch6 4 2 以下查询请求方式均为Post 索引名 book 索引结构 mappings novel properties word count type interger author type keywor
  • Vue基础之指令与过滤器

    vue 简介 1 什么是 vue 官方给出的概念 Vue 读音 vju 类似于 view 是一套用于构建用户界面的前端框架 2 vue 的特性 vue 框架的特性 主要体现在如下两方面 数据驱动视图 双向数据绑定 2 1 数据驱动视图 在使
  • unity第一人称射击游戏,枪击游戏,功能完整可以当大作业或者毕设

    unity第一人称射击游戏 枪击游戏 含源码和exe导出文件 下载链接在文末 unity第一人称枪击游戏 分为海岛地图和沙漠地图 可以开镜射击 敌人可以移动 菜单页面有开始游戏 游戏说明 退出三个按钮 游戏有音效 可以通过ASDW移动人物
  • 查看jks证书内容

    keytool list v keystore xxx jks 输入密钥库口令
  • C++进阶必读书籍

    结合一些我的学习经历 希望对于想学C 的人有些帮助 大家有什么好想法望提出 我老师最初是从C语言教起的 用的是潭浩强的 lt
  • ajax day3

    3 将普通对象转为查询参数字符串形式 创建URLSearchParams参数 再用toString方法转为字符串 4 xhr对象 请求参数 body参数 5 promise promise对象一旦被兑现或拒绝 就是已敲定了 状态无法再被改变
  • Python模块之操作数据库MySQL篇

    目录 一 安装PyMySQL模块 二 操作数据库 1 连接数据库 2 执行sql语句 execute和executemany 3 创建数据表 三 操作MySQL数据表 1 新增数据 2 查询数据 3 修改数据 4 删除数据 5 踩到的坑 一

随机推荐

  • python批量读取Excel文件

    将同一个文件夹下的xlsx文件读取 import os import pandas as pd path r path of file for i in os listdir path df pd read excel os path jo
  • [读论文]CAAD-2018 Targeted Attack方向季军技术报告

    这次分享的是CAAD 2018比赛中Northwest Security团队的技术报告 该团队在此次比赛中取得了了targeted Attack 方向第三名 non targeted Attack方向第四名的成绩 题目 Leverage O
  • 构建 fluentd 镜像与部署应用

    本文将具体介绍如何在基础镜像 ubuntu 20 04 上搭建 fluentd 镜像 并且实现监控指定目录的日志文件 构建镜像 首先 从 docker hub 中挑选一个合适的基础镜像 例如 ubuntu 20 04 docker pull
  • 区块链+物联网=?

    链客 专为开发者而生 有问必答 此文章来自区块链技术社区 未经允许拒绝转载 区块链与物联网 IoT 的交叉应用已成为最有前途的区块链用例之一 在过去的几个月里 IoTeX一直与我们的战略合作伙伴合作 并进行了独立的研究 为了能够在短期内采用
  • 新闻

    4月 中国科技产业智库甲子光年发布 AIGC应用与实践研究展望报告 及AIGC产业图谱 面向AIGC技术创新者 产业参与者 资本机构和政府等各方展现AIGC产业的整体生态环境和行业发展 华院数智人凭借其在生成式AI技术 人机交互能力和市场应
  • esp32+vscode环境搭建速记

    esp32idf vscode环境搭建速记 建议按照入下步骤进行 在vscode插件里安装esp32idf 或者用在线的下载器安装会出现一些莫名奇妙的问题 第一步 安装esp32idf 官方网址 https dl espressif cn
  • gridlayout java_Swing-布局管理器之GridLayout(网格布局)-入门

    网格布局特点 l 使容器中的各组件呈M行 N列的网格状分布 l 网格每列宽度相同 等于容器的宽度除以网格的列数 l 网格每行高度相同 等于容器的高度除以网格的行数 l 各组件的排列方式为 从上到下 从左到右 l 组件放入容器的次序决定了它在
  • idea中thymeleaf语法不提示的所有原因

    首先pom xml里面要导入thymeleaf的依赖 然后在html中加入 xmlns th http www thymeleaf org 最后点击file gt settings 查看插件是否使用 未使用点击打勾重启
  • AMS1117典型电路

    AMS1117 3 3V 5V 封装 常见应用连接 1 输入旁路电容Input Bypass Capacitor A 10uF tantalum on the input is a suitable input bypassing fora
  • 解决 SyntaxError:Unexpected end of JSON input 或 Unexpected token u in JSON at position 0 问题

    1 报错原因 JSON 接收的数据不完整 或者数据格式不符合要求 如 undefined 2 JSON 数据格式要求 1 JSON文件都是被包裹在一个大括号中 通过key value的方式来表达数据 2 JSON的Key必须包裹在一个双引号
  • Python 魔法方法(三) __getattr__,__setattr__, __delattr__

    1 getattr 当我们访问一个不存在的属性的时候 会抛出异常 提示我们不存在这个属性 而这个异常就是 getattr 方法抛出的 其原因在于他是访问一个不存在的属性的最后落脚点 作为异常抛出的地方提示出错再适合不过了 看例子 我们找一个
  • 调试最长的一帧(第27天)

    对于几个多线程渲染中的成员变量 继续抄一抄 Block阻塞器 BlockCount 计数器类 它与阻塞器类的使用方法基本相同 block 阻塞线程 release 释放线程 不过除此之外 BlockCount的构造函数还可以设置一个阻塞计数
  • Point Cloud Library学习之ICP迭代最近点匹配法NDT2D正态分布转换法

    参考来源 https pointclouds org documentation classpcl 1 1 registration html ab1d64f86162b2df716ead8d978579c11 http epsilonjo
  • ctab提取dna流程图_核酸提取、纯化与常见问题解答

    写在前面 核酸提取包括DNA提取 RNA提取 质粒提取 核酸是遗传信息的携带者 是基因表达的物质基础 是分子生物学研究的主要对象 无论是进行核酸的结构还是功能研究 首先都需要对核酸进行提取和纯化 核酸是生命的最基本物质之一 可分为DNA和R
  • Kotlin协程的简单用法(GlobalScope、lifecycleScope、viewModelScope)

    协程 Coroutine 协程就像非常轻量级的线程 线程是由系统调度的 线程切换或线程阻塞的开销都比较大 而协程依赖于线程 但是协程挂起时不需要阻塞线程 协程是由开发者控制的 所以协程也像用户态的线程 非常轻量级 一个线程中可以创建任意个协
  • 目前开源数据集整理

    Attention 我的Dr Sure项目正式上线了 主旨在分享学习Tensorflow以及DeepLearning中的一些想法 期间随时更新我的论文心得以及想法 Github地址 https github com wangqingbaid
  • RabbitMQ系列(十)RabbitMQ进阶-Queue队列参数详解-死信交换机

    RabbitMQ进阶 Queue队列参数详解 死信交换机 文章目录 RabbitMQ进阶 Queue队列参数详解 死信交换机 1 Dead Letter Exchange 介绍 2 死信消息方式 2 1 消息被拒绝 2 1 1 channe
  • vim 使用set paste 解决多行复制粘贴乱序问题

    要粘贴的话 先set paste 然后粘贴 然后再set nopaste Reference https blog csdn net Dream Flying BJ article details 54708157
  • Ethere以太坊学习笔记

    以下不一定全 准确率99 1字节等于2gas 1 变量类型 是否是真 bool 数字类型 int uint 有符号和无符号整形 默认256 int8到uint256 地址类型 address 20字节长度 属性方法 send call ca
  • Sa-Token – 轻量级权限认证框架!

    本页目录 Sa Token介绍 相关链接 框架应用原理 接入权限框架 sa token Maven依赖 添加配置文件 配置全局异常捕获 开启Sa Token注解鉴权 添加事件监听器 添加角色认证 授权 使用Sa Token Demo 引入R