使用Sa-token实现单点登录
创始人
2024-05-12 22:32:25
0

使用Sa-token实现单点登录

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

单点登录需求

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

为何选择Sa-Token

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

简单使用sa-token

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

        cn.dev33sa-token-spring-boot-starter1.34.0cn.dev33sa-token-sso1.34.0cn.dev33sa-token-dao-redis-jackson1.34.0

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: 10slettuce: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 {// 注册拦截器@Overridepublic 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去判断接口是否异常@ResponseBodypublic 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)@ResponseBodypublic 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)@ResponseBodypublic SaResult handle404Error(NoHandlerFoundException e) {log.error("访问资源不存在", e);return new SaResult(404, "访问的资源不存在", null);}

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

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

相关内容

热门资讯

【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
【前端】‘??‘与‘||‘有什... 0 问题 经常写const data = res.data.a ?? ''或者const d...
ChatGPT 怎么用最新详细... ChatGPT 以其强大的信息整合和对话能力惊艳了全球,在自然语言处理上面表现出了惊人...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...