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

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

本篇将介绍 Sa-Token 中的多账号认证操作。

一、需求分析

有的时候,我们会在一个项目中设计两套账号体系,比如一个电商系统的 user表 和 admin表, 在这种场景下,如果两套账号我们都使用 StpUtil 类的API进行登录鉴权,那么势必会发生逻辑冲突。

在Sa-Token中,这个问题的模型叫做:多账号体系认证。

要解决这个问题,我们必须有一个合理的机制将这两套账号的授权给区分开,让它们互不干扰才行。

二、演进思路

假如说我们的 user表 和 admin表 都有一个 id=10001 的账号,它们对应的登录代码:StpUtil.login(10001) 是一样的,

那么问题来了:在StpUtil.getLoginId()获取到的账号id如何区分它是User用户,还是Admin用户?

你可能会想到为他们加一个固定前缀,比如StpUtil.login("User_" + 10001)、StpUtil.login("Admin_" + 10001),这样确实是可以解决问题的,

但是同样的:你需要在StpUtil.getLoginId()时再裁剪掉相应的前缀才能获取真正的账号id,这样一增一减就让我们的代码变得无比啰嗦。

那么,有没有从框架层面支持的,更优雅的解决方案呢?

SaManager 提供了动态创建 StpLogic 的能力:

// 在当前会话登录 Admin 账号 10001
SaManager.getStpLogic("admin").login(10001); // 在当前会话登录 User 账号 10001
SaManager.getStpLogic("user").login(10001);

这是一种解决方案,但仍然需要我们每次调用方法时指定账号类型参数,代码略显啰嗦,接下来我们介绍方案二:自定义 StpUtil 鉴权工具类。

三、自定义 StpUtil 鉴权工具类

前面几篇介绍的api调用,都是经过 StpUtil 类的各种静态方法进行授权认证,

而如果我们深入它的源码,点此阅览

就会发现,此类并没有任何代码逻辑,唯一做的事就是对成员变量stpLogic的各个API包装一下进行转发。

这样做有两个优点:

  • StpLogic 类的所有函数都可以被重写,按需扩展。
  • 在构造方法时随意传入一个不同的 loginType,就可以再造一套账号登录体系。

四、操作示例

比如说,对于原生StpUtil类,我们只做admin账号权限认证,而对于user账号,我们则:

  1. 新建一个新的权限认证类,比如: StpUserUtil.java。
  2. 将StpUtil.java类的全部代码复制粘贴到 StpUserUtil.java里。
  3. 更改一下其 LoginType, 比如:
public class StpUserUtil {

	/**
* 账号体系标识
*/
public static final String TYPE = "user"; // 将 LoginType 从`login`改为`user` // 其它代码 ... }
  1. 接下来就可以像调用StpUtil.java一样调用 StpUserUtil.java了,这两套账号认证的逻辑是完全隔离的。

成品样例参考:码云 StpUserUtil.java

五、在多账户模式下使用注解鉴权

框架默认的注解鉴权 如@SaCheckLogin 只针对原生StpUtil进行鉴权。

例如,我们在一个方法上加上@SaCheckLogin注解,这个注解只会放行通过StpUtil.login(id)进行登录的会话,

而对于通过StpUserUtil.login(id)进行登录的会话,则始终不会通过校验。

那么如何告诉@SaCheckLogin要鉴别的是哪套账号的登录会话呢?很简单,你只需要指定一下注解的type属性即可:

// 通过type属性指定此注解校验的是我们自定义的`StpUserUtil`,而不是原生`StpUtil`
@SaCheckLogin(type = StpUserUtil.TYPE)
@RequestMapping("info")
public String info() {
return "查询用户信息";
}

注:@SaCheckRole("xxx")、@SaCheckPermission("xxx")同理,亦可根据type属性指定其校验的账号体系,此属性默认为"",代表使用原生StpUtil账号体系。

六、使用注解合并简化代码

交流群里有同学反应,虽然可以根据 @SaCheckLogin(type = "user") 指定账号类型,但几十上百个注解都加上这个的话,还是有些繁琐,代码也不够优雅,有么有更简单的解决方案?

我们期待一种[注解继承/合并]的能力,即:自定义一个注解,标注上@SaCheckLogin(type = "user"),

然后在方法上标注这个自定义注解,效果等同于标注@SaCheckLogin(type = "user")。

很遗憾,JDK默认的注解处理器并没有提供这种[注解继承/合并]的能力,不过好在我们可以利用 Spring 的注解处理器,达到同样的目的。

1、重写Sa-Token默认的注解处理器:
@Configuration
public class SaTokenConfigure {
@Autowired
public void rewriteSaStrategy() {
// 重写Sa-Token的注解处理器,增加注解合并功能
SaStrategy.me.getAnnotation = (element, annotationClass) -> {
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
};
}
}
2、自定义一个注解:
/**
* 登录认证(User版):只有登录之后才能进入该方法
* <p> 可标注在函数、类上(效果等同于标注在此类的所有方法上)
*/
@SaCheckLogin(type = "user")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE})
public @interface SaUserCheckLogin { }
3、接下来就可以使用我们的自定义注解了:
// 使用 @SaUserCheckLogin 的效果等同于使用:@SaCheckLogin(type = "user")
@SaUserCheckLogin
@RequestMapping("info")
public String info() {
return "查询用户信息";
}

注:其它注解 @SaCheckRole("xxx")、@SaCheckPermission("xxx")同理,

完整示例参考:码云:自定义注解。

七、同端多登陆

假设我们不仅需要在后台同时集成两套账号,我们还需要在一个客户端同时登陆两套账号(业务场景举例:一个APP中可以同时登陆商家账号和用户账号)。

如果我们不做任何特殊处理的话,在客户端会发生token覆盖,新登录的token会覆盖掉旧登录的token从而导致旧登录失效。

那么如何解决这个问题?

很简单,我们只要更改一下 StpUserUtil 的 TokenName 即可,参考示例如下:

public class StpUserUtil {

	// 使用匿名子类 重写`stpLogic对象`的一些方法
public static StpLogic stpLogic = new StpLogic("user") {
// 重写 StpLogic 类下的 `splicingKeyTokenName` 函数,返回一个与 `StpUtil` 不同的token名称, 防止冲突
@Override
public String splicingKeyTokenName() {
return super.splicingKeyTokenName() + "-user";
}
// 同理你可以按需重写一些其它方法 ...
}; // ... }

再次调用 StpUserUtil.login(10001) 进行登录授权时,token的名称将不再是 satoken,而是我们重写后的 satoken-user。

八、不同体系不同 SaTokenConfig 配置

如果自定义的 StpUserUtil 需要使用不同 SaTokenConfig 对象, 也很简单,参考示例如下:

public class StpUserUtil {

	// 使用匿名子类 重写`stpLogic对象`的一些方法
public static StpLogic stpLogic = new StpLogic("user") { // 首先自定义一个 Config 对象
SaTokenConfig config = new SaTokenConfig()
.setTokenName("satoken")
.setTimeout(2592000)
// ... 其它set
; // 然后重写 stpLogic 配置获取方法
@Override
public SaTokenConfig getConfig() {
return config;
}
}; // ... }

九、多账号体系混合鉴权

QQ群中有小伙伴提问:在多账号体系下,怎么在 SaInterceptor 拦截器中给一个接口登录鉴权?

其实这个问题,主要是靠你的业务需求来决定,以后台 Admin 账号和前台 User 账号为例:

// 注册 Sa-Token 拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor(handle -> { // 如果这个接口,要求客户端登录了后台 Admin 账号才能访问:
SaRouter.match("/art/getInfo").check(r -> StpUtil.checkLogin()); // 如果这个接口,要求客户端登录了前台 User 账号才能访问:
SaRouter.match("/art/getInfo").check(r -> StpUserUtil.checkLogin()); // 如果这个接口,要求客户端同时登录 Admin 和 User 账号,才能访问:
SaRouter.match("/art/getInfo").check(r -> {
StpUtil.checkLogin();
StpUserUtil.checkLogin();
}); // 如果这个接口,要求客户端登录 Admin 和 User 账号任意一个,就能访问:
SaRouter.match("/art/getInfo").check(r -> {
if(StpUtil.isLogin() == false && StpUserUtil.isLogin() == false) {
throw new SaTokenException("请登录后再访问接口");
}
}); })).addPathPatterns("/**");
}

参考资料

Sa-Token 多账号认证:同时为系统的 Admin 账号和 User 账号提供鉴权操作的更多相关文章

  1. 基于Springboot集成security、oauth2实现认证鉴权、资源管理

    1.Oauth2简介 OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAu ...

  2. 深入理解k8s中的访问控制(认证、鉴权、审计)流程

    Kubernetes自身并没有用户管理能力,无法像操作Pod一样,通过API的方式创建/删除一个用户实例,也无法在etcd中找到用户对应的存储对象. 在Kubernetes的访问控制流程中,用户模型是 ...

  3. .Net Core使用Ocelot网关(二) -鉴权认证

    前言 上一章已经简单的介绍了ocelot的使用了,但是网关暴露的接口如果什么人都能访问的话安全性就太低啦.所以我们需要去鉴权和认证.这里我们使用identityServer4给我们的网关来鉴权认证. ...

  4. .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析

    介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...

  5. 基于Token的身份认证 与 基于服务器的身份认证

    基于Token的身份认证 与 基于服务器的身份认证 基于服务器的身份认证 在讨论基于Token的身份认证是如何工作的以及它的好处之前,我们先来看一下以前我们是怎么做的: HTTP协议是无状态的,也就是 ...

  6. JWT对SpringCloud进行系统认证和服务鉴权

    JWT对SpringCloud进行系统认证和服务鉴权 一.为什么要使用jwt?在微服务架构下的服务基本都是无状态的,传统的使用session的方式不再适用,如果使用的话需要做同步session机制,所 ...

  7. 5分钟搞懂:基于token的用户认证

    https://www.qikegu.com/easy-understanding/880 用户认证 用户认证或者说用户登录是确认某人确实是某人的过程,生活中靠身份证,网络上就要靠账号和密码.用户提供 ...

  8. 在ASP.NET Core中使用Angular2,以及与Angular2的Token base身份认证

    注:下载本文提到的完整代码示例请访问:How to authorization Angular 2 app with asp.net core web api 在ASP.NET Core中使用Angu ...

  9. Python+Django+SAE系列教程17-----authauth (认证与授权)系统1

    通过session,我们能够在多次浏览器请求中保持数据,接下来的部分就是用session来处理用户登录了. 当然,不能仅凭用户的一面之词,我们就相信,所以我们须要认证. 当然了,Django 也提供了 ...

  10. Linux下锁定账号,禁止登录系统的设置总结【转】

    在我们运维工作中,会经常要求一些用户不允许登陆系统,以加固系统安全.今天这里介绍下锁定账号登陆的几种方法: (推荐使用)这种方式会更加人性化一点,因为不仅可以禁止用户登录,还可以在禁用登陆时给提示告诉 ...

随机推荐

  1. 如何遍历HashMap集合?

    在Java中,HashMap是一种常用的数据结构,它提供了快速的查找.插入和删除操作.当我们需要遍历HashMap中的所有元素时,可以利用三种不同的方法实现. 方法一:使用键值对遍历 HashMap中 ...

  2. ARL:资产侦察灯塔系统

    资产灯塔,不仅仅是域名收集 功能简介 "挖洞神器"资产安全灯塔(ARL),旨在快速侦察与目标关联的互联网资产,构建基础资产信息库. 协助甲方安全团队或者渗透测试人员有效侦察和检索资 ...

  3. CefSharp自定义缓存实现

    大家好,我是沙漠尽头的狼. 上文介绍了<C#使用CefSharp内嵌网页-并给出C#与JS的交互示例>,本文介绍CefSharp的缓存实现,先来说说添加缓存的好处: 提高页面加载加速:Ce ...

  4. 第一章:PyTorch 入门

    第一章:PyTorch 入门 1.1 Pytorch 简介 1.1.1 PyTorch的由来 1.1.2 Torch是什么? 1.1.3 重新介绍 PyTorch 1.1.4 对比PyTorch和Te ...

  5. MySQL WorkBench更换界面成中文的方法

    菜单页面更换 文章目录 菜单页面更换 汉化文件的xml文件我放在下面的网盘中了 1.找到MySQL的安装位置, 总结 汉化文件的xml文件我放在下面的网盘中了 1.找到MySQL的安装位置, 具体安装 ...

  6. Python数据分析中 melt()函数的一些用法

    melt()函数是一个数据重塑工具,用于将宽格式数据转换为长格式数据(Unpivot a DataFrame from wide to long format, optionally leaving ...

  7. Element-DatePicker的宽度

    Element如何修改DatePicker的宽度 方法/步骤 1 打开一个vue文件,添加DatePicker日期选择器组件,设置默认日期为null.如图 2 在组件上添加style样式属性,设置wi ...

  8. weexplus监听android返回按钮

    看见了平台 https://weexplus.github.io/doc/mo-kuai/pageye-mian-kong-zhi-566829.html的这段代码 var page=weex.req ...

  9. ir_ui_view: 字段 `group_display_incoterm` 不存在

    ir_ui_view: 字段 `group_display_incoterm` 不存在 这种提示是没有这个字段, IR_UI_VIEW 模型存在这个视图: 1 可以登陆数据库,查询 SELECT * ...

  10. 图解MySQL在Linux下的安装与配置

    MySQL简介 MySQL是最流行的RDBMS(Relational Database Management System:关系数据库管理系统)之一,被广泛地应用在互联网上的中小型网站中.关联数据库将 ...