关于springboot:前后端分离架构下使用-SaToken-完成登录认证

29次阅读

共计 5422 个字符,预计需要花费 14 分钟才能阅读完成。

一、架构剖析

目前绝大多数零碎都曾经采纳“前后端拆散”架构来设计了,传统的 Session 模式鉴权也不再适宜这种架构(或者须要额定写很多的代码来专门适配)。

Sa-Token 是一个 java 轻量级权限认证框架,专为前后端拆散架构打造,次要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相干问题。

Gitee 开源地址:https://gitee.com/dromara/sa-token

本文将介绍在 Springboot 架构下的前后端拆散我的项目,如何应用 Sa-Token 不便的实现登录认证。

首先在我的项目中引入 Sa-Token 依赖:

<!-- Sa-Token 权限认证 -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.34.0</version>
</dependency>

注:如果你应用的是 SpringBoot 3.x,只须要将 sa-token-spring-boot-starter 批改为 sa-token-spring-boot3-starter 即可。

二、无 Cookie 模式

无 Cookie 模式:特指不反对 Cookie 性能的终端,艰深来讲就是咱们常说的 —— 前后端拆散模式

惯例 Web 端鉴权办法,个别由 Cookie 模式 实现,而 Cookie 有两个个性:

  1. 可由后端管制写入。
  2. 每次申请主动提交。

这就使得咱们在前端代码中,无需任何非凡操作,就能实现鉴权的全副流程(因为整个流程都是后端管制实现的)<br/>
而在 app、小程序等前后端拆散场景中,个别是没有 Cookie 这一性能的,此时大多数人都会一脸懵逼,咋进行鉴权啊?

见招拆招,其实答案很简略:

  • 不能后端管制写入了,就前端本人写入。(难点在 后端如何将 Token 传递到前端
  • 每次申请不能主动提交了,那就手动提交。(难点在 前端如何将 Token 传递到后端 ,同时 后端将其读取进去

三、后端将 token 返回到前端

  1. 首先调用 StpUtil.login(id) 进行登录。
  2. 调用 StpUtil.getTokenInfo() 返回以后会话的 token 具体参数。

    • 此办法返回一个对象,其有两个要害属性:tokenNametokenValue(token 的名称和 token 的值)。
    • 将此对象传递到前台,让前端人员将这两个值保留到本地。

代码示例:

// 登录接口
@RequestMapping("doLogin")
public SaResult doLogin() {
    // 第 1 步,先登录上 
    StpUtil.login(10001);
    // 第 2 步,获取 Token  相干参数 
    SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
    // 第 3 步,返回给前端 
    return SaResult.data(tokenInfo);
}

四、前端将 token 提交到后端

  1. 无论是 app 还是小程序,其传递形式都大同小异。
  2. 那就是,将 token 塞到申请 header 里,格局为:{tokenName: tokenValue}
  3. 以经典跨端框架 uni-app 为例:

形式 1,简略粗犷

// 1、首先在登录时,将 tokenValue 存储在本地,例如:uni.setStorageSync('tokenValue', tokenValue);

// 2、在发动 ajax 申请的中央,获取这个值,并塞到 header 里 
uni.request({
    url: 'https://www.example.com/request', // 仅为示例,并非实在接口地址。header: {
        "content-type": "application/x-www-form-urlencoded",
        "satoken": uni.getStorageSync('tokenValue')        // 要害代码, 留神参数名字是 satoken 
    },
    success: (res) => {console.log(res.data);    
    }
});

形式 2,更加灵便

// 1、首先在登录时,将 tokenName 和 tokenValue 一起存储在本地,例如:uni.setStorageSync('tokenName', tokenName); 
uni.setStorageSync('tokenValue', tokenValue); 

// 2、在发动 ajax 的中央,获取这两个值, 并组织到 head 里 
var tokenName = uni.getStorageSync('tokenName');    // 从本地缓存读取 tokenName 值
var tokenValue = uni.getStorageSync('tokenValue');    // 从本地缓存读取 tokenValue 值
var header = {"content-type": "application/x-www-form-urlencoded"};
if (tokenName != undefined && tokenName != '') {header[tokenName] = tokenValue;
}

// 3、后续在发动申请时将 header 对象塞到申请头部 
uni.request({
    url: 'https://www.example.com/request', // 仅为示例,并非实在接口地址。header: header,
    success: (res) => {console.log(res.data);    
    }
});
  1. 只有依照如此办法将 token 值传递到后端,Sa-Token 就能像传统 PC 端一样主动读取到 token 值,进行鉴权。
  2. 你可能会有疑难,难道我每个 ajax 都要写这么一坨?岂不是麻烦死了?

    • 你当然不能每个 ajax 都写这么一坨,因为这种重复性代码都是要封装在一个函数里对立调用的。

其它解决方案:

如果你对 Cookie 十分理解,那你就会明确,所谓 Cookie,实质上就是一个非凡的 header 参数而已,
而既然它只是一个 header 参数,咱们就能手动模仿实现它,从而实现鉴权操作。

这其实是对 无 Cookie 模式 的另一种解决方案,有趣味的同学能够百度理解一下,在此暂不赘述。

五、代码比照

为了更加直观的显示出 前后端一体架构 和 前后端拆散架构 的差别,此处再提供一个示例:

package com.pj.cases.up;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;

/**
 * Sa-Token 前后端拆散模式示例 
 * 
 * @author kong
 * @since 2022-10-17 
 */
@RestController
@RequestMapping("/NotCookie/")
public class NotCookieController {

    // 前后端一体模式的登录样例    ---- http://localhost:8081/NotCookie/doLogin?name=zhang&pwd=123456
    @RequestMapping("doLogin")
    public SaResult doLogin(String name, String pwd) {if("zhang".equals(name) && "123456".equals(pwd)) {
            // 会话登录 
            StpUtil.login(10001);
            return SaResult.ok();}
        return SaResult.error("登录失败");
    }
    
    // 前后端拆散模式的登录样例    ---- http://localhost:8081/NotCookie/doLogin2?name=zhang&pwd=123456
    @RequestMapping("doLogin2")
    public SaResult doLogin2(String name, String pwd) {if("zhang".equals(name) && "123456".equals(pwd)) {
            
            // 会话登录 
            StpUtil.login(10001);
            
            // 与惯例登录不同点之处:这里须要把 Token 信息从响应体中返回到前端 
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            return SaResult.data(tokenInfo);
        }
        return SaResult.error("登录失败");
    }
    
}
  • 接口一:Token 将在 Cookie 上下文返回到前端,并由浏览器每次申请时主动提交,这种模式适宜前后端一体的架构。
  • 接口二:Token 将在响应 body 里返回到前端,并由前端手动存储,并手动在每次申请时提交,这种模式适宜前后端拆散的架构。

六、自定义 Token 提交的前缀

在某些零碎中,前端提交 token 时会在后面加个固定的前缀,例如:

{"satoken": "Bearer xxxx-xxxx-xxxx-xxxx"}

此时后端如果不做任何非凡解决,框架将会把 Bearer 视为 token 的一部分,无奈失常读取 token 信息,导致鉴权失败。

为此,咱们须要在 yml 中增加如下配置:

sa-token: 
    # token 前缀
    token-prefix: Bearer

此时 Sa-Token 便可在读取 Token 时裁剪掉 Bearer,胜利获取xxxx-xxxx-xxxx-xxxx

留神点:

  1. Token 前缀 与 Token 值 之间必须有一个空格。
  2. 一旦配置了 Token 前缀,则前端提交 Token 时,必须带有前缀,否则会导致框架无奈读取 Token。
  3. 因为 Cookie 中无奈存储空格字符,也就象征配置 Token 前缀后,Cookie 鉴权形式将会生效,此时只能将 Token 提交到 header 里进行传输。

七、自定义 Token 格调

Sa-Token 默认的 token 生成策略是 uuid 格调,其模样相似于:623368f0-ae5e-4475-a53f-93e4225f16ae

如果你对这种格调不太感冒,还能够将 token 生成设置为其余格调。

怎么设置呢?只须要在 yml 配置文件里设置 sa-token.token-style= 格调类型 即可,其有多种取值:

// 1. token-style=uuid    —— uuid 格调 (默认格调)
"623368f0-ae5e-4475-a53f-93e4225f16ae"

// 2. token-style=simple-uuid    —— 同上,uuid 格调, 只不过去掉了中划线
"6fd4221395024b5f87edd34bc3258ee8"

// 3. token-style=random-32    —— 随机 32 位字符串
"qEjyPsEA1Bkc9dr8YP6okFr5umCZNR6W"

// 4. token-style=random-64    —— 随机 64 位字符串
"v4ueNLEpPwMtmOPMBtOOeIQsvP8z9gkMgIVibTUVjkrNrlfra5CGwQkViDjO8jcc"

// 5. token-style=random-128    —— 随机 128 位字符串
"nojYPmcEtrFEaN0Otpssa8I8jpk8FO53UcMZkCP9qyoHaDbKS6dxoRPky9c6QlftQ0pdzxRGXsKZmUSrPeZBOD6kJFfmfgiRyUmYWcj4WU4SSP2ilakWN1HYnIuX0Olj"

// 6. token-style=tik    —— tik 格调
"gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"

八、自定义 Token 生成策略

如果你觉着以上格调都不是你喜爱的类型,那么你还能够 自定义 token 生成策略,来定制化 token 生成格调。

怎么做呢?只须要重写 SaStrategy 策略类的 createToken 算法即可:

参考步骤如下:

1、在 SaTokenConfigure 配置类中增加代码:

@Configuration
public class SaTokenConfigure {
    /**
     * 重写 Sa-Token 框架外部算法策略 
     */
    @Autowired
    public void rewriteSaStrategy() {
        // 重写 Token 生成策略 
        SaStrategy.me.createToken = (loginId, loginType) -> {return SaFoxUtil.getRandomString(60);    // 随机 60 位长度字符串
        };
    }
}

2、再次调用 StpUtil.login(10001)办法进行登录,察看其生成的 token 款式:

gfuPSwZsnUhwgz08GTCH4wOgasWtc3odP4HLwXJ7NDGOximTvT4OlW19zeLH

参考资料

  • Sa-Token 文档:https://sa-token.cc
  • Gitee 仓库地址:https://gitee.com/dromara/sa-token
  • GitHub 仓库地址:https://github.com/dromara/sa-token

正文完
 0