diff --git a/README.md b/README.md index 226138be..6dc4acc7 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,14 @@ Sa-Token是一个轻量级Java权限认证框架,主要解决:登录认证 框架集成简单、开箱即用、API设计清爽,通过Sa-Token,你将以一种极其简单的方式实现系统的权限认证部分 -- **登录验证** —— 单端登录、多端登录、同端互斥登录、七天内免登录 -- **权限验证** —— 权限认证、角色认证、会话二级认证 +- **登录认证** —— 单端登录、多端登录、同端互斥登录、七天内免登录 +- **权限认证** —— 权限认证、角色认证、会话二级认证 - **Session会话** —— 全端共享Session、单端独享Session、自定义Session - **踢人下线** —— 根据账号id踢人下线、根据Token值踢人下线 - **账号封禁** —— 指定天数封禁、永久封禁、设定解封时间 - **持久层扩展** —— 可集成Redis、Memcached等专业缓存中间件,重启数据不丢失 - **分布式会话** —— 提供jwt集成、共享数据中心两种分布式会话方案 -- **微服务网关鉴权** —— 适配Gateway、Soul、Zuul等常见网关的路由拦截认证 +- **微服务网关鉴权** —— 适配Gateway、ShenYu、Zuul等常见网关的路由拦截认证 - **单点登录** —— 内置三种单点登录模式:无论是否跨域、是否共享Redis,都可以搞定 - **二级认证** —— 在已登录的基础上再次认证,保证安全性 - **独立Redis** —— 将权限缓存与业务缓存分离 diff --git a/sa-token-doc/doc/start/download.md b/sa-token-doc/doc/start/download.md index 03710a89..14de7095 100644 --- a/sa-token-doc/doc/start/download.md +++ b/sa-token-doc/doc/start/download.md @@ -1,4 +1,4 @@ -# 集成 +# 集成 Sa-Token ------ @@ -73,7 +73,7 @@ implementation 'cn.dev33:sa-token-core:${sa.top.version}' ``` -注:JDK版本:`v1.8+` +注:JDK版本:`v1.8+`,SpringBoot:`建议2.0以上` ## 获取源码 diff --git a/sa-token-doc/doc/start/example.md b/sa-token-doc/doc/start/example.md index 1ec2abc9..602d282f 100644 --- a/sa-token-doc/doc/start/example.md +++ b/sa-token-doc/doc/start/example.md @@ -9,7 +9,7 @@ 在IDE中新建一个SpringBoot项目,例如:`sa-token-demo-springboot`(不会的同学请自行百度或者参考github示例) -### 2、设置依赖 +### 2、添加依赖 在 `pom.xml` 中添加依赖: ``` xml diff --git a/sa-token-doc/doc/start/webflux-example.md b/sa-token-doc/doc/start/webflux-example.md index 80b5b330..fd6a8bee 100644 --- a/sa-token-doc/doc/start/webflux-example.md +++ b/sa-token-doc/doc/start/webflux-example.md @@ -1,17 +1,19 @@ # Spring WebFlux 集成 Sa-Token 示例 -WebFlux基于Reactor响应式模型开发,有着与标准ServletAPI完全不同的底层架构,因此要适配WebFlux, 必须提供与Reactor相关的整合实现, -本篇将以WebFlux为例,展示Sa-Token与Reactor响应式模型web框架相整合的示例, **你可以用同样方式去对接其它Reactor模型Web框架**(Netty、ShenYu、Gateway等) +**Reactor** 是一种非阻塞的响应式模型,本篇将 **WebFlux** 以为例,展示 Sa-Token 与 Reactor 响应式模型架相整合的示例, +**你可以用同样方式去对接其它Reactor模型框架(Netty、ShenYu、SpringCloud Gateway等)** 整合示例在官方仓库的`/sa-token-demo/sa-token-demo-webflux`文件夹下,如遇到难点可结合源码进行测试学习 +!> WebFlux常用于微服务网关架构中,如果您的应用基于单体架构且非 Reactor 模型,可以先跳过本章 + --- ### 1、创建项目 在IDE中新建一个SpringBoot项目,例如:`sa-token-demo-webflux`(不会的同学请自行百度或者参考github示例) -### 2、设置依赖 +### 2、添加依赖 在 `pom.xml` 中添加依赖: ``` xml @@ -24,7 +26,7 @@ WebFlux基于Reactor响应式模型开发,有着与标准ServletAPI完全不 ``` -### 4、创建启动类 +### 3、创建启动类 在项目中新建包 `com.pj` ,在此包内新建主类 `SaTokenDemoApplication.java`,输入以下代码: ``` java @@ -37,7 +39,7 @@ public class SaTokenDemoApplication { } ``` -### 5、创建全局过滤器 +### 4、创建全局过滤器 新建`SaTokenConfigure.java`,注册Sa-Token的全局过滤器 ``` java /** @@ -72,7 +74,7 @@ public class SaTokenConfigure { ?> 你只需要按照此格式复制代码即可,有关过滤器的详细用法,会在之后的章节详细介绍 -### 6、创建测试Controller +### 5、创建测试Controller ``` java @RestController @RequestMapping("/user/") @@ -98,7 +100,7 @@ public class UserController { } ``` -### 7、运行 +### 6、运行 启动代码,从浏览器依次访问上述测试接口: ![运行结果](https://oss.dev33.cn/sa-token/doc/test-do-login.png) diff --git a/sa-token-doc/doc/use/jur-auth.md b/sa-token-doc/doc/use/jur-auth.md index 56549ee4..a0efa60e 100644 --- a/sa-token-doc/doc/use/jur-auth.md +++ b/sa-token-doc/doc/use/jur-auth.md @@ -16,7 +16,7 @@ ### 获取当前账号权限码集合 因为每个项目的需求不同,其权限设计也千变万化,因此【获取当前账号权限码集合】这一操作不可能内置到框架中, -所以`Sa-Token`将此操作以接口的方式暴露给你,以方便的你根据自己的业务逻辑进行重写 +所以 Sa-Token 将此操作以接口的方式暴露给你,以方便的你根据自己的业务逻辑进行重写 你需要做的就是新建一个类,实现`StpInterface`接口,例如以下代码: @@ -156,5 +156,62 @@ StpUtil.hasPermission("index.html"); // false 注意:以上写法只为提供一个参考示例,不同框架有不同写法,开发者可根据项目技术栈灵活封装进行调用 + ### 前端有了鉴权后端还需要鉴权吗? -**需要!前端的鉴权只是一个辅助功能,对于专业人员这些限制都是可以轻松绕过的,为保证服务器安全,无论前端是否进行了权限校验,后端接口都需要对会话请求再次进行权限校验!** +**需要!** + +前端的鉴权只是一个辅助功能,对于专业人员这些限制都是可以轻松绕过的,为保证服务器安全,无论前端是否进行了权限校验,后端接口都需要对会话请求再次进行权限校验! + + +### 将权限数据放在缓存里 +前面我们讲解了通过`StpInterface`接口将权限数据注入到框架中,框架默认是不提供缓存能力的, +如果每次权限校验都要查库操作,势必会对数据库造成不小的压力,为了解决这个问题,我们需要将权限数据放到缓存中 + +参考示例: +``` java +/** + * 返回一个账号所拥有的权限码集合 + */ +@Override +public List getPermissionList(Object loginId, String loginType) { + + // 1. 获取这个账号所属角色id + long roleId = StpUtil.getSessionByLoginId(loginId).get("Role_Id", () -> { + return ...; // 从数据库查询这个账号所属的角色id + }); + + // 2. 获取这个角色id拥有的权限列表 + SaSession roleSession = SaSessionCustomUtil.getSessionById("role-" + roleId); + List list = roleSession.get("Permission_List", () -> { + return ...; // 从数据库查询这个角色id拥有的权限列表 + }); + + // 3. 返回 + return list; +} +``` +以上仅为代码示例,角色列表步骤同理 + +##### 疑问:为什么不直接缓存 `[账号id->权限列表]`的关系,而是 `[账号id -> 角色id -> 权限列表]`? + + + +答:`[账号id->权限列表]`的缓存方式虽然更加直接粗暴,却有一个严重的问题: + +- 通常我们系统的权限架构是RBAC模型:权限与用户没有直接的关系,而是:用户拥有指定的角色,角色再拥有指定的权限 +- 而这种'拥有关系'是动态的,是可以随时修改的,一旦我们修改了它们的对应关系,便要同步修改或清除对应的缓存数据 + +现在假设如下业务场景:我们系统中有十万个账号属于同一个角色,当我们变动这个角色的权限时,难道我们要同时清除这十万个账号的缓存信息吗? +这显然是一个不合理的操作,同一时间缓存大量清除容易引起Redis的缓存雪崩 + +而当我们采用 `[账号id -> 角色id -> 权限列表]` 的缓存模型时,则只需要清除或修改 `[角色id -> 权限列表]` 一条缓存即可 + +一言以蔽之:权限的缓存模型需要跟着权限模型走,角色缓存亦然 + + diff --git a/sa-token-doc/doc/use/login-auth.md b/sa-token-doc/doc/use/login-auth.md index 8e8a9f43..82c8eb50 100644 --- a/sa-token-doc/doc/use/login-auth.md +++ b/sa-token-doc/doc/use/login-auth.md @@ -4,8 +4,8 @@ ### 核心思想 -所谓登录认证,说白了就是限制某些接口只有登录后才能访问(如:查询我的账号资料)
-那么判断一个会话是否登录的依据是什么?当然是登录成功后框架给你做个标记!然后在需要鉴权的接口里检查此标记,有标记者视为已登录,无标记者视为未登录! +所谓登录认证,说白了就是限制某些API接口必须登录后才能访问(例:查询我的账号资料)
+那么如何判断一个会话是否登录?框架会在登录成功后给你做个标记,每次登录认证时校验这个标记,有标记者视为已登录,无标记者视为未登录! ### 登录与注销 @@ -26,33 +26,34 @@ StpUtil.isLogin(); StpUtil.checkLogin() ``` -扩展:`NotLoginException` 对象可通过 `getLoginType()` 方法获取具体是哪个 `StpLogic` 抛出的异常
-扩展:`NotLoginException` 对象可通过 `getType()` 方法获取具体的场景值,详细参考章节:[未登录场景值](/fun/not-login-scene) +##### `NotLoginException`异常对象扩展: +1. 通过 `getLoginType()` 方法获取具体是哪个 `StpLogic` 抛出的异常
+2. 通过 `getType()` 方法获取具体的场景值,详细参考章节:[未登录场景值](/fun/not-login-scene) ### 会话查询 ``` java -// 获取当前会话登录id, 如果未登录,则抛出异常:`NotLoginException` +// 获取当前会话账号id, 如果未登录,则抛出异常:`NotLoginException` StpUtil.getLoginId(); // 类似查询API还有: -StpUtil.getLoginIdAsString(); // 获取当前会话登录id, 并转化为`String`类型 -StpUtil.getLoginIdAsInt(); // 获取当前会话登录id, 并转化为`int`类型 -StpUtil.getLoginIdAsLong(); // 获取当前会话登录id, 并转化为`long`类型 +StpUtil.getLoginIdAsString(); // 获取当前会话账号id, 并转化为`String`类型 +StpUtil.getLoginIdAsInt(); // 获取当前会话账号id, 并转化为`int`类型 +StpUtil.getLoginIdAsLong(); // 获取当前会话账号id, 并转化为`long`类型 // ---------- 指定未登录情形下返回的默认值 ---------- -// 获取当前会话登录id, 如果未登录,则返回null +// 获取当前会话账号id, 如果未登录,则返回null StpUtil.getLoginIdDefaultNull(); -// 获取当前会话登录id, 如果未登录,则返回默认值 (`defaultValue`可以为任意类型) +// 获取当前会话账号id, 如果未登录,则返回默认值 (`defaultValue`可以为任意类型) StpUtil.getLoginId(T defaultValue); ``` ### 其它API ``` java -// 获取指定token对应的登录id,如果未登录,则返回 null +// 获取指定token对应的账号id,如果未登录,则返回 null StpUtil.getLoginIdByToken(String tokenValue); // 获取当前`StpLogic`的token名称 @@ -65,6 +66,6 @@ StpUtil.getTokenValue(); StpUtil.getTokenInfo(); ``` -?> 有关TokenInfo参数详解,请参考:[参考:TokenInfo参数详解](/fun/token-info) +有关`TokenInfo`参数详解,请参考:[TokenInfo参数详解](/fun/token-info)