完善文档

This commit is contained in:
2020-02-06 00:52:49 +08:00
parent 3329910bdd
commit 8efce61988
28 changed files with 979 additions and 19 deletions

View File

@@ -5,6 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import cn.dev33.satoken.SaTokenManager;
import cn.dev33.satoken.spring.SaTokenSetup; import cn.dev33.satoken.spring.SaTokenSetup;
@SaTokenSetup // 标注启动 sa-token @SaTokenSetup // 标注启动 sa-token
@@ -13,6 +14,12 @@ public class SaTokenDemoApplication {
public static void main(String[] args) throws JsonProcessingException { public static void main(String[] args) throws JsonProcessingException {
SpringApplication.run(SaTokenDemoApplication.class, args); // run--> SpringApplication.run(SaTokenDemoApplication.class, args); // run-->
System.out.println("启动成功sa-token配置如下" + SaTokenManager.getConfig());
} }
} }

View File

@@ -0,0 +1,29 @@
//package com.pj.satoken;
//
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Primary;
//
//import cn.dev33.satoken.config.SaTokenConfig;
//
///**
// * sa-token代码方式进行配置
// */
//@Configuration
//public class MySaTokenConfig {
//
// // 获取配置Bean
// @Primary
// @Bean(name="MySaTokenConfig")
// public SaTokenConfig getSaTokenConfig() {
// SaTokenConfig config = new SaTokenConfig();
// config.setTokenName("satoken"); // token名称同时也是cookie名称
// config.setTimeout(30 * 24 * 60 * 60); // token有效期单位s 默认30天-1为永不过期
// config.setIsShare(true); // 在多人登录同一账号时是否共享会话为true时共用一个为false时新登录挤掉旧登录
// config.setIsReadHead(true); // 是否在cookie读取不到token时继续从请求header里继续尝试读取
// config.setIsReadBody(true); // 是否在cookie读取不到token时继续从请求header里继续尝试读取
// config.setIsV(true); // 是否在初始化配置时打印版本字符画
// return config;
// }
//
//}

View File

@@ -13,9 +13,10 @@ import cn.dev33.satoken.stp.StpInterface;
@Component // 打开此注解保证此类被springboot扫描即可完成sa-token的自定义权限验证扩展 @Component // 打开此注解保证此类被springboot扫描即可完成sa-token的自定义权限验证扩展
public class StpCustom implements StpInterface { public class StpCustom implements StpInterface {
// 返回一个账号所拥有的权限码集合
@Override @Override
public List<Object> getPermissionCodeList(Object login_id, String login_key) { public List<Object> getPermissionCodeList(Object login_id, String login_key) {
List<Object> list = new ArrayList<Object>(); List<Object> list = new ArrayList<Object>(); // 本list仅做模拟实际项目中要根据具体业务逻辑来查询权限
list.add("101"); list.add("101");
list.add("user-add"); list.add("user-add");
list.add("user-delete"); list.add("user-delete");

View File

@@ -4,7 +4,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.session.SaSessionUtil; import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
@RestController @RestController
@@ -22,9 +22,16 @@ public class TestController {
System.out.println("登录成功"); System.out.println("登录成功");
System.out.println("当前是否登录:" + StpUtil.isLogin()); System.out.println("当前是否登录:" + StpUtil.isLogin());
System.out.println("当前登录账号:" + StpUtil.getLoginId()); System.out.println("当前登录账号:" + StpUtil.getLoginId());
System.out.println("当前登录账号:" + StpUtil.getLoginId_asInt()); // 获取登录id并转为int System.out.println("当前登录账号:" + StpUtil.getLoginId_asInt()); // 获取登录id并转为int
// StpUtil.logout();
// System.out.println("注销登录");
// System.out.println("当前是否登录:" + StpUtil.isLogin());
// System.out.println("当前登录账号:" + StpUtil.getLoginId_defaultNull());
// StpUtil.setLoginId(id); // 在当前会话登录此账号
System.out.println("当前token信息" + StpUtil.getTokenInfo()); // 获取登录id并转为int System.out.println("当前token信息" + StpUtil.getTokenInfo()); // 获取登录id并转为int
System.out.println("当前登录账号:" + StpUtil.getLoginId_defaultNull());
return AjaxJson.getSuccess(); return AjaxJson.getSuccess();
} }
@@ -70,11 +77,11 @@ public class TestController {
public AjaxJson session2() { public AjaxJson session2() {
System.out.println("======================= 进入方法测试自定义session接口 ========================= "); System.out.println("======================= 进入方法测试自定义session接口 ========================= ");
// 自定义session就是无需登录也可以使用 的session :比如拿用户的手机号当做 key 来获取 session // 自定义session就是无需登录也可以使用 的session :比如拿用户的手机号当做 key 来获取 session
System.out.println("自定义 session的id为" + SaSessionUtil.getSessionById("1895544896").getId()); System.out.println("自定义 session的id为" + SaSessionCustomUtil.getSessionById("1895544896").getId());
System.out.println("测试取值name" + SaSessionUtil.getSessionById("1895544896").getAttribute("name")); System.out.println("测试取值name" + SaSessionCustomUtil.getSessionById("1895544896").getAttribute("name"));
SaSessionUtil.getSessionById("1895544896").setAttribute("name", "张三"); // 写入值 SaSessionCustomUtil.getSessionById("1895544896").setAttribute("name", "张三"); // 写入值
System.out.println("测试取值name" + SaSessionUtil.getSessionById("1895544896").getAttribute("name")); System.out.println("测试取值name" + SaSessionCustomUtil.getSessionById("1895544896").getAttribute("name"));
System.out.println("测试取值name" + SaSessionUtil.getSessionById("1895544896").getAttribute("name")); System.out.println("测试取值name" + SaSessionCustomUtil.getSessionById("1895544896").getAttribute("name"));
return AjaxJson.getSuccess(); return AjaxJson.getSuccess();
} }

View File

@@ -8,8 +8,8 @@ public class SaTokenConfig {
private String tokenName = "satoken"; // token名称同时也是cookie名称 private String tokenName = "satoken"; // token名称同时也是cookie名称
private long timeout = 30 * 24 * 60 * 60; // token有效期单位s 默认30天-1为永不过期 private long timeout = 30 * 24 * 60 * 60; // token有效期单位s 默认30天-1为永不过期
private Boolean isShare = true; // 在多人登录同一账号时是否共享会话为true时共用一个为false时新登录挤掉旧登录 private Boolean isShare = true; // 在多人登录同一账号时是否共享会话为true时共用一个为false时新登录挤掉旧登录
private Boolean isReadHead = false; // 是否在cookie读取不到token时继续从请求header里继续尝试读取 private Boolean isReadHead = true; // 是否在cookie读取不到token时继续从请求header里继续尝试读取
private Boolean isReadBody = false; // 是否在header读取不到token时继续从请求题参数里继续尝试读取 private Boolean isReadBody = true; // 是否在header读取不到token时继续从请求题参数里继续尝试读取
private Boolean isV = true; // 是否在初始化配置时打印版本字符画 private Boolean isV = true; // 是否在初始化配置时打印版本字符画

View File

@@ -3,11 +3,11 @@ package cn.dev33.satoken.session;
import cn.dev33.satoken.SaTokenManager; import cn.dev33.satoken.SaTokenManager;
/** /**
* sa-session工具类 * 自定义sa-session工具类
* @author kong * @author kong
* *
*/ */
public class SaSessionUtil { public class SaSessionCustomUtil {
// 添加上指定前缀防止恶意伪造session // 添加上指定前缀防止恶意伪造session
public static String session_key = "custom"; public static String session_key = "custom";

View File

View File

@@ -0,0 +1,49 @@
# 介绍
------
## sa-token是什么
一个的`JavaWeb`权限认证框架,强大、简单、好用
> 与其它权限认证框架相比sa-token尽力保证两点
> - 上手简单:能自动化的配置全部自动化,不让你费脑子
> - 功能强大:能涵盖的功能全部涵盖,不让你用个框架还要自己给框架打各种补丁
## 涵盖功能
- 登录验证
- 权限验证
- 自定义session会话
- 踢人下线
- 模拟他人账号
- 持久层扩展集成redis
- 多账号认证体系比如一个商城项目的user表和admin表
- 无cookie模式APP、小程序等前后台分离场景
- 零配置与Spring等框架集成
- ...
## 贡献代码
1. 在github上fork一份到自己的仓库
2. clone自己的仓库到本地电脑
3. 在本地电脑修改、commit、push
4. 提交pr点击New Pull Request提交pr前请保证自己fork的仓库是最新版本如若不是先强制更新一下
5. 等待合并
## 建议贡献的地方
- 修复源码现有bug或增加新的实用功能
- 完善在线文档,或者修复现有错误之处
- 更多demo示例比如SSM版搭建步骤
- 如果更新实用功能,可在文档友情链接处留下自己的推广链接
## 交流群
QQ交流群[782974737 点击加入](https://jq.qq.com/?_wv=1027&k=5DHN5Ib) ,欢迎你的加入
![扫码加群](https://color-test.oss-cn-qingdao.aliyuncs.com/sqlfly-doc/qqq.png ':size=150')

View File

@@ -0,0 +1,31 @@
<!-- 这是目录树文件 -->
- **开始**
- [介绍](/README)
- [下载](/start/download)
- [示例](/start/example)
- **使用**
- [登录验证](/use/login-auth)
- [权限验证](/use/jur-auth)
- [session会话](/use/session)
- [踢人下线](/use/kick)
- [持久层扩展](/use/dao-extend)
- [无cookie模式](/use/not-cookie)
- [模拟他人](/use/mock-person)
- [多账号验证](/use/many-account)
- [框架配置](/use/config)
- **其它**
- [常见问题](/more/common-questions)
- [友情链接](/more/link)
- [更新日志](/more/update-log)
<!-- - [常见问题](/c1) -->
<!-- - [常用函数](/c1) -->
<!-- - [插件](/c1) -->
<!-- - [关于](/c1) -->
<!-- - eclipse插件 -->
<!-- - idea插件 -->

110
sa-token-doc/doc/index.html Normal file
View File

@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sa-token</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|权限认证框架">
<meta name="description" content="sa-token是一个的JavaWeb权限认证框架强大、简单、好用登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
<link rel="shortcut icon" type="image/x-icon" href="logo.png">
<style type="text/css">
.logo-box {
position: absolute;
left: 30px;
top: 10px;
cursor: pointer;
color: #000;
}
.logo-box img {
width: 50px;
height: 50px;
vertical-align: middle;
}
.logo-box .logo-text {
display: inline-block;
vertical-align: middle;
font-size: 24px;
font-weight: 400;
}
#main {
padding-bottom: 300px;
}
#main h2 {
font-size: 1.6rem;
}
#main h3 {
font-size: 1.25rem;
}
@media screen and (max-width: 800px) {
.logo-box {
display: none;
}
}
</style>
</head>
<body>
<a href="/">
<div class="logo-box">
<img src="logo.png" title="logo" />
<h1 class="logo-text">sa-token</h1>
</div>
</a>
<nav>
<a href="/">首页</a>
<a href="/doc/">文档</a>
<a href="/doc/#/more/update-log">更新日志</a>
</nav>
<div id="app">加载中...</div>
<script>
var name = '<img style="width: 50px; height: 50px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
name += '<b style="font-size: 24px; vertical-align: middle;">sa-token</b> <sub>v1.0.0</sub>'
// var name = '<b style="font-size: 24px; vertical-align: middle;">SqlFly</b> <sub>v1.0.0</sub>'
window.$docsify = {
name: name, // 名字
repo: 'https://github.com/click33/sa-token', // github地址
// themeColor: '#06A3D7', // 主题颜色
// basePath: '/sa-token-doc/', // 设置文件加载的父路径, 这在一些带项目名部署的文件中非常有效
// basePath: '/sa-token-doc/index.html',
auto2top: true, // 是否在切换页面后回到顶部
// coverpage: true, // 开启封面
subMaxLevel: 3, // 标题解析层级, 写几就在目录树中解析到几级标题 ,一般写2吧也就
loadSidebar: true, // 加载自定义侧边栏 , 目录定制在: _sidebar.md 文件 (需要创建 .nojekyll 的空文件,阻止 GitHub Pages 忽略命名是下划线开头的文件)
copyCode: { // 复制插件
buttonText: '复制到剪贴板',
errorText: '错误',
successText: '复制成功'
},
alias: {
'/.*/_sidebar.md': '/_sidebar.md'
}
}
</script>
<script src="https://unpkg.com/docsify@4.9.4/lib/docsify.min.js"></script>
<script src="https://unpkg.com/docsify-copy-code@2.1.0/dist/docsify-copy-code.min.js"></script>
<script src="https://unpkg.com/prismjs@1.19.0/components/prism-java.min.js"></script>
<script>
(function() {
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https') {
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
} else {
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
</script>
</body>
</html>

BIN
sa-token-doc/doc/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -0,0 +1,18 @@
# 常见问题
---
### 用这个框架我需要很多配置吗?
- 零配置开箱即用,但同时也支持自定义配置:参考:[框架配置](use/config)
### 如何踢人下线?
- 参考:[踢人下线](use/kick)
### 能否集成redis?
- 参考:[无cookie模式](use/dao-extend)
### 还是有不明白到的地方?
- 请在`github`提交`issues`或者加入qq群交流群链接在[首页](README?id=交流群)
### 我能为这个框架贡献代码吗?
- **可以**请参照首页的提交pr步骤 [贡献代码](README?id=贡献代码)

View File

@@ -0,0 +1,9 @@
# 友情链接
---
- **SqlFly**[一个好用的ORM框架](https://sqlfly.dev33.cn/)
- **sa-admin**[一个基于多窗口后台模板, 强大、易用](http://sa-admin.dev33.cn/)
- **颜值排行榜:**[一个以颜值为讨论主题的社区](http://web.yanzhi21.com/)
- 虚位以待

View File

@@ -0,0 +1,7 @@
# 更新日志
### 2020-2-4
- 第一个版本出炉
- GitHub[https://github.com/click33/sa-token](https://github.com/click33/sa-token)
- gitee[https://gitee.com/sz6/sa-token](https://gitee.com/sz6/sa-token)

View File

@@ -0,0 +1,24 @@
# 下载
------
## 源码
你可以通过`github`或者`gitee`来获取源码
- github地址[https://github.com/click33/sa-token](https://github.com/click33/sa-token)
- gitee地址[https://gitee.com/sz6/sa-token](https://gitee.com/sz6/sa-token)
- 开源不易求鼓励给个star吧
- 源码目录介绍
- sa-token-dev: 源码
- sa-token-demo-springboot: springboot集成示例
- sa-token-doc: 文档介绍
## jar包下载
[点击下载](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-token/sa-token-spring-1.0.0.jar)

View File

@@ -0,0 +1,85 @@
# 示例
---
> - 本篇将带你从零开始集成sa-token从而让你快速熟悉sa-token的使用姿势
> - 以maven + springboot为例
## springboot环境
#### 1、创建项目
在IDE中新建一个Springboot项目例如`sa-token-demo-springboot`不会的同学请自行百度或者参考github示例
#### 2、设置jar包依赖
- 在项目根目录新建文件夹`lib`,将 `sa-token-spring-xxx.jar` 复制到其中
- 并在 `pom.xml` 中添加依赖:
``` xml
<!-- sa-token 安全认证 -->
<dependency>
<groupId>cn.dev33.sa-token</groupId>
<artifactId>sa-token-spring</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/sa-token-spring-1.0.0.jar</systemPath>
</dependency>
```
#### 3、配置文件
- 你可以零配置启动项目
- 但同时你也可以在`application.yml`中增加如下配置,定制性使用框架:
``` java
spring:
# sa-token配置
sa-token:
# token名称同时也是cookie名称
token-name: satoken
# token有效期单位s 默认30天-1为永不过期
timeout: 2592000
# 在多人登录同一账号时是否共享会话为true时共用一个为false时新登录挤掉旧登录
is-share: true
# 是否在cookie读取不到token时继续从请求header里继续尝试读取
is-read-head: true
# 是否在header读取不到token时继续从请求题参数里继续尝试读取
is-read-body: true
# 是否在初始化配置时打印版本字符画
is-v: true
```
> - 如果你习惯于 `application.properties` 类型的配置文件,那也很好办:
> - 百度: [springboot properties与yml 配置文件的区别](https://www.baidu.com/s?ie=UTF-8&wd=springboot%20properties%E4%B8%8Eyml%20%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E5%8C%BA%E5%88%AB)
#### 4、创建主类
在项目中新建包 `com.pj` ,在此包内新建主类 `SaTokenDemoApplication.java`,输入以下代码:
``` java
@SaTokenSetup // 标注启动 sa-token
@SpringBootApplication
public class SaTokenDemoApplication {
public static void main(String[] args) throws JsonProcessingException {
SpringApplication.run(SaTokenDemoApplication.class, args); // run-->
System.out.println("启动成功sa-token配置如下" + SaTokenManager.getConfig());
}
}
```
#### 5、运行
运行代码,当你从控制台看到类似下面的内容时,就代表框架已经成功集成了
![运行结果](https://color-test.oss-cn-qingdao.aliyuncs.com/sa-token/app-run.jpg)
## 普通spring环境
- 普通spring环境与springboot环境大体无异只不过需要在项目根目录手动创建配置文件`sa-token.properties`来完成配置
## 详细了解
通过这个示例你已经对sa-token有了初步的了解那么现在开始详细了解一下它都有哪些[能力](/use/login-auth)吧

View File

@@ -0,0 +1,68 @@
# 框架配置
- 你可以零配置启动框架
- 但同时你也可以通过配置定制性使用框架sa-token支持多种方式配置框架信息
---
### 所有可配置项
| 参数名称 | 类型 | 默认值 | 说明 |
| :-------- | :-------- | :-------- | :-------- |
| tokenName | String | satoken | token名称同时也是cookie名称 |
| timeout | long | 2592000 | token有效期单位s 默认30天-1为永不过期 |
| isShare | Boolean | true | 在多人登录同一账号时是否共享会话为true时共用一个为false时新登录挤掉旧登录 |
| isReadHead| Boolean | true | 是否在cookie读取不到token时继续从请求header里继续尝试读取 |
| isReadBody| Boolean | true | 是否在header读取不到token时继续从请求题参数里继续尝试读取 |
| isV | Boolean | true | 是否在初始化配置时打印版本字符画 |
### 方式1、通过代码配置
``` java
/**
* sa-token代码方式进行配置
*/
@Configuration
public class MySaTokenConfig {
// 获取配置Bean
@Primary
@Bean(name="MySaTokenConfig")
public SaTokenConfig getSaTokenConfig() {
SaTokenConfig config = new SaTokenConfig();
config.setTokenName("satoken"); // token名称同时也是cookie名称
config.setTimeout(30 * 24 * 60 * 60); // token有效期单位s 默认30天-1为永不过期
config.setIsShare(true); // 在多人登录同一账号时是否共享会话为true时共用一个为false时新登录挤掉旧登录
config.setIsReadHead(true); // 是否在cookie读取不到token时继续从请求header里继续尝试读取
config.setIsReadBody(true); // 是否在cookie读取不到token时继续从请求header里继续尝试读取
config.setIsV(true); // 是否在初始化配置时打印版本字符画
return config;
}
}
```
### 方式2、在`application.yml`配置
``` java
spring:
# sa-token配置
sa-token:
# token名称同时也是cookie名称
token-name: satoken
# token有效期单位s 默认30天-1为永不过期
timeout: 2592000
# 在多人登录同一账号时是否共享会话为true时共用一个为false时新登录挤掉旧登录
is-share: true
# 是否在cookie读取不到token时继续从请求header里继续尝试读取
is-read-head: true
# 是否在header读取不到token时继续从请求题参数里继续尝试读取
is-read-body: true
# 是否在初始化配置时打印版本字符画
is-v: true
```
- 如果你习惯于 `application.properties` 类型的配置文件,那也很好办:
- 百度: [springboot properties与yml 配置文件的区别](https://www.baidu.com/s?ie=UTF-8&wd=springboot%20properties%E4%B8%8Eyml%20%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E5%8C%BA%E5%88%AB)

View File

@@ -0,0 +1,106 @@
# 持久层扩展
---
- 每次重启项目就得重新登录一遍我想把登录数据都放在redis里这样重启项目也不用重新登录行不行
- 行!
- 你需要做的就是重写`sa-token`的dao层实现方式参考以下方案
## 具体代码
- 新建文件`SaTokenDaoRedis.java`,实现接口`SaTokenDao`, 并加上注解`@Component`保证此类被springboot扫描到
- 代码参考:
```java
package com.pj.satoken;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
// import org.springframework.stereotype.Component;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.session.SaSession;
/**
* sa-token持久层的实现类 , 基于redis
*/
@Component // 保证此类被springboot扫描即可完成sa-token与redis的集成
public class SaTokenDaoRedis implements SaTokenDao {
// string专用
@Autowired
StringRedisTemplate stringRedisTemplate;
// SaSession专用
RedisTemplate<String, SaSession> redisTemplate;
@Autowired
@SuppressWarnings({ "rawtypes", "unchecked" })
public void setRedisTemplate(RedisTemplate redisTemplate) {
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
this.redisTemplate = redisTemplate;
}
// 根据key获取value ,如果没有,则返回空
@Override
public String getValue(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
// 写入指定key-value键值对并设定过期时间(单位:秒)
@Override
public void setValue(String key, String value, long timeout) {
stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
}
// 删除一个指定的key
@Override
public void delKey(String key) {
stringRedisTemplate.delete(key);
}
// 根据指定key的session如果没有则返回空
@Override
public SaSession getSaSession(String sessionId) {
return redisTemplate.opsForValue().get(sessionId);
}
// 将指定session持久化
@Override
public void saveSaSession(SaSession session, long timeout) {
redisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS);
}
// 更新指定session
@Override
public void updateSaSession(SaSession session) {
long expire = redisTemplate.getExpire(session.getId());
if(expire == -2) { // -2 = 无此键
return;
}
redisTemplate.opsForValue().set(session.getId(), session, expire, TimeUnit.SECONDS);
}
// 删除一个指定的session
@Override
public void delSaSession(String sessionId) {
redisTemplate.delete(sessionId);
}
}
```
- 可参考代码:[码云SaTokenDaoRedis.java](https://gitee.com/sz6/sa-token/blob/master/sa-token-demo-springboot/src/main/java/com/pj/satoken/SaTokenDaoRedis.java)

View File

@@ -0,0 +1,75 @@
# 权限验证
---
## 核心思想
- 所谓权限验证,验证的核心就是当前账号是否拥有一个权限码
- 有:就让你通过、没有:那么禁止访问
- 再往底了说,就是每个账号都会拥有一个权限码集合,我来验证这个集合中是否包括我需要检测的那个权限码
- 例如:当前账号拥有权限码集合:`[101, 102, "user-add", "user-get"]`,这时候我去验证权限码:`201`,则结果就是验证失败,禁止访问
- 所以现在问题的核心就是1、如何获取一个账号所拥有的的权限码集合2、本次操作要验证的权限码是哪个
## 获取当前账号权限码集合
因为每个项目的需求不同,其权限设计也千变万化,所以【获取当前账号权限码集合】这一操作不可能内置到框架中,
所以`sa-token`将此操作以接口的方式暴露给你,以方便的你根据自己的业务逻辑进行重写
- 你需要做的就是新建一个类,重写`StpInterface`接口,例如以下代码:
``` java
package com.pj.satoken;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.stp.StpInterface;
/**
* 自定义权限验证接口扩展
*/
@Component // 保证此类被springboot扫描完成sa-token的自定义权限验证扩展
public class StpCustom implements StpInterface {
// 返回一个账号所拥有的权限码集合
@Override
public List<Object> getPermissionCodeList(Object login_id, String login_key) {
List<Object> list = new ArrayList<Object>(); // 本list仅做模拟实际项目中要根据具体业务逻辑来查询权限
list.add("101");
list.add("user-add");
list.add("user-delete");
list.add("user-update");
list.add("user-get");
list.add("article-get");
return list;
}
}
```
- 可参考代码:[码云StpCustom.java](https://gitee.com/sz6/sa-token/blob/master/sa-token-demo-springboot/src/main/java/com/pj/satoken/StpCustom.java)
## 验证是否包含指定权限码
然后就可以用以下api来鉴权了
#### StpUtil.hasPermission(Object pcode)
- 查询当前账号是否含有指定权限返回true或false
#### StpUtil.checkPermission(Object pcode)
- 检测当前账号是否含有指定权限,如果有则安全通过,如果没有则抛出异常:`NotPermissionException`
#### StpUtil.checkPermissionAnd(Object... pcode)
- 检测当前账号是否含有指定权限【指定多个,必须全都有,否则抛出异常】
#### StpUtil.checkPermissionOr(Object... pcode)
- 检测当前账号是否含有指定权限【指定多个,有一个就可以了,全都没有才会抛出异常】
## 拦截全局异常
- 有同学要问,鉴权失败,抛出异常,然后呢?要把异常显示给用户看吗?
- 当然不能把异常抛给用户看,你可以创建一个全局异常拦截器,统一返回给前端的格式,例如以下示例:
- 参考:[码云TopController.java](https://gitee.com/sz6/sa-token/blob/master/sa-token-demo-springboot/src/main/java/com/pj/test/TopController.java)

View File

@@ -0,0 +1,15 @@
# 踢人下线
---
## 核心思想
- 所谓踢人下线核心操作就是找到其指定login_id的token并设置其失效
## 具体API
#### StpUtil.logoutByLoginId(Object login_id)
- 让指定login_id的会话注销登录踢人下线

View File

@@ -0,0 +1,38 @@
# 登录验证
---
## 核心思想
- 所谓登录验证,说白了就是限制某些接口只有登录后才能访问(如:查询我的账号资料)
- 如何判断你有没有登录?当然是登录成功后我给你做个标记
- 在需要鉴权的接口里检查标记,有标记者视为已登录,无标记者视为未登录
- 根据以上思路我们很容易想到以下api
## 具体API
#### StpUtil.setLoginId(Object login_id)
- 标记当前会话登录的账号id
- 建议的参数类型long | int | String 不可以传入复杂类型User、Admin等等
#### StpUtil.logout()
- 当前会话注销登录
#### StpUtil.isLogin()
- 获取当前会话是否已经登录返回true=已登录false=未登录
#### StpUtil.getLoginId()
- 获取当前会话登录id, 如果未登录,则抛出异常:`NotLoginException`
- 类似API还有
- `StpUtil.getLoginId_asString()` 获取当前会话登录id, 并转化为String类型
- `StpUtil.getLoginId_asInt()` 获取当前会话登录id, 并转化为int类型
- `StpUtil.getLoginId_asLong()` 获取当前会话登录id, 并转化为long类型
#### StpUtil.getLoginId(T default_value)
- 获取当前会话登录id, 如果未登录,则返回默认值 default_value可以为任意类型
- 类似API还有
- `StpUtil.getLoginId_defaultNull()` 获取当前会话登录id, 如果未登录则返回null

View File

@@ -0,0 +1,28 @@
# 多账号验证
---
## 问题
- 有的时候在一个项目中我们会设计两套账号体系比如一个商城的user表和admin表
- 这时候,我们就需要将两套账号的权限认证分开,防止冲突
## 核心思想
- sa-token在设计时充分考虑了多账号体系时的各种逻辑
- 以上几篇介绍的api都是经过 `StpUtil`类的各种静态方法进行各种验证,而如果你深入它的源码,[点此阅览](https://gitee.com/sz6/sa-token/blob/master/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java)
- 就会发现,此类并没有任何代码逻辑,唯一做的事就是对成员变量`stpLogic`的各个API进行包装一下进行转发
- 这样做有两个优点
- `StpLogic`类的所有函数都可以被重写,按需扩展
- 在构造方法时随意传入一个不同的 `login_key`,就可以再造一套账号登录体系
## 操作示例
1. 新建一个新的权限验证类,比如: `StpAdminUtil.java`
2.`StpUtil.java`类的全部代码复制粘贴到 `StpAdminUtil.java`
3. 更改一下其 `login_key` 比如:
```
// 底层的 StpLogic 对象
public static StpLogic stpLogic = new StpLogic("admin"); // login_key改为admin
```
4. 接下来就可以像调用`StpUtil.java`一样调用 `StpAdminUtil.java`了,这两套账号认证的逻辑是完全隔离的

View File

@@ -0,0 +1,24 @@
# 模拟他人
---
- 以上介绍的api都是操作当前账号对当前账号进行各种鉴权操作你可能会问我能不能对别的账号进行一些操作
- 比如查看账号10001有无某个权限码、获取id账号为10002的用户session等等...
- `sa-token`在api设计时充分考虑了这一点暴露出多个api进行此类操作
## 有关操作其它账号的api
#### StpUtil.getTokenValueByLoginId(Object login_id)
- 获取指定login_id的tokenValue值
#### StpUtil.logoutByLoginId(Object login_id)
- 指定login_id的会话注销登录踢人下线
#### StpUtil.getSessionByLoginId(Object login_id)
- 获取指定login_id的session
#### StpUtil.hasPermission(Object login_id, Object pcode)
- 指定login_id是否含有指定权限

View File

@@ -0,0 +1,47 @@
# 无cookie模式
---
## 何为无cookie
- 常规PC端鉴权方法一般由cookie进行
- 而cookie有两个特性1、可由后端控制写入2、每次请求自动提交
- 这就使得大多数web前端码农无需任何特殊操作就能完成鉴权的流程因为整个流程都是后端控制完成的
- 而在app、小程序等前后台分离场景中是没有cookie这一功能的此时大多数人都会一脸懵逼咋进行鉴权啊
- 其实很简单
- 不能后端控制写入了,就前端自己写入(难点在**如何将token传递到前端**
- 每次请求不能自动提交了,那就手动提交(难点在前端如何**将token传递到后端**,同时后端**将其读取出来**
## 将token传递到前端
1. 首先调用 `StpUtil.setLoginId(Object login_id)` 进行登录
2. 调用 `StpUtil.getTokenInfo()` 返回当前会话的token值
- 此方法返回一个Map有两个keytokenName和tokenValuetoken的名称和token的值
- 将此Map传递到前台让前端人员将这两个值保存到本地
## 前端将token提交到后端
1. 无论是app还是小程序其传递方式都大同小异
2. 那就是将token塞到请求header里 ,格式为:`{tokenName: tokenValue}`
3. 以经典跨端框架`uni-app`为例:
```
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;
}
// 后续在发起请求时将 header 对象塞到请求头部
```
4. 只要按照如此方法将token值传递到后端`sa-token`就能像传统PC端一样自动读取到token值进行鉴权
5. 你可能会有疑问难道我每个ajax都要写这么一坨岂不是麻烦死了
- 你当然不能每个ajax都写这么一坨因为这种重复代码都是要封装在一个函数里统一调用的
## 其它解决方案?
- 如果你对cookie非常了解那你就会明白所谓cookie本质上就是一个特殊的header参数而已
- 而既然它只是一个header参数我们就能就能手动模拟实现它从而完成鉴权操作
- 这其实是对无cookie模式的另一种解决方案有兴趣的同学可以百度了解一下在此暂不赘述

View File

@@ -0,0 +1,37 @@
# session会话
---
## 账号session
账号session指的是为每个登录账号分配的session
#### StpUtil.getSession()
- 返回当前登录账号的session必须是登录后才能调用
## 自定义session
自定义session指的是未登录状态下以一个特定的值作为key来分配的session
#### SaSessionCustomUtil.isExists(String sessionId)
- 查询指定key的session是否存在
#### SaSessionCustomUtil.getSessionById(String sessionId)
- 获取指定key的session如果没有则新建并返回
#### SaSessionCustomUtil.delSessionById(String sessionId)
- 删除指定key的session
## session相关操作
那么获取到的`SaSession`具体有哪些方法可供操作?
#### getId()
- 返回此session的id
#### setAttribute(String key, Object value)
- 在此session对象上写入值
#### getAttribute(String key)
- 在此session对象上查询值
具体可参考`javax.servlet.http.HttpSession``SaSession`所含方法与其大体类似

60
sa-token-doc/index.css Normal file
View File

@@ -0,0 +1,60 @@
/* ================================== 内容 ====================================== */
/* 总 */
*{margin: 0px; padding: 0px;}
body{font-size: 16px; color: #34495E; font-family: "Source Sans Pro","Helvetica Neue","Arial,sans-serif";}
header{height: 70px; border: 0px #000 solid; position: fixed; width: 100%;}
/* logo部分 */
.logo-box {margin-top: 15px; margin-left: 30px; cursor: pointer; color: #000; float: left;}
.logo-box img {width: 50px; height: 50px; vertical-align: middle;}
.logo-box .logo-text {display: inline-block;vertical-align: middle; font-size: 22px;font-weight: 400;}
/* 右边导航 */
.nav-right{float: right; line-height: 70px; padding-right: 4.5em;}
.nav-right a{padding: 0px 1em; color: #34495E; text-decoration: none; transition: all 0.2s;}
.nav-right a:hover{color: #42B983;}
/* 主要 */
html,body,.main-box{width: 100%; height: 100%; }
.main-box{display: flex; align-items: center; text-align: center;}
.main-box .content-box{flex: 1;}
.content-box h1{font-size: 110px; font-weight: 300; position: relative;}
.content-box h1 small{font-size: 20px; position: absolute; bottom: 0px;}
.sub-title{font-size: 24px; font-weight: 400; margin-top: 30px; margin-bottom: 25px;}
.content-box p{line-height: 30px;}
.btn-box{margin-top: 16px;}
.btn-box a{
border: 1px #42B983 solid;
border-radius: 2rem;
box-sizing: border-box;
color: #42B983;
display: inline-block;
font-size: 1.05rem;
letter-spacing: .1rem;
margin: .5rem 1rem;
padding: 0.9em 2rem;
text-decoration: none;
transition: all .15s ease;
}
/* 最后一个 */
.btn-box a:last-child {
background-color: #42B983;
color: #fff;
}
.btn-box a:hover{color: #000;}
/* 底部 */
footer{
position: fixed;
bottom: 0;
width: 100%;
line-height: 80px;
text-align: center;
}
footer a{color: inherit; text-decoration: none;}
footer a:hover{text-decoration: underline;}
/* 媒体查询 */
@media screen and (max-width: 800px) {
.logo-box,.copyright {display: none;}
}

View File

@@ -1,11 +1,96 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <title>sa-token</title>
<title>sa-token在线文档</title> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta name="keywords" content="sa-token|sa-token框架|sa-token文档|sa-token在线文档|权限认证框架">
<meta name="description" content="sa-token是一个的JavaWeb权限认证框架强大、简单、好用登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、Spring集成...,零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
<link rel="stylesheet" href="https://unpkg.com/docsify/lib/themes/vue.css">
<link rel="shortcut icon" type="image/x-icon" href="doc/logo.png">
<link rel="stylesheet" href="index.css">
</head> </head>
<body> <body>
<h1>sa-token在线文档</h1> <header>
<a href="/" style="text-decoration: none;">
<div class="logo-box">
<img src="doc/logo.png" title="logo">
<span class="logo-text">sa-token</span>
</div>
</a>
<nav class="nav-right">
<a href="/">首页</a>
<a href="/doc/">文档</a>
<a href="/doc/#/more/update-log">更新日志</a>
<!-- github小猫图标 -->
<a href="https://github.com/click33/sa-token" class="github-corner" aria-label="View source on Github" style="position: fixed; right: -16px;">
<svg viewBox="0 0 250 250" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor" class="octo-body"></path>
</svg>
</a>
</nav>
</header>
<!-- 内容部分 -->
<div class="main-box">
<div class="content-box">
<h1>sa-token<small>v1.0.0</small></h1>
<div class="sub-title">一个的JavaWeb权限认证框架强大、简单、好用</div>
<!-- <p>0配置开箱即用低学习成本</p> -->
<p>登录验证、权限验证、自定义session会话、踢人下线、持久层扩展、无cookie模式、模拟他人账号、多账号体系、Spring集成...</p>
<p>零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有</p>
<div class="btn-box">
<a href="https://github.com/click33/sa-token" target="_blank">GitHub</a>
<a href="https://gitee.com/sz6/sa-token" target="_blank">码云</a>
<a href="doc/" target="_self">开发文档</a>
</div>
</div>
</div>
<footer>
<p>
<span class="copyright">Copyright © 2020 &nbsp;</span>
<a href="http://www.miitbeian.gov.cn/" target="_blank">鲁ICP备18046274号-2</a> &nbsp;
QQ交流群<a href="https://jq.qq.com/?_wv=1027&k=5DHN5Ib" target="_blank">784013340</a>
</p>
</footer>
<script>
// 随机数
function randomNum(minNum, maxNum) {
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
}
// 随机一个渐变背景色
function randomBG() {
var c1 = randomNum(0, 255);
var c2 = randomNum(0, 255);
document.body.style.background = 'linear-gradient(to left bottom, hsl(' + c1 + ', 100%, 85%) 0%,hsl(' + c2 +
', 100%, 85%) 100%)';
}
randomBG();
</script>
<!-- 自动提交 -->
<script>
(function() {
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https') {
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
} else {
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
</script>
</body> </body>
</html> </html>