Sa-token简单介绍和基本使用

Posted xiaocstudy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sa-token简单介绍和基本使用相关的知识,希望对你有一定的参考价值。

Sa-Token

官方文档 官方文档写的很好,推荐去参考

1.概述

1.1 Sa-Token介绍

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

功能简单示例

Sa-Token 的 API 设计非常简单,有多简单呢?以登录认证为例,你只需要:

// 在登录时写入当前会话的账号id
StpUtil.login(10001);

// 然后在需要校验登录处调用以下方法:
// 如果当前会话未登录,这句代码会抛出 `NotLoginException` 异常
StpUtil.checkLogin();

权限认证示例(只有具备 user:add 权限的会话才可以进入请求)

@SaCheckPermission("user:add")
@RequestMapping("/user/insert")
public String insert(SysUser user) 
    // ... 
    return "用户增加";

将某个账号踢下线(待到对方再次访问系统时会抛出NotLoginException异常)

// 将账号id为 10001 的会话踢下线 
StpUtil.kickout(10001);

其他功能

StpUtil.login(10001);    // 标记当前会话登录的账号id
StpUtil.getLoginId();    // 获取当前会话登录的账号id
StpUtil.isLogin();    // 获取当前会话是否已经登录, 返回true或false
StpUtil.logout();    // 当前会话注销登录
StpUtil.kickout(10001);    // 将账号为10001的会话踢下线
StpUtil.hasRole("super-admin");    // 查询当前账号是否含有指定角色标识, 返回true或false
StpUtil.hasPermission("user:add");    // 查询当前账号是否含有指定权限, 返回true或false
StpUtil.getSession();    // 获取当前账号id的Session
StpUtil.getSessionByLoginId(10001);    // 获取账号id为10001的Session
StpUtil.getTokenValueByLoginId(10001);    // 获取账号id为10001的token令牌值
StpUtil.login(10001, "PC");    // 指定设备标识登录,常用于“同端互斥登录”
StpUtil.kickout(10001, "PC");    // 指定账号指定设备标识踢下线 (不同端不受影响)
StpUtil.openSafe(120);    // 在当前会话开启二级认证,有效期为120秒 
StpUtil.checkSafe();    // 校验当前会话是否处于二级认证有效期内,校验失败会抛出异常 
StpUtil.switchTo(10044);    // 将当前会话身份临时切换为其它账号 

1.2 Sa-Token 功能一览

  • 登录认证 —— 单端登录、多端登录、同端互斥登录、七天内免登录
  • 权限认证 —— 权限认证、角色认证、会话二级认证
  • Session会话 —— 全端共享Session、单端独享Session、自定义Session
  • 踢人下线 —— 根据账号id踢人下线、根据Token值踢人下线
  • 账号封禁 —— 指定天数封禁、永久封禁、设定解封时间
  • 持久层扩展 —— 可集成Redis、Memcached等专业缓存中间件,重启数据不丢失
  • 分布式会话 —— 提供jwt集成、共享数据中心两种分布式会话方案
  • 微服务网关鉴权 —— 适配Gateway、ShenYu、Zuul等常见网关的路由拦截认证
  • 单点登录 —— 内置三种单点登录模式:无论是否跨域、是否共享Redis,都可以搞定
  • OAuth2.0认证 —— 基于RFC-6749标准编写,OAuth2.0标准流程的授权认证,支持openid模式
  • 二级认证 —— 在已登录的基础上再次认证,保证安全性
  • Basic认证 —— 一行代码接入 Http Basic 认证
  • 独立Redis —— 将权限缓存与业务缓存分离
  • 临时Token验证 —— 解决短时间的Token授权问题
  • 模拟他人账号 —— 实时操作任意用户状态数据
  • 临时身份切换 —— 将会话身份临时切换为其它账号
  • 前后台分离 —— APP、小程序等不支持Cookie的终端
  • 同端互斥登录 —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录
  • 多账号认证体系 —— 比如一个商城项目的user表和admin表分开鉴权
  • 花式token生成 —— 内置六种Token风格,还可:自定义Token生成策略、自定义Token前缀
  • 注解式鉴权 —— 优雅的将鉴权与业务代码分离
  • 路由拦截式鉴权 —— 根据路由拦截鉴权,可适配restful模式
  • 自动续签 —— 提供两种Token过期策略,灵活搭配使用,还可自动续签
  • 会话治理 —— 提供方便灵活的会话查询接口
  • 记住我模式 —— 适配[记住我]模式,重启浏览器免验证
  • 密码加密 —— 提供密码加密模块,可快速MD5、SHA1、SHA256、AES、RSA加密
  • 全局侦听器 —— 在用户登陆、注销、被踢下线等关键性操作时进行一些AOP操作
  • 开箱即用 —— 提供SpringMVC、WebFlux等常见web框架starter集成包,真正的开箱即用
  • 更多功能正在集成中… —— 如有您有好想法或者建议,欢迎加群交流

2. 使用

2.1 导入依赖

<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.28.0</version>
</dependency>

2.2 springBoot的简单集成

2.2.1 配置文件

server:
  port: 8001

# Sa-Token配置
sa-token:
  # token名称 (同时也是cookie名称)
  token-name: xc_satoken
  # token有效期,单位s 默认30天, -1代表永不过期
  timeout: 2592000
  # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
  activity-timeout: -1
  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
  is-share: false
  # token风格
  token-style: uuid
  # 是否输出操作日志
  is-log: false

2.2.2 controller

package com.xc.satoken.controller;

import cn.dev33.satoken.stp.StpUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController 

    @PostMapping("/login")
    public String login(String username,String password)
        //模拟登录校验
        if("xx".equals(username) && "123456".equals(password))
            StpUtil.login(1001);
            return "登陆成功";
        
        return "登陆失败";
    

    @GetMapping("/islogin")
    public String test()
        return "当前会话是否登录:" + StpUtil.isLogin();
    


2.2.3 简单登录页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <form action="/test/login" method="post">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="checkbox" name="rememberMe">记住我<br>
        <input type="submit">
    </form>
</body>
</html>

2.3 功能详解

2.3.1 登录认证

那么如何判断一个会话是否登录

框架会在登录成功后给你做个标记,每次登录认证时校验这个标记,有标记者视为已登录,无标记者视为未登录!

2.3.1.1 登录与注销
// 标记当前会话登录的账号id 
// 建议的参数类型:long | int | String, 不可以传入复杂类型,如:User、Admin等等
StpUtil.login(Object id);     

// 当前会话注销登录
StpUtil.logout();

// 获取当前会话是否已经登录,返回true=已登录,false=未登录
StpUtil.isLogin();

// 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`
StpUtil.checkLogin()
NotLoginException
场景值对应常量含义说明
-1NotLoginException.NOT_TOKEN未能从请求中读取到token
-2NotLoginException.INVALID_TOKEN已读取到token,但是token无效
-3NotLoginException.TOKEN_TIMEOUT已读取到token,但是token已经过期
-4NotLoginException.BE_REPLACED已读取到token,但是token已被顶下线
-5NotLoginException.KICK_OUT已读取到token,但是token已被踢下线
登录分析 先校验账号和密码,成功在进行satoken的登陆操作

stpUtil.login(login(Object id)) id账号id

–》stpLogic.login(id);

–》(stpLogic)login(id, new SaLoginModel())

SaLoginModel():登录的配置参数,device(客户端设备标识),isLastingCookie(是否为持久cookie,临时的浏览器关闭就消失),timeout(token有效期)

**(stpLogic)login(id, new SaLoginModel()) **
1. 检查传进来的id是否为空,为空抛异常
2. 检查该id是否被禁用-》先检查该id(xc_satoken(token名称):login:disable:1001(id))是否过期,过期就删除SaTokenDao持久层中的存储,如果在SaTokenDao持久层中的数据集合datamap中有该key说明被禁用,抛异常
3.初始化 loginModel ,从配置类中取值赋给SaLoginModel
4.生成token-》分三者情况①:允许并发登录同时是共享token的,从session中取 ②非并发的要先顶替就账号 ③其余情况并发非共享token,以及从session没取到token,非并发的情况生成token。 根据token的类型创建相应的token,默认uuid
5.获取当前用户的session(User-Session),如果当前session已过期。则删掉持久层中的session,否则取出持久层中的session,如果没有session则创建一个
6.为session续期,
	①获取session剩余存活时间(判断是否过期,过期的话删除持久层中的session,没过期的话获取过期时间,计算过期时间与当前时间的差值求得剩余时间,小于0直接删除过期时间。返回-1(永不过期),-2(已过期),或者剩余时间)。
	②如果剩余存活时间小于配置类设定的能存活时间,则更新持久层中的session的过期时间,当前时间+配置类设定的能存活时间
7.把token记录在session的tokenSignList中,已存在就不添加。更新持久层中的session(好像只是看有没有过期,过期就删掉,并没有更新)
8.存储token和id的映射关系(过期就不存储),存储在持久层的数据集和过期时间集,过期时间为当前时间+可存活时间
9.在当前会话中写入token,token为空不写入,写入当前请求的存储器(属性:当前请求request)中,把加前缀和不加前缀的token都写入存储器(即写入request的属性中)
10.把token写入cookie,如果支持从cookie中取token,则创建cookie给客户端,否则不操作
11.标记当前token的最后操作时间,(临时有效的token)
12.通知监听器登录成功
islogin 调用StpLogic.getLoginIdDefaultNull()
临时登录,从存储器里获取临时登录id
取出token,token为空返回null,从持久层的数据集中获取登录id,过期从持久层中删除
id为空,或在异常表中返回null
检查临时token是否过期,过期返回null
返回登录id
null表示未登录
checkLogin 调用StpLogic.getLoginId();

与islogin差不多,不过是checkLogin在返回null的地方直接抛异常了,同时临时过期的token判断是否自动续签。

2.3.2 权限认证

做权限认证之前要先实现StpInterface接口,来编写自己的权限集合

package com.xc.satoken.config;import cn.dev33.satoken.stp.StpInterface;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.ArrayList;import java.util.List;@Componentpublic class StpInterfaceImpl implements StpInterface     @Override    public List<String> getPermissionList(Object loginId, String loginType)         //真是场景区数据库查,这里就直接模拟        List<String> list = new ArrayList<>();        list.add("user*");//        list.add("user:update");//        list.add("user:delete");//        list.add("user:select");        return list;        @Override    public List<String> getRoleList(Object loginId, String loginType)         List<String> list = new ArrayList<>();        list.add("a*");        list.add("user");        return list;    
2.3.2.1 Permission
// 判断:当前账号是否含有指定权限, 返回true或false
StpUtil.hasPermission("user-update");        

// 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException 
StpUtil.checkPermission("user-update");        

// 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
StpUtil.checkPermissionAnd("user-update", "user-delete");        

// 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
StpUtil.checkPermissionOr("user-update", "user-delete");        

2.3.2.2 role
// 判断:当前账号是否拥有指定角色, 返回true或false
StpUtil.hasRole("super-admin");        

// 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRole("super-admin");        

// 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleAnd("super-admin", "shop-admin");        

// 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] 
StpUtil.checkRoleOr("super-admin", "shop-admin");        

2.3.2.3 支持通配符
// 当拥有 user* 权限时StpUtil.hasPermission("user-add");        // trueStpUtil.hasPermission("user-update");     // trueStpUtil.hasPermission("art-add");         // false// 当拥有 *-delete 权限时StpUtil.hasPermission("user-add");        // falseStpUtil.hasPermission("user-delete");     // trueStpUtil.hasPermission("art-delete");      // true// 当拥有 *.js 权限时StpUtil.hasPermission("index.js");        // trueStpUtil.hasPermission("index.css");       // falseStpUtil.hasPermission("index.html");      // false
2.3.2.4 StpUtil.hasPermission(“user-update”);

-》stpLogic.hasPermission(permission);

-》(stpLogic)hasElement(getPermissionList(), permission)

getPermissionList()->getPermissionList(getLoginId()) //根据id获取权限

-》SaStrategy.me.hasElement.apply(list, element)

-》SaManager.getSaTokenAction().hasElement(list, element)

hasElement(List<String> list, String element)list为null -》falselist.contains(element) ->true模糊匹配 不带*直接equals,带*正则表达式比较
2.3.2.5 StpUtil.checkPermissionAnd(“user-update”, “user-delete”);

-》stpLogic.checkPermissionAnd(permissionArray)

-》checkPermissionAnd(String… permissionArray)

checkPermissionAnd(String... permissionArray)1.getLoginId()获取登录id2.getPermissionList(loginId) 获取该用户权限列表3.循环permissionArray 比较 hasElement(permissionList, permission) ,当有一个没有权限直接抛异常
2.3.2.6 StpUtil.checkPermissionOr(“user-update”, “user-delete”)

类似and只是在循环比较时有一个正确就返回

2.3.2.7 and和or的has调用check
2.3.2.8 role的类似Permission

2.3.3 注销与下线与封禁

2.3.3.2 强制注销
StpUtil.logout(10001);                    // 强制指定账号注销下线 StpUtil.logout(10001, "PC");              // 强制指定账号指定端注销下线 StpUtil.logoutByTokenValue("token");      // 强制指定 Token 注销下线 
2.3.3.3 踢人下线
StpUtil.kickout(10001);                    // 将指定账号踢下线 StpUtil.kickout(10001, "PC");              // 将指定账号指定端踢下线StpUtil.kickoutByTokenValue("token");      // 将指定 Token 踢下线

强制注销 和 踢人下线 的区别在于:

  • 强制注销等价于对方主动调用了注销方法,再次访问会提示:Token无效。
  • 踢人下线不会清除Token信息,而是将其打上特定标记,再次访问会提示:Token已被踢下线。
2.3.3.4 账号封禁

对于违规账号,有时候我们仅仅将其踢下线还是远远不够的,我们还需要对其进行账号封禁防止其再次登录

// 封禁指定账号 // 参数一:账号id// 参数二:封禁时长,单位:秒  (86400秒=1天,此值为-1时,代表永久封禁)StpUtil.disable(10001, 86400); // 获取指定账号是否已被封禁 (true=已被封禁, false=未被封禁) StpUtil.isDisable(10001); // 获取指定账号剩余封禁时间,单位:秒StpUtil.getDisableTime(10001); // 解除封禁StpUtil.untieDisable(10001); 

注意点

对于正在登录的账号,对其账号封禁时并不会使其立刻注销
如果需要将其封禁后立即掉线,可采取先踢再封禁的策略,例如:

// 先踢下线StpUtil.kickout(10001); // 再封禁账号StpUtil.disable(10001, 86400); 
2.3.3.5 分析
StpUtil.logout(10001)

->stpLogic.logout(loginId)

->logout(loginId, null) logout(Object loginId, String device) null注销所有设备

logout(Object loginId, String device) clearTokenCommonMethod(Object loginId, String device, Consumer<String> appendFun, boolean isLogoutSession)①查询持久层是否有该session,过期删除,没有session此账号尚未登录,直接return②循环token签名列表TokenSignList,开始删除相关信息 	a获取TokenSignList里的token	b清理掉[token-last-activity] -》删除[最后操作时间],删除存储器的标记	c从token签名列表移除 	d删除Token-Id映射 & 清除Token-Session		deleteTokenToIdMapping(tokenValue)   删除持久层的数据集dataMap和过期时间集合expireMap		deleteTokenSession(tokenValue)       删除token所属的session		SaManager.getSaTokenListener().doLogout(loginType, loginId, tokenValue) 通知监听器		 账号注销成功③ 尝试注销session session.logoutByTokenSignCountToZero()	tokenSign数量为零时,注销会话 logout()-》从持久库删除session,通知监听器session注销成功
StpUtil.kickout(10001)

->stpLogic.kickout(loginId)

->kickout(loginId, null) kickout(Object loginId, String device)

kickout(Object loginId, String device)
clearTokenCommonMethod(Object loginId, String device, Consumer<String> appendFun, boolean isLogoutSession)
同logout,只是第②步的d不同
	将此 token 标记为已被踢下线 
	updateTokenToIdMapping(tokenValue, NotLoginException.KICK_OUT) 更改 Token 指向的 账号Id 值(-5)
	SaManager.getSaTokenListener().doKickout(loginType, loginId, tokenValue) 通知账号被踢下线
StpUtil.disable(10001, 86400)

-》stpLogic.disable(loginId, disableTime)

​ 1.标注为已被封禁 添加xc_satoken:login:disable:1001标记到持久层的数据集和过期集 getSaTokenDao().set(splicingKeyDisable(loginId), DisableLoginException.BE_VALUE, disableTime)

​ 2.通知监听器 账号[" + loginId + "]被封禁 (解封时间: " + SaFoxUtil.formatDate(date) + ")

2.3.4 注解式鉴权

注册拦截器

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer 
    // 注册Sa-Token的注解拦截器,打开注解式鉴权功能 
    @Override
    public void addInterceptors(InterceptorRegistry registry) 
        // 注册注解拦截器,并排除不需要注解鉴权的接口地址 (与登录拦截器无关)
        registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");    
    

// 登录认证:只有登录之后才能进入该方法 
@SaCheckLogin                        
@RequestMapping("info")
public String info() 
    return "查询用户信息";


// 角色认证:必须具有指定角色才能进入该方法 
@SaCheckRole("super-admin")        
@RequestMapping("add")
public String add() 
    return "用户增加";


// 权限认证:必须具有指定权限才能进入该方法 
@SaCheckPermission("user-add")        
@RequestMapping("add")
public String add() 
    return "用户增加";


// 二级认证:必须二级认证之后才能进入该方法 
@SaCheckSafe()        
@RequestMapping("add")
public String add() 
    return "用户增加";


// Http Basic 认证:只有通过 Basic 认证后才能进入该方法 
@SaCheckBasic(account = "sa:123456")
@RequestMapping("add")
public String add() 
    return "用户增加";


注意

使用拦截器模式,只能在Controller层进行注解鉴权

分析

1.Method method = ((HandlerMethod)handler).getMethod(); 获取当前调用的方法

2.SaStrategy.me.checkMethodAnnotation.accept(method); 对该方法上的注解进行检查

Consumer<Method> checkMethodAnnotation
1.先校验 Method 所属 Class 上的注解    me.checkElementAnnotation.accept(method.getDeclaringClass());
2.再校验 Method 上的注解    me.checkElementAnnotation.accept(method);

上述两个方法共同调用了SaStrategy的Consumer<AnnotatedElement> checkElementAnnotation方法
-》SaManager.getSaTokenAction().validateAnnotation(element)
	validateAnnotation(AnnotatedElement target)
	①比较该类或方法是否有@SaCheckLogin 注解 有的话调用StpLogic的checkLogin()方法-》getLoginId()
	②判断是否有@SaCheckRole 注解  根据模式SaMode(AND,OR,默认为AND)判断调用checkRoleAnd(roleArray)还是checkPermissionOr(permissionArray)
	③判断是否有@SaCheckPermission注解  根据模式SaMode(AND,OR,默认为AND)判断调用checkPermissionAnd(permissionArray)还是checkRoleOr(roleArray)。权限验证不通过,判断是否开启orRole()角色判断,开启判断,则进行角色校验hasRoleAnd(rArr)
	④判断是否有@SaCheckSafe 注解  执行checkSafe-》调用StpLogic的 isSafe() == false抛异常
		isSafe()-》获取tokensession-》在tokensession中获取SAFE_AUTH_SAVE_KEY_的值,没有返回false,否则返回true
		tokenSession-》如果配置了需要校验登录状态(tokenSessionCheckLogin),则验证一下checkLogin()-》从持久层中获取tokensession,没有则创建一个,并存入持久层
	⑤判断是否有@SaCheckBasic 注解 
	check(String realm, String account)-》saBasicTemplate.check(realm, account):获取请求头中的authorization信息,与account对比,错误抛出异常

附录

简单测试 controller

package com.xc.satoken.controller;

import cn.dev33.satoken.annotation.*;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/user")
@RestController
public class UserController 
    @PostMapping("/login")
    public String login(String username,String password)
        //模拟登录校验
        if("xx".equals(username) && "123456".equals(password))
            StpUtil.login(1001);
            return "登陆成功";
        
        return "登陆失败";
    

    @GetMapping("/isLogin")
    public String islogin()
        return "当前会话是否登录:" + StpUtil.isLogin();
    


    @GetMapping("/checkLogin")
    pub

SA-Token授权 鉴权中心微服务

授权 鉴权中心微服务
1 什么是JWT
1.2 JWT 的基本概念

1.3 JSON Web Token jwt 是一个开放标准 它定义了一种紧凑的、自包含的方式 用于作为JSON 对象在各方之间安全地传输信息

1.4.那些场景下可以考虑使用JWT ?

​ 1.用户授权 信息交换

1.5 JWT的结构及其含义

​ 1.JWT 由三个部分组成 Header、Payload Signature 且用圆点连接

​ 2.Header 由两部分(Token类型 加密算法名称)组成,并使用Base64 编码

​ 3.Payload kv形式的数据 即你想传递的数据 (授权的话就是token 信息)

​ 4.Signature 为了得到签名部分 你必须有编码过的Header 编码过的payload 一个秘钥 签名算法是Header 中指定的那个 然对它们签名即可

我以前的公司使用的是JWT+SpringSecurity 做的鉴权和权限中心 现在我最近公司的微服务项目采用的SA—Token 所以在该Demo 当中直接采取Sa-Token 方式 去授权和鉴权的服务

2 授权和鉴权服务设计 分为好几种方案
2.1 、全局鉴权中心

有些微服务项目弄了个全局的鉴权中心,所有微服务接收HTTP调用之后,先去全局鉴权中心验证用户的身份和权限,然后再允许用户调用微服务。

这种缺点 我个人认为 发送了多次http请求 其二 每个服务都与鉴权服务进行耦合 耦合度有点深,

二、BFF鉴权
BFF全称是Backends For Frontends,直译过来就是“服务于前端的后端”。 简而言之,BFF就是设计后端微服务API接口时,考虑到不同设备的需求,为不同的设备提供不同的API接口。

客户端不是直接访问服务器的公共接口,而是调用BFF层提供的接口,BFF层再调用基础的服务,不同的客户端拥有不同的BFF层,它们定制客户端需要的API接口。

有了BFF层之后,客户端只需要发起一次HTTP请求,BFF层就能调用不同的服务,然后把汇总后的数据返回给客户端,这样就减少了外网的HTTP请求,响应速度也就更快。

跨横切面(Cross-Cutting Concerns)的功能,需要协调更新框架升级发版(路由、认证、限流、安全),因此全部上沉,引入了 API Gateway,把业务集成度高的 BFF 层和通用功能服务层 API Gateway 进行了分层处理。

在新的架构中,网关承担了重要的角色,它是解耦拆分和后续升级迁移的利器。

在网关的配合下,单块 BFF 实现了解耦拆分,各业务线团队可以独立开发和交付各自的微服务,研发效率大大提升。

BFF的划分:

重要性
垂直业务,闭环
流量大小
另外,把跨横切面逻辑从 BFF 剥离到网关上去以后,BFF 的开发人员可以更加专注业务逻辑交付,实现了架构上的关注分离(Separation of Concerns)。

我们业务流量实际为:
移动端 -> API Gateway -> BFF -> Mircoservice

在 FE Web业务中,BFF 可以是 nodejs 来做服务端渲染(SSR,Server-Side Rendering),注意这里忽略了上游的 CDN、4/7层负载均衡(ELB)。

总结

基于服务器的身份认证
最为传统的做法 客服端存储cookie 一般是session id 服务器存储Session
Session 是每次用户认证通过以后 服务器需要创建一条记录保存用户信息 通常是在内存中 随着认证通过的用户越来越多 服务器的开销越来越大
在不同域名之前切换时 请求可能会被禁止 跨域问题
基于token方式身份认证
JWT 与Session 的差异相同点是 它们都是存储用户信息 然而Session 存在服务器端 而JWT 是在客户端
jwt 方式将用户状态分散到了客户端中 可以明显减轻服务端的内存压力
在不同域名之前切换时 请求可能会被禁止 跨域问题
基于token 与服务器身份认证
解析方式 jwt 使用算法直接解析得到用户信息 Session 需要额外的数据映射 实现匹配
jwt 只有过期时间的限制 session 保存在服务器 可控性强大
跨平台 jwt 就是一个string 可以任意传送 Session 跨平台需要一个统一解析方式
时效性 jwt一旦生成 独立存在 很难做特殊控制 Session 时效性可以由服务端控制
代码如下

以上是关于Sa-token简单介绍和基本使用的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot:Sa-Token的具体介绍与使用

Sa-Token简单使用

SA-Token授权 鉴权中心微服务

一文详解 Sa-Token 中的 SaSession 对象

【SpringCloud-Alibaba系列教程】13.gateway网关结合Sa-token进行登录鉴权

Sa-Token