使用Sa-token实现单点登录

news2025/1/11 6:08:10

使用Sa-token实现单点登录

  • 单点登录需求
    • 为何选择Sa-Token
    • 简单使用sa-token
    • 接口如何保持登录态
    • 使用拦截器实现鉴权
    • 聊聊Sa-Token的理解
    • 聊聊遇到的一些问题

单点登录需求

     其实一直想写一个单点登录系统,现在的现状是公司内部有非常多项目的,然后每个项目一套登录系统,系统和系统之间存在单独的鉴权,每一个操作应用都需要登录一次的话,这样不仅仅用户体验不好,也会出现非常多重复的代码,于是单点登录的需求诞生了。

为何选择Sa-Token

     轻量级且开箱即用,如果你说为何不用shiro,那么我只能说它比shiro更加轻量更加好用,只需要简单的配置和写几行代码,即可实现登录功能。

简单使用sa-token

1.往项目中引入sa-token依赖

        <!-- sa-token鉴权模块 -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot-starter</artifactId>
            <version>1.34.0</version>
        </dependency>

        <!-- Sa-Token 插件:整合SSO 【sso单点登录依赖】 -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-sso</artifactId>
            <version>1.34.0</version>
        </dependency>

        <!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) 【后续版本管理改为父级】 -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-dao-redis-jackson</artifactId>
            <version>1.34.0</version>
        </dependency>

2.编写配置文件

# 端口
server:
    port: 9000

# Sa-Token 配置
sa-token: 
    # ------- SSO-模式一相关配置  (非模式一不需要配置) 
    # cookie:
         # 配置 Cookie 作用域 
         # domain: stp.com
        
    # ------- SSO-模式二相关配置 
    sso: 
        # Ticket有效期 (单位: 秒),默认五分钟 
        ticket-timeout: 300
        # 所有允许的授权回调地址
        allow-url: "*"
        # 是否打开单点注销功能
        is-slo: true
        
        # ------- SSO-模式三相关配置 (下面的配置在SSO模式三并且 is-slo=true 时打开) 
        # 是否打开模式三 
        isHttp: true
        # 接口调用秘钥(用于SSO模式三的单点注销功能)
        secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
        # ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明) 
        
spring: 
    # Redis配置 (SSO模式一和模式二使用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    

3.编写登录接口

// 会话登录接口 
@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("登录失败");
}

     上术代码已经实现了登录鉴权,但是我们也许可以注意到此处仅仅做了会话登录,并没有主动向前端返回 Token 信息。 是因为不需要吗?严格来讲是需要的,只不过 StpUtil.login(id) 方法利用了 Cookie 自动注入的特性,省略了你手写返回 Token 的代码。
     但在某些情况下,我们就是需要返回token给到前端,那应该如何操作呢?请往下看

     登录接口返回token信息

    // 登录接口
    @RequestMapping("doLogin")
    public SaResult doLogin(String username, String password) {
        // 第1步,先登录上
        StpUtil.login(10003);

        // 第2步,获取 Token  相关参数
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        // 第3步,返回给前端
        return SaResult.data(tokenInfo);
    }

     接口将会返回如下的数据:
在这里插入图片描述

     其中tokenName是请求头名称,tokenValue是请求头的值,将它们放入请求头即可保持登录态。

接口如何保持登录态

请求头放入token【登录接口返回】即可,比如:
在这里插入图片描述
controller代码如下:

    @RequestMapping("/userinfo")
    public Object userinfo() {

        // 自定义返回结果(模拟)
        return SaResult.ok()
                .set("id", StpUtil.getLoginId())
                .set("name", "zengjq")
                .set("sex", "男")
                .set("age", 18);
    }

是不是非常简单。我们通过StpUtil.getLoginId()即可拿到登录用户的id,但实际情况下我们往往需要拿到用户名称部门之类的更多信息,而登录态工具类StpUtil并未不能获取到,有同学肯定想到了,我拿用户id去访问数据库呀,但是每次都去查数据库那么它的压力就太大啦,其实sa-token提供了一个TokenSession
它是会话中的数据缓存组件,通过 Session 我们可以很方便的缓存一些高频读写数据,提高程序性能,例如:

// 在登录时缓存user对象 
StpUtil.getTokenSession().set("user", user);

// 然后我们就可以在任意处使用这个user对象
SysUser user = (SysUser) StpUtil.getTokenSession().get("user");

其实Sa-Token还提供了别的session类,这里不做赘述,有兴趣可以移步官方文档 sa-token session

使用拦截器实现鉴权

     我们来看拦截器代码:

import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

    // 注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 新增登录校验拦截器 校验规则为 StpUtil.checkLogin() 登录校验。
        SaInterceptor saInterceptor = new SaInterceptor(handle -> StpUtil.checkLogin());

        // 注册 Sa-Token 拦截器
        registry.addInterceptor(saInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/user/doLogin", "/demo/**");  // 排除了/user/doLogin接口用来开放登录
    }
}

     这里的拦截器使用的是spring框架自带的,并不是sa-token提供的,所以它不仅仅可以用于sa-token的鉴权,还可以应用于spring框架的所有拦截操作

     实现完上述代码,sa-token 项目的简单鉴权就算是完成了。但其实sa-token还提供了许多功能,比如权限认证OAuth2.0分布式Session会话微服务网关鉴权 等等,有需要还是建议使用前看看官方文档 Sa-Token

聊聊Sa-Token的理解

     Sa-Token 我的理解是:它会更加希望你搭配一套统一的前端来使用,而不是每个系统都搭建一套登录系统,就算是在SSO单点登录模块里面,它依旧会给出 SSO整合-定制化登录页面 的教程,希望在未登录时跳转至我们编写好的页面,而不是去返回统一状态码给到前端。而我们公司会更加倾向于去返回统一json数据和不同的code状态码,其他交给前端自行判断。我们来看看我公司的处理。
     如果我想未登录时返回某个状态码,我们需要在异常处理类中加入NotLoginExceptionhandler
异常处理类代码如下:

@ControllerAdvice
public class GlobalExceptionHandler {
	private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

	@ExceptionHandler(NotLoginException.class)
	@ResponseStatus(HttpStatus.OK)	// HttpStatus.OK是因为前端不基于httpStatus去判断接口是否异常
	@ResponseBody
	public SaResult handleNoHandlerFoundException(NotLoginException e) {
		log.error("用户未登录 ", e);
		// ErrorCode.LOGIN_REQUIRE.getCode() 是未登录时的与前端约定好的异常编码
		return SaResult.error(e.getMessage()).setCode(ErrorCode.LOGIN_REQUIRE.getCode());
	}


	@ExceptionHandler(SaTokenException.class)
	@ResponseStatus(HttpStatus.OK)
	@ResponseBody
	public SaResult handleSaTokenException(SaTokenException e) {
		log.error("sa-token抛出其他异常 ", e);
		return SaResult.error(e.getMessage());
	}
}

聊聊遇到的一些问题

当接口404时,spring会拦截后直接抛出异常信息:
在这里插入图片描述

上面的json其实并不是我们返回的,而是spring框架层返回的,如果我想改成自定义json,应该怎么处理呢?请往下看

自定义接口404响应信息
1、在配置文件中加入配置

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

throw-exception-if-no-handler-found表示当没有对应处理器时,允许抛出异常;而add-mappings表示是否为静态资源添加对应的处理器。而默认404异常不被全局处理器拦截,才导致未抛出异常。

2、在全局异常处理中捕获404

	@ExceptionHandler(NoHandlerFoundException.class)
	@ResponseStatus(HttpStatus.OK)
	@ResponseBody
	public SaResult handle404Error(NoHandlerFoundException e) {
		log.error("访问资源不存在", e);
		return new SaResult(404, "访问的资源不存在", null);
	}

其实一般我们会直接捕获Exception.class而不是特定的异常,只是如果你想做特殊处理,才需要类似上述的处理代码

好了,以上就是本次博客的全部内容,如有疑问,欢迎留言沟通

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/168003.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据分析面试题--数理知识点1

目录标题1&#xff0c;python统计一段话每个单词出现的次数2&#xff0c;SQL中如何利用replace函数统计给定重复字段在字符串中的出现频率&#xff1f;3&#xff0c;常见的统计分析方法有哪些&#xff1f;拿到数据如何分析4&#xff0c;参数估计和假设检验的联系和区别5&#x…

网络实验之OSPF路由协议(一)

一、OSPF路由协议简介 开放式最短路径优先&#xff08;Open Shortest Path First&#xff0c;OSPF&#xff09;路由协议是用于网际协议&#xff08;IP&#xff09;网络的链路状态路由协议。该协议使用链路状态路由算法的内部网关协议&#xff08;IGP&#xff09;&#xff0c;在…

NIO笔记

一. NIO 基础 non-blocking io 非阻塞 IO 1. 三大组件 1.1 Channel & Buffer channel 有一点类似于 stream&#xff0c;它就是读写数据的双向通道&#xff0c;可以从 channel 将数据读入 buffer&#xff0c;也可以将 buffer 的数据写入 channel&#xff0c;而之前的 st…

SAP ABAP 代码修改自动比较对象版本一致

第一步&#xff0c;找到SE38/SE37代码修改的出口 SMOD中查找 第二步&#xff0c;实施增强 CMOD中添加增强并激活&#xff0c;如下图 第三步&#xff0c;添加代码 如上图两个双击添加并修改代码 ZXSEUU08中与 ZXSEUU01代码一致&#xff0c;如下 *&----------------------…

【年度总结】我的2022年-职业生涯大转折

【年度总结】我的2022年-职业生涯大转折2022总结大厂的苦与乐找工作的焦虑再起启航2023展望持续刷题持续学习捡起博客在漩涡中疯狂挣扎的一年 2022总结 大厂的苦与乐 上半年主要在搞中台&#xff0c;需要对接的其他团队比较多&#xff0c;每天都在对接需求、优化需求。同时还…

Python NumPy 创建数组(ndarray)

前言NumPy&#xff08;Numerical Python的缩写&#xff09;是一个开源的Python科学计算库。使用NumPy&#xff0c;就可以很自然地使用数组和矩阵。NumPy包含很多实用的数学函数&#xff0c;涵盖线性代数运算、傅里叶变换和随机数生成等功能。本文主要介绍使用Python NumPy 创建…

鉴源论坛 · 观辙丨基于机器学习的汽车CAN总线异常检测方法

作者 | 张渊策 上海控安可信软件创新研究院研发工程师 来源 | 鉴源实验室 目前机器学习是研究车辆网络入侵检测技术的热门方向&#xff0c;通过引入机器学习算法来识别车载总线上的网络报文&#xff0c;可实现对车辆已知/未知威胁的入侵检测。这种基于机器学习的异常检测技术普…

chrono_duration(一)

文章目录chrono简介std::chrono::durationduratio基本介绍基本概念使用引入std::ratio 参数深入特化的duratio改造之前的代码静态成员函数 count原型例子构造函数支持加减乘除运算编译细节支持比较运算符查询范围类型转换例子引入修改seconds的范围浮点类型系统特化的duratio自…

os模块的使用方法详解

os模块os模块负责程序与操作系统的交互&#xff0c;提供了访问操作系统底层的接口&#xff1b;即os模块提供了非常丰富的方法用来处理文件和目录。使用的时候需要导入该模块&#xff1a;import os常用方法如下&#xff1a;方法名作用os.remove(‘path/filename’)删除文件os.re…

Unidbg模拟执行某段子so实操教程(一) 先把框架搭起来

一、目标 最近又开始研究Unidbg了&#xff0c;费了好大劲&#xff0c;没有跑起来。今天就先找个软柿子捏捏看。 今天的目标是 之前研究的 某段子App签名计算方法(一) 某段子App版本 5.5.10 二、步骤 先搭起框架来 在 /unidbg/unidbg-android/src/test/java/ 下面新建一个 …

K8S 三种探针ReadinessProbe、LivenessProbe和StartupProbe 之探索

一、事件背景因为k8s中采用大量的异步机制&#xff0c;以及多种对象关系设计上的解耦&#xff0c;当应用实例数增加/删除、或者应用版本发生变化触发滚动升级时&#xff0c;系统并不能保证应用相关的service、ingress配置总是及时能完成刷新。在一些情况下&#xff0c;往往只是…

Python爬虫之Scrapy框架系列(4)——项目实战【某瓣Top250电影更多信息的获取】

前言&#xff1a; 上篇文章使用Scrapy框架简单爬取并下载了某瓣Top250首页的排名前25个电影的电影名。 太寒酸了&#xff0c;这篇文章咱就来仔细搞一搞&#xff0c;搞到更加详细的信息&#xff01;&#xff01;&#xff01; 目录&#xff1a;1.分析2.使用scrapy shell提取电影详…

进程信号--Linux

文章目录信号&#xff1f;kill -l 指令查看所有信号信号的工作流程信号产生1.通过终端按键产生信号2.通过系统调用接口产生信号3.通过软件产生信号4.硬件异常产生信号信号接收信号处理总结信号&#xff1f; 进程间的通信我们了解到有管道通信&#xff0c;有共享内存的通信。这…

flowable编译

git clone -b flowable-release-6.7.2 https://github.com/flowable/flowable-engine.git下载之后File-Open&#xff0c;打开工程&#xff0c;modules是核心代码模块 找到flowable-root.xml按下altf12 &#xff0c;启动Terminal终端输入命令&#xff1a;mvn clean package -Ds…

《Buildozer打包实战指南》第三节 安装Buildozer打包所需的依赖文件

目录 3.1 安装依赖软件包 3.2 安装Cython 3.3 设置环境变量 3.4 安装p4a、Android SDK、NDK以及其他编译文件 Buidozer这个打包库下载安装完毕之后&#xff0c;我们还需要下载一些打包安卓apk所需要的依赖文件。 3.1 安装依赖软件包 首先输入以下命令更新Ubuntu上的软件包…

使众人行:如何带领一群人把事做成?

你好&#xff0c;我是雷蓓蓓&#xff0c;一名程序员出身的项目经理&#xff0c;曾任网易杭研项目管理部总监。 我所带领的网易杭研项目管理部&#xff0c;从2011年成立以来&#xff0c;就一直在互联网项目管理领域深耕&#xff0c;为网易云音乐、网易严选、云计算、智慧企业等…

智慧社区管理系统改造方案

伴随着城市发展的持续加速&#xff0c;许多在建智慧社区和老旧小区智能化改造都在有规划的展开着。如今许多老旧小区在展开设备升级&#xff0c;许多小区智能安防设备、物业管理服务系统软件、社区综合服务平台及其监控器设备等都会展开智能化改造。但是&#xff0c;很多人对老…

17.优于select的epoll

优于select的epoll epoll 理解及应用 select复用方法其实由来已久&#xff0c;因此&#xff0c;利用该技术后&#xff0c;无论如何优化程序性能也无法同时接入上百个客户端&#xff08;当然&#xff0c;硬件性能不同&#xff0c;差别也很大&#xff09;。这种select方式并不适…

IIC驱动中景园0.96寸OLED

驱动硬件介绍 1、驱动电压3.3到5,但是正点的也是这个芯片说用3.3 5会烧坏掉。 2、RST 上的低电平,将导致OLED 复位,在每次初始化之前,都应该复位一下 OLED 模块。而我们使用四线,里面就没有复位了 3、裸屏有多种接口方式(驱动芯片为SSD1306) 6800、8080 两种并行接口方…

Redis应用2(Redison)

不推荐使用application的配置方式,因为会替代spring内部的对于redis的配置方式 注意:如果redis数据库没有密码,不要使用 config.useSingleServer().setPassword("") 的形式,直接跳过setPassword()就可以,配置类写法如下: Configuration public class RedisConfig…