完善新版本特性文档

This commit is contained in:
click33 2021-10-11 01:09:28 +08:00
parent d491f4083f
commit 3bec16627e
23 changed files with 352 additions and 232 deletions

View File

@ -56,8 +56,8 @@ public String insert(SysUser user) {
将某个账号踢下线(待到对方再次访问系统时会抛出`NotLoginException`异常)
``` java
// 使账号id为 10001 的会话强制注销登录
StpUtil.logoutByLoginId(10001);
// 将账号id为 10001 的会话踢下线
StpUtil.kickout(10001);
```
在 Sa-Token 中,绝大多数功能都可以 **一行代码** 完成:
@ -66,14 +66,14 @@ StpUtil.login(10001); // 标记当前会话登录的账号id
StpUtil.getLoginId(); // 获取当前会话登录的账号id
StpUtil.isLogin(); // 获取当前会话是否已经登录, 返回true或false
StpUtil.logout(); // 当前会话注销登录
StpUtil.logoutByLoginId(10001); // 让账号为10001的会话注销登录踢人下线
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.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响)
StpUtil.kickout(10001, "PC"); // 指定账号指定设备标识踢下线 (不同端不受影响)
StpUtil.openSafe(120); // 在当前会话开启二级认证有效期为120秒
StpUtil.checkSafe(); // 校验当前会话是否处于二级认证有效期内,校验失败会抛出异常
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号

View File

@ -111,7 +111,7 @@ public class SaManager {
}
/**
* 容器操作 Bean
* 上下文 Bean
*/
private volatile static SaTokenContext saTokenContext;
public static void setSaTokenContext(SaTokenContext saTokenContext) {

View File

@ -42,8 +42,8 @@ public @interface SaCheckPermission {
* </p>
*
* <p>
* 例2 orRole={"admin", "manager", "staff"}具有三个角色其一即可 <br>
* 例3 orRole={"admin, manager, staff"}必须三个角色同时具备
* 例2 orRole = {"admin", "manager", "staff"}具有三个角色其一即可 <br>
* 例3 orRole = {"admin, manager, staff"}必须三个角色同时具备
* </p>
*/
String[] orRole() default {};

View File

@ -1,7 +1,6 @@
package com.pj.satoken;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotatedElementUtils;
@ -10,7 +9,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.pj.util.AjaxJson;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
@ -77,12 +75,12 @@ public class SaTokenConfigure implements WebMvcConfigurer {
/**
* 重写 Sa-Token 框架内部算法策略
*/
@PostConstruct
@Autowired
public void rewriteSaStrategy() {
// 重写Sa-Token的注解处理器增加注解合并功能
SaStrategy.me.setGetAnnotation((element, annotationClass) -> {
return AnnotatedElementUtils.getMergedAnnotation(element, SaCheckLogin.class);
});
SaStrategy.me.getAnnotation = (element, annotationClass) -> {
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
};
}
}

View File

@ -14,7 +14,7 @@
</p>
<p>登录之后才能显示:<span sa:login>value</span></p>
<p>不登录之后才能显示:<span sa:notLogin>value</span></p>
<p>不登录才能显示:<span sa:notLogin>value</span></p>
<p>具有角色 admin 才能显示:<span sa:hasRole="admin">value</span></p>
<p>同时具备多个角色才能显示:<span sa:hasRoleAnd="admin, ceo, cto">value</span></p>
@ -28,7 +28,7 @@
<p th:if="${stp.isLogin()}">
从SaSession中取值
<span th:text="${stp.getSession().get('name', )}"></span>
<span th:text="${stp.getSession().get('name', '')}"></span>
</p>
</div>

View File

@ -58,8 +58,8 @@ public String insert(SysUser user) {
将某个账号踢下线(待到对方再次访问系统时会抛出`NotLoginException`异常)
``` java
// 使账号id为 10001 的会话强制注销登录
StpUtil.logoutByLoginId(10001);
// 将账号id为 10001 的会话踢下线
StpUtil.kickout(10001);
```
在 Sa-Token 中,绝大多数功能都可以 **一行代码** 完成:
@ -68,14 +68,14 @@ StpUtil.login(10001); // 标记当前会话登录的账号id
StpUtil.getLoginId(); // 获取当前会话登录的账号id
StpUtil.isLogin(); // 获取当前会话是否已经登录, 返回true或false
StpUtil.logout(); // 当前会话注销登录
StpUtil.logoutByLoginId(10001); // 让账号为10001的会话注销登录踢人下线
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.logoutByLoginId(10001, "PC"); // 指定设备标识进行强制注销 (不同端不受影响)
StpUtil.kickout(10001, "PC"); // 指定账号指定设备标识踢下线 (不同端不受影响)
StpUtil.openSafe(120); // 在当前会话开启二级认证有效期为120秒
StpUtil.checkSafe(); // 校验当前会话是否处于二级认证有效期内,校验失败会抛出异常
StpUtil.switchTo(10044); // 将当前会话身份临时切换为其它账号

View File

@ -29,7 +29,7 @@
- [会话治理](/up/search-session)
- [全局侦听器](/up/global-listener)
- [全局过滤器](/up/global-filter)
- [多账号证](/up/many-account)
- [多账号证](/up/many-account)
<!-- - [微服务](/senior/dcs) -->
- **单点登录**
@ -63,6 +63,7 @@
- [Quick-Login快速登录插件](/plugin/quick-login)
- [Alone独立Redis插件](/plugin/alone-redis)
- [持久层扩展](/plugin/dao-extend)
- [和 Thymeleaf 集成](/plugin/thymeleaf-extend)
- **其它**
- [更新日志](/more/update-log)

View File

@ -61,6 +61,14 @@ body{font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu
.lang-xml .token.tag *{color: #db2d20;}
.lang-xml .token.attr-value{color: #A6E22E;}
/* html语言样式优化 */
.lang-html .token.comment{color: #CDAB53;}
.lang-html .token.tag *{color: #db2d20;}
.lang-html .token.tag .attr-name,
.lang-html .token.tag .attr-name *{color: #A6E22E; opacity: 0.9;}
.lang-html .token.tag .attr-value,
.lang-html .token.tag .attr-value *{color: #E6DB74; opacity: 0.9;}
/* java语言样式优化 */
.main-box .lang-java{color: #01a252 !important;; opacity: 1;}
.lang-java .token.keyword{color: #db2d20;}

View File

@ -12,6 +12,7 @@ SaManager.getStpInterface(); // 获取权限认证对象
SaManager.getSaTokenAction(); // 获取框架行为对象
SaManager.getSaTokenContext(); // 获取上下文处理对象
SaManager.getSaTokenListener(); // 获取侦听器对象
SaManager.getSaTemp(); // 获取临时令牌验证模块对象
SaManager.getStpLogic("type"); // 获取指定账号类型的StpLogic对象
```

View File

@ -0,0 +1,125 @@
# Thymeleaf 标签方言
本插件的作用是让我们可以在 Thymeleaf 页面中使用 Sa-Token 相关API俗称 —— 标签方言。
---
### 1、引入依赖
首先我们确保项目已经引入 Thymeleaf 依赖,然后在此基础上继续添加:
``` xml
<!-- 在 thymeleaf 标签中使用 Sa-Token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dialect-thymeleaf</artifactId>
<version>${sa.top.version}</version>
</dependency>
```
### 2、注册标签方言对象
在 SaTokenConfigure 配置类中注册 Bean
``` java
@Configuration
public class SaTokenConfigure {
// Sa-Token 标签方言 (Thymeleaf版)
@Bean
public SaTokenDialect getSaTokenDialect() {
return new SaTokenDialect();
}
}
```
### 3、使用标签方言
然后我们就可以愉快的使用在 Thymeleaf 页面中使用标签方言了
##### 3.1、登录判断
``` html
<h2>标签方言测试页面</h2>
<p>
登录之后才能显示:
<span sa:login>value</span>
</p>
<p>
不登录才能显示:
<span sa:notLogin>value</span>
</p>
```
##### 3.2、角色判断
``` html
<p>
具有角色 admin 才能显示:
<span sa:hasRole="admin">value</span>
</p>
<p>
同时具备多个角色才能显示:
<span sa:hasRoleAnd="admin, ceo, cto">value</span>
</p>
<p>
只要具有其中一个角色就能显示:
<span sa:hasRoleOr="admin, ceo, cto">value</span>
</p>
<p>
不具有角色 admin 才能显示:
<span sa:lackRole="admin">value</span>
</p>
```
##### 3.3、权限判断
``` html
<p>
具有权限 user-add 才能显示:
<span sa:hasPermission="user-add">value</span>
</p>
<p>
同时具备多个权限才能显示:
<span sa:hasPermissionAnd="user-add, user-delete, user-get">value</span>
</p>
<p>
只要具有其中一个权限就能显示:
<span sa:hasPermissionOr="user-add, user-delete, user-get">value</span>
</p>
<p>
不具有权限 user-add 才能显示:
<span sa:lackPermission="user-add">value</span>
</p>
```
### 4、调用 Sa-Token 相关API
以上的标签方言,可以满足我们大多数场景下的权限判断,然后有时候我们依然需要更加灵活的在页面中调用 Sa-Token 框架API
首先在 SaTokenConfigure 配置类中为 Thymeleaf 配置全局对象:
``` java
public class SaTokenConfigure{
// ... 其它代码
// 为 Thymeleaf 注入全局变量,以便在页面中调用 Sa-Token 的方法
@Autowired
private void configureThymeleafStaticVars(ThymeleafViewResolver viewResolver) {
viewResolver.addStaticVariable("stp", StpUtil.stpLogic);
}
}
```
然后我们就可以在页面上调用 StpLogic 的 API 了,例如:
``` html
<p>调用 StpLogic 方法调用测试</p>
<p th:if="${stp.isLogin()}">
从SaSession中取值
<span th:text="${stp.getSession().get('name')}"></span>
</p>
```

View File

@ -181,16 +181,16 @@ public class SaSsoServerApplication {
![sso-server-init-login.png](https://oss.dev33.cn/sa-token/doc/sso/sso-server-init-login.png 's-w-sh')
可以看到这个页面非常简陋这是因为我们以上的代码示例主要目标是为了带大家从零搭建一个可用的SSO认证服务端所以就对一些不太必要的步骤做了简化
可以看到这个页面目前非常简陋这是因为我们以上的代码示例主要目标是为了带大家从零搭建一个可用的SSO认证服务端所以就对一些不太必要的步骤做了简化
大家可以下载运行一下官方仓库里的示例`/sa-token-demo/sa-token-demo-sso-server/`,里面有制作好的登录页面:
![sso-server-init-login2.png](https://oss.dev33.cn/sa-token/doc/sso/sso-server-init-login2.png 's-w-sh')
默认账号密码为:`sa / 123456`大家先别着急点击登录,因为我们还没有搭建对应的 Client 端项目,
默认账号密码为:`sa / 123456`,先别着急点击登录,因为我们还没有搭建对应的 Client 端项目,
真实项目中我们是不会直接从浏览器访问 `/sso/auth` 授权地址的,我们需要在 Client 端点击登录按钮重定向而来。
现在我们先来看看除了 `/sso/auth` 统一授权地址,这个 SSO-Server 认证中心还开放了哪些API呢,且往下看
现在我们先来看看除了 `/sso/auth` 统一授权地址,这个 SSO-Server 认证中心还开放了哪些API
### 5、API 列表
@ -244,7 +244,7 @@ http://{host}:{port}/sso/checkTicket
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| ticket | 是 | 在步骤 5.1 中授权重定向时的 ticket 参数 |
| ssoLogoutCall | 否 | 单点注销时的回调通知地址只在SSO模式三时需要携带此参数|
| ssoLogoutCall | 否 | 单点注销时的回调通知地址只在SSO模式三单点注销时需要携带此参数|
返回值场景:
- 返回空,代表校验失败。
@ -267,7 +267,7 @@ http://{host}:{port}/sso/logout
此接口有两种调用方式
##### 方式一:在前端页面引导用户直接跳转,并带有 back 参数
##### 方式一:在 Client 的前端页面引导用户直接跳转,并带有 back 参数
例如:`http://{host}:{port}/sso/logout?back=xxx`代表用户注销成功后返回back地址
##### 方式二:在 Client 的后端通过 http 工具来调用

View File

@ -40,17 +40,19 @@ OK所有理论就绪下面开始实战
``` yml
sa-token:
cookie:
# 配置Cookie作用域 (这个配置原本是被注释掉的,现在我们将其打开)
# 配置Cookie作用域
domain: stp.com
```
在SSO模式一测试完毕之后一定要将这个配置再次注释掉因为模式一与模式二三使用不同的授权流程这行配置会影响到我们模式二和模式三的正常运行。
这个配置原本是被注释掉的,现在将其打开。另外我们格外需要注意:
在SSO模式一测试完毕之后一定要将这个配置再次注释掉因为模式一与模式二三使用不同的授权流程这行配置会影响到我们模式二和模式三的正常运行。
### 4、搭建 Client 端项目
> 整合示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso1-client/`,如遇到难点可结合源码进行测试学习。
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso1-client/`,如遇到难点可结合源码进行测试学习。
#### 4.1、引入依赖

View File

@ -122,7 +122,7 @@ public Object myinfo() {
5. Server 端注销下线。
6. 单点注销完成。
这些逻辑 Sa-Token 内部已经封装完毕,你只需按照文增加以下配置即可:
这些逻辑 Sa-Token 内部已经封装完毕,你只需按照文增加以下配置即可:
#### 4.1、SSO-Client 端新增配置

View File

@ -12,6 +12,7 @@
### 自定义侦听器实现
新建`MySaTokenListener.java`,继承`SaTokenListener`接口,并添加上注解`@Component`,保证此类被`SpringBoot`扫描到
``` java
/**
* 自定义侦听器的实现
@ -33,13 +34,13 @@ public class MySaTokenListener implements SaTokenListener {
/** 每次被踢下线时触发 */
@Override
public void doLogoutByLoginId(String loginType, Object loginId, String tokenValue, String device) {
public void doKickout(String loginType, Object loginId, String tokenValue) {
// ...
}
/** 每次被顶下线时触发 */
@Override
public void doReplaced(String loginType, Object loginId, String tokenValue, String device) {
public void doReplaced(String loginType, Object loginId, String tokenValue) {
// ...
}

View File

@ -1,4 +1,4 @@
# 多账号
# 多账号
---
### 0、需求场景
@ -80,48 +80,20 @@ public String info() {
我们期待一种`[注解继承/合并]`的能力,即:自定义一个注解,标注上`@SaCheckLogin(type = "user")`,然后在方法上标注这个自定义注解,效果等同于标注`@SaCheckLogin(type = "user")`
很遗憾JDK默认的注解处理器并没有提供这种`[注解继承/合并]`的能力不过好在我们可以利用Spring的注解处理器达到同样的目的
很遗憾JDK默认的注解处理器并没有提供这种`[注解继承/合并]`的能力,不过好在我们可以利用 Spring 的注解处理器,达到同样的目的
1. 重写Sa-Token默认的注解处理器
``` java
/**
* 继承Sa-Token行为Bean默认实现, 重写部分逻辑
*/
@Component
public class MySaTokenAction extends SaTokenActionDefaultImpl {
/**
* 重写Sa-Token的注解处理器加强注解合并功能
*/
@Override
protected void validateAnnotation(AnnotatedElement target) {
// 校验 @SaCheckLogin 注解
if(AnnotatedElementUtils.isAnnotated(target, SaCheckLogin.class)) {
SaCheckLogin at = AnnotatedElementUtils.getMergedAnnotation(target, SaCheckLogin.class);
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
}
// 校验 @SaCheckRole 注解
if(AnnotatedElementUtils.isAnnotated(target, SaCheckRole.class)) {
SaCheckRole at = AnnotatedElementUtils.getMergedAnnotation(target, SaCheckRole.class);
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
}
// 校验 @SaCheckPermission 注解
if(AnnotatedElementUtils.isAnnotated(target, SaCheckPermission.class)) {
SaCheckPermission at = AnnotatedElementUtils.getMergedAnnotation(target, SaCheckPermission.class);
SaManager.getStpLogic(at.type()).checkByAnnotation(at);
}
// 校验 @SaCheckSafe 注解
if(AnnotatedElementUtils.isAnnotated(target, SaCheckSafe.class)) {
SaCheckSafe at = AnnotatedElementUtils.getMergedAnnotation(target, SaCheckSafe.class);
SaManager.getStpLogic(null).checkByAnnotation(at);
}
}
@Configuration
public class SaTokenConfigure {
@Autowired
public void rewriteSaStrategy() {
// 重写Sa-Token的注解处理器增加注解合并功能
SaStrategy.me.getAnnotation = (element, annotationClass) -> {
return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
};
}
}
```
@ -184,6 +156,3 @@ public class StpUserUtil {
再次调用 `StpUserUtil.login(10001)` 进行登录授权时token的名称将不再是 `satoken`,而是我们重写后的 `satoken-user`
> 不同体系账号在登录时设置不同的token有效期等信息详见[登录时指定token有效期](/up/remember-me?id=登录时指定token有效期)

View File

@ -14,8 +14,8 @@ Sa-Token在api设计时充分考虑了这一点暴露出多个api进行此类
// 获取指定账号10001的`tokenValue`值
StpUtil.getTokenValueByLoginId(10001);
// 将账号10001的会话注销登录(踢人下线)
StpUtil.logoutByLoginId(10001);
// 将账号10001的会话注销登录
StpUtil.logout(10001);
// 获取账号10001的Session对象, 如果session尚未创建, 则新建并返回
StpUtil.getSessionByLoginId(10001);

View File

@ -21,10 +21,10 @@ StpUtil.login(10001, "PC");
#### 指定设备标识强制注销
``` java
// 指定`账号id`和`设备标识`进行强制注销 (踢人下线)
StpUtil.logoutByLoginId(10001, "PC");
// 指定`账号id`和`设备标识`进行强制注销
StpUtil.logout(10001, "PC");
```
如果第二个参数填写null或不填代表将这个账号id所有在线端踢下线,被踢出者再次访问系统时会抛出 `NotLoginException` 异常,场景值=`-5`
如果第二个参数填写null或不填代表将这个账号id所有在线端强制注销,被踢出者再次访问系统时会抛出 `NotLoginException` 异常,场景值=`-2`
#### 查询当前登录的设备标识
@ -40,5 +40,3 @@ StpUtil.getLoginDevice();
StpUtil.getTokenValueByLoginId(10001, "APP");
```
> 不同设备账号在登录时设置不同的token有效期等信息, 详见[登录时指定token有效期](/up/remember-me?id=登录时指定token有效期)

View File

@ -37,27 +37,24 @@ Sa-Token默认的token生成策略是uuid风格其模样类似于`623368f0
如果你觉着以上风格都不是你喜欢的类型,那么你还可以**自定义token生成策略**来定制化token生成风格 <br>
怎么做呢?只需要重写`SaTokenAction`接口的`createToken`方法即可
怎么做呢?只需要重写 `SaStrategy` 策略类的 `createToken`法即可
#### 参考步骤如下:
1、新建文件`MySaTokenAction.java`,继承`SaTokenActionDefaultImpl`默认实现类, 并添加上注解`@Component`,保证此类被`springboot`扫描到
1、在`SaTokenConfigure`配置类中添加代码:
``` java
package com.pj.satoken;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
/**
* 继承Sa-Token行为Bean默认实现, 重写部分逻辑
*/
@Component
public class MySaTokenAction extends SaTokenActionDefaultImpl {
// 重写token生成策略
@Override
public String createToken(Object loginId, String loginType) {
return SaFoxUtil.getRandomString(60); // 随机60位字符串
}
@Configuration
public class SaTokenConfigure {
/**
* 重写 Sa-Token 框架内部算法策略
*/
@Autowired
public void rewriteSaStrategy() {
// 重写 Token 生成策略
SaStrategy.me.createToken = (loginId, loginType) -> {
return SaFoxUtil.getRandomString(60); // 随机60位长度字符串
};
}
}
```
@ -67,42 +64,3 @@ gfuPSwZsnUhwgz08GTCH4wOgasWtc3odP4HLwXJ7NDGOximTvT4OlW19zeLH
```
<!-- ## 以雪花算法生成token
在此再举一个例子,以`自定义token生成策略`的方式集成`雪花算法`来生成token -->
<!-- 1、首先我们需要找一个合适的类库帮助我们生成雪花算法唯一id在此推荐 [Hutool](https://hutool.cn/docs/#/) ,在`pom.xml`里添加依赖:
``` xml
Hutool 一个小而全的Java工具类库
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.4</version>
</dependency>
``` -->
<!-- 2、同上我们需要新建文件`MySaTokenAction.java`,继承`SaTokenActionDefaultImpl`默认实现类, 并添加上注解`@Component`,保证此类被`springboot`扫描到
``` java
package com.pj.satoken;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
import cn.hutool.core.util.IdUtil;
/**
* 继承Sa-Token行为Bean默认实现, 重写部分逻辑
*/
@Component
public class MySaTokenAction extends SaTokenActionDefaultImpl {
// 重写token生成策略
@Override
public String createToken(Object loginId, String loginType) {
return IdUtil.getSnowflake(1, 1).nextIdStr(); // 以雪花算法生成token
}
}
``` -->
<!-- 3、再次调用 `StpUtil.login(10001)`方法进行登录观察其生成的token样式:
``` html
1339604338175250432
``` -->

View File

@ -89,14 +89,30 @@ public AjaxJson atJurOr() {
}
```
mode有两种取值
- `SaMode.AND`, 标注一组权限,会话必须全部具有才可通过校验
- `SaMode.OR`, 标注一组权限,会话只要具有其一即可通过校验
### 4、角色权限双重 “or校验”
假设有以下业务场景:一个接口在具体权限 `user-add` 或角色 `admin` 时可以调通。怎么写?
### 4、在业务逻辑层使用注解鉴权
``` java
// 注解式鉴权:只要具有其中一个权限即可通过校验
@RequestMapping("userAdd")
@SaCheckPermission(value = "user-add", orRole = "admin")
public AjaxJson userAdd() {
return AjaxJson.getSuccessData("用户信息");
}
```
orRole 字段代表权限认证未通过时的次要选择,两者只要其一认证成功即可通过校验,其有三种写法:
- 写法一:`orRole = "admin"`,代表需要拥有角色 admin 。
- 写法二:`orRole = {"admin", "manager", "staff"}`,代表具有三个角色其一即可。
- 写法三:`orRole = {"admin, manager, staff"}`,代表必须同时具有三个角色。
### 5、在业务逻辑层使用注解鉴权
疑问:我能否将注解写在其它架构层呢,比如业务逻辑层?
使用拦截器模式,只能在`Controller层`进行注解鉴权,如需在任意层级使用注解鉴权,请参考:[AOP注解鉴权](/plugin/aop-at)

View File

@ -33,7 +33,7 @@ sa-token:
### 方式2、通过代码配置
式1
式1
``` java
/**
* Sa-Token代码方式进行配置
@ -59,7 +59,7 @@ public class SaTokenConfigure {
}
```
式2
式2
``` java
// 以代码的方式配置Sa-Token-Config
@Autowired
@ -69,7 +69,7 @@ public void configSaToken(SaTokenConfig config) {
}
```
PS两者的区别在于**`方式1会覆盖yml中的配置式2会与yml中的配置合并`**
PS两者的区别在于**`模式1会覆盖yml中的配置式2会与yml中的配置合并`**
---
@ -96,6 +96,18 @@ PS两者的区别在于**`方式1会覆盖yml中的配置方式2会与y
| basic | String | "" | Http Basic 认证的账号和密码 [参考Http Basic 认证](/up/basic-auth) |
| currDomain | null | "" | 配置当前项目的网络访问地址 |
| sso | Object | new SaSsoConfig() | SSO 单点登录相关配置 |
| cookie | Object | new SaCookieConfig() | Cookie配置对象 |
Cookie相关配置
| 参数名称 | 类型 | 默认值 | 说明 |
| :-------- | :-------- | :-------- | :-------- |
| domain | String | null | 作用域写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景 |
| path | String | / | 路径,默认写在域名根路径下 |
| secure | Boolean | false | 是否只在 https 协议下有效 |
| httpOnly | Boolean | false | 是否禁止 js 操作 Cookie |
| sameSite | String | Lax | 第三方限制级别Strict=完全禁止Lax=部分允许None=不限制) |
### 单点登录相关配置
@ -130,7 +142,7 @@ Client 端:
sa-token:
# SSO-相关配置
sso:
# SSO-Server端 单点登录地址
# SSO-Server端 单点登录授权地址
auth-url: http://sa-sso-server.com:9000/sso/auth
```

View File

@ -75,16 +75,16 @@ public class StpInterfaceImpl implements StpInterface {
然后就可以用以下api来鉴权了
``` java
// 当前账号是否含有指定权限, 返回true或false
// 判断:当前账号是否含有指定权限, 返回true或false
StpUtil.hasPermission("user-update");
// 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
// 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
StpUtil.checkPermission("user-update");
// 当前账号是否含有指定权限 [指定多个,必须全部验证通过]
// 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
StpUtil.checkPermissionAnd("user-update", "user-delete");
// 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
// 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
StpUtil.checkPermissionOr("user-update", "user-delete");
```
@ -95,16 +95,16 @@ StpUtil.checkPermissionOr("user-update", "user-delete");
在Sa-Token中角色和权限可以独立验证
``` java
// 当前账号是否含有指定角色标识, 返回true或false
// 判断:当前账号是否拥有指定角色, 返回true或false
StpUtil.hasRole("super-admin");
// 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
// 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRole("super-admin");
// 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
// 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleAnd("super-admin", "shop-admin");
// 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
// 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
StpUtil.checkRoleOr("super-admin", "shop-admin");
```

View File

@ -6,25 +6,28 @@
---
### 根据账号id踢人
让指定账号id的会话注销登录例如
### 1、强制注销
``` java
// 使账号id为10001的会话注销登录踢人下线待到10001再次访问系统时会抛出`NotLoginException`异常,场景值为-5
StpUtil.logoutByLoginId(10001);
StpUtil.logout(10001); // 强制指定账号注销下线
StpUtil.logout(10001, "PC"); // 强制指定账号指定端注销下线
StpUtil.logoutByTokenValue("token"); // 强制指定 Token 注销下线
```
### 根据Token令牌踢人
你还可以让指定token的会话注销登录
### 2、踢人下线
``` java
// 使账号id为10001的会话注销登录
StpUtil.logoutByTokenValue("xxxx-xxxx-xxxx-xxxx-xxxx");
StpUtil.kickout(10001); // 将指定账号踢下线
StpUtil.kickout(10001, "PC"); // 将指定账号指定端踢下线
StpUtil.kickoutByTokenValue("token"); // 将指定 Token 踢下线
```
此方法直接删除了`token->uid`的映射关系,对方再次访问时提示:`token无效`,场景值为-2
强制注销 和 踢人下线 的区别在于:
- 强制注销等价于对方主动调用了注销方法再次访问会提示Token无效。
- 踢人下线不会清除Token信息而是将其打上特定标记再次访问会提示Token已被踢下线。
### 账号封禁
### 3、账号封禁
对于违规账号,有时候我们仅仅将其踢下线还是远远不够的,我们还需要对其进行**账号封禁**防止其再次登录
``` java
@ -49,7 +52,7 @@ StpUtil.untieDisable(10001);
如果需要将其封禁后立即掉线,可采取先踢再封禁的策略,例如:
``` java
// 先踢下线
StpUtil.logoutByLoginId(10001);
StpUtil.kickout(10001);
// 再封禁账号
StpUtil.disable(10001, 86400);
```

View File

@ -9,7 +9,7 @@
### 1、注册路由拦截器
### 1、注册 Sa-Token 路由拦截器
以`SpringBoot2.0`为例, 新建配置类`SaTokenConfigure.java`
``` java
@Configuration
@ -17,39 +17,44 @@ public class SaTokenConfigure implements WebMvcConfigurer {
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册Sa-Token的路由拦截器并排除登录接口或其他可匿名访问的接口地址 (与注解拦截器无关)
registry.addInterceptor(new SaRouteInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/doLogin");
// 注册Sa-Token的路由拦截器
registry.addInterceptor(new SaRouteInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/user/doLogin");
}
}
```
以上代码,我们注册了一个登录证拦截器,并且排除了`/user/doLogin`接口用来开放登录(除了`/user/doLogin`以外的所有接口都需要登录才能访问) <br>
以上代码,我们注册了一个登录证拦截器,并且排除了`/user/doLogin`接口用来开放登录(除了`/user/doLogin`以外的所有接口都需要登录才能访问) <br>
那么我们如何进行权限认证拦截呢,且往下看
### 2、自定义权限验证规则
你可以使用函数式编程自定义验证规则
### 2、校验函数详解
你可以使用函数式编程自定义认证规则,例如:
``` java
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册路由拦截器,自定义证规则
// 注册路由拦截器,自定义证规则
registry.addInterceptor(new SaRouteInterceptor((req, res, handler)->{
// 根据路由划分模块,不同模块不同鉴权
SaRouter.match("/user/**", () -> StpUtil.checkPermission("user"));
SaRouter.match("/admin/**", () -> StpUtil.checkPermission("admin"));
SaRouter.match("/goods/**", () -> StpUtil.checkPermission("goods"));
SaRouter.match("/orders/**", () -> StpUtil.checkPermission("orders"));
SaRouter.match("/notice/**", () -> StpUtil.checkPermission("notice"));
SaRouter.match("/comment/**", () -> StpUtil.checkPermission("comment"));
SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice"));
SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment"));
})).addPathPatterns("/**");
}
}
```
### 3、完整示例
所有用法示例:
SaRouter.match() 匹配函数有两个参数:
- 参数一要匹配的path路由。
- 参数二:要执行的校验函数。
在校验函数内不只可以使用 `StpUtil.checkPermission("xxx")` 进行权限校验,你还可以写任意代码,例如:
``` java
@Configuration
@ -57,41 +62,28 @@ public class SaTokenConfigure implements WebMvcConfigurer {
// 注册Sa-Token的拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册路由拦截器,自定义证规则
// 注册路由拦截器,自定义证规则
registry.addInterceptor(new SaRouteInterceptor((req, res, handler) -> {
// 登录验证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouter.match("/**", "/user/doLogin", () -> StpUtil.checkLogin());
// 登录认证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouter.match("/**", "/user/doLogin", r -> StpUtil.checkLogin());
// 角色认证 -- 拦截以 admin 开头的路由,必须具备 admin 角色或者 super-admin 角色才可以通过认证
SaRouter.match("/admin/**", r -> StpUtil.checkRoleOr("admin", "super-admin"));
// 权限认证 -- 不同模块认证不同权限
SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice"));
SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment"));
// 登录验证 -- 排除多个路径
SaRouter.match(Arrays.asList("/**"), Arrays.asList("/user/doLogin", "/user/reg"), () -> StpUtil.checkLogin());
// 角色认证 -- 拦截以 admin 开头的路由,必须具备[admin]角色或者[super-admin]角色才可以通过认证
SaRouter.match("/admin/**", () -> StpUtil.checkRoleOr("admin", "super-admin"));
// 权限认证 -- 不同模块, 校验不同权限
SaRouter.match("/user/**", () -> StpUtil.checkPermission("user"));
SaRouter.match("/admin/**", () -> StpUtil.checkPermission("admin"));
SaRouter.match("/goods/**", () -> StpUtil.checkPermission("goods"));
SaRouter.match("/orders/**", () -> StpUtil.checkPermission("orders"));
SaRouter.match("/notice/**", () -> StpUtil.checkPermission("notice"));
SaRouter.match("/comment/**", () -> StpUtil.checkPermission("comment"));
// 匹配 restful 风格路由
SaRouter.match("/article/get/{id}", () -> StpUtil.checkPermission("article"));
// 检查请求方式
SaRouter.match("/notice/**", () -> {
if(req.getMethod().equals(HttpMethod.GET.toString())) {
StpUtil.checkPermission("notice");
}
});
// 提前退出 (执行SaRouter.stop()后会直接退出匹配链)
SaRouter.match("/test/back", () -> SaRouter.stop());
// 在多账号模式下可以使用任意StpUtil进行校验
SaRouter.match("/user/**", () -> StpUserUtil.checkLogin());
// 甚至你可以随意的写一个打印语句
SaRouter.match("/**", r -> System.out.println("----啦啦啦----"));
// 连缀写法
SaRouter.match("/**").check(r -> System.out.println("----啦啦啦----"));
})).addPathPatterns("/**");
}
@ -99,44 +91,80 @@ public class SaTokenConfigure implements WebMvcConfigurer {
```
### 4、提前退出匹配链条
### 3、匹配特征详解
除了上述示例的 path 路由匹配,还可以根据很多其它特征进行匹配,以下是所有可匹配的特征:
``` java
// 基础写法样例匹配一个path执行一个校验函数
SaRouter.match("/user/**").check(r -> StpUtil.checkLogin());
// 根据 path 路由匹配 ——— 支持写多个path支持写 restful 风格路由
SaRouter.match("/user/**", "/goods/**", "/art/get/{id}").check( /* 要执行的校验函数 */ );
// 根据 path 路由排除匹配
SaRouter.match("/**").notMatch("*.html", "*.css", "*.js").check( /* 要执行的校验函数 */ );
// 根据请求类型匹配
SaRouter.match(SaHttpMethod.GET).check( /* 要执行的校验函数 */ );
// 根据一个 boolean 条件进行匹配
SaRouter.match( StpUtil.isLogin() ).check( /* 要执行的校验函数 */ );
// 根据一个返回 boolean 结果的lambda表达式匹配
SaRouter.match( r -> StpUtil.isLogin() ).check( /* 要执行的校验函数 */ );
// 多个条件一起使用
SaRouter.match(SaHttpMethod.GET).match("/**").check( /* 要执行的校验函数 */ );
// 可以无限连缀下去
SaRouter
.match(SaHttpMethod.GET)
.match("/admin/**")
.match("/user/**")
.notMatch("/**/*.js")
.notMatch("/**/*.css")
// ....
.check( /* 只有上述所有条件都匹配成功才会执行最后的check校验函数 */ );
```
### 4、提前退出匹配链
使用 `SaRouter.stop()` 可以提前退出匹配链,例:
``` java
registry.addInterceptor(new SaRouteInterceptor((req, res, handler) -> {
SaRouter.match("/**", () -> System.out.println("进入1"));
SaRouter.match("/**", () -> {System.out.println("进入2"); SaRouter.stop();});
SaRouter.match("/**", () -> System.out.println("进入3"));
SaRouter.match("/**").check(r -> System.out.println("进入1"));
SaRouter.match("/**").check(r -> System.out.println("进入2")).stop();
SaRouter.match("/**").check(r -> System.out.println("进入3"));
})).addPathPatterns("/**");
```
如上示例代码运行至第2条匹配链时会在stop函数处提前退出整个匹配函数从而忽略掉剩余的所有match匹配
除了`stop()`函数,`SaRouter`还提供了 `back()` 函数,用于:停止匹配,结束执行,直接向前端返回结果
``` java
SaRouter.match("/user/back", () -> SaRouter.back("执行back函数后将停止匹配也不会进入Controller而是直接将此参数作为返回值输出到前端"));
// 执行back函数后将停止匹配也不会进入Controller而是直接将 back参数 作为返回值输出到前端
SaRouter.match("/user/back").back("参数");
```
`stop()``back()` 函数的区别在于:
stop() 与 back() 函数的区别在于:
- `SaRouter.stop()` 会停止匹配进入Controller。
- `SaRouter.back()` 会停止匹配,直接返回结果到前端。
<!--
### 注意事项
在`v1.14`及以前版本下,路由拦截器提供了封装式写法,该方法代码比较冗余,在`v1.15`版本已移除,替代方案如下:
### 5、使用free打开一个独立的作用域
``` java
// 原写法
registry.addInterceptor(SaRouteInterceptor.createPermissionVal("user")).addPathPatterns("/user/**");
// 改为以下方式,效果同上
registry.addInterceptor(new SaRouteInterceptor((request, response, handler) -> {
SaRouter.match("/user/**", () -> StpUtil.checkPermission("user"));
})).addPathPatterns("/**");
// 进入 free 独立作用域
SaRouter.match("/**").free(r -> {
SaRouter.match("/a/**").check(/* --- */);
SaRouter.match("/a/**").check(/* --- */).stop();
SaRouter.match("/a/**").check(/* --- */);
});
// 执行 stop() 函数跳出 free 后继续执行下面的 match 匹配
SaRouter.match("/**").check(/* --- */);
```
-->
free() 的作用是:打开一个独立的作用域,使内部的 stop() 不再一次性跳出整个 Auth 函数,而是仅仅跳出当前 free 作用域。