Compare commits

...

23 Commits

Author SHA1 Message Date
shengzhang
50ffda7bef SaTokenManager新增stpLogicMap集合,记录所有StpLogic的初始化,方便查找 2021-03-20 16:51:18 +08:00
shengzhang
a122a8b083 StpUtil添加setTokenValue方法 2021-03-18 23:51:56 +08:00
shengzhang
69ce298a8a 适配token前缀模式 2021-03-18 13:06:24 +08:00
省长
f92e64b9cf update sa-token-doc/doc/use/jur-auth.md. 2021-03-16 17:27:13 +08:00
省长
d3138cefc8 !21 update sa-token-doc/doc/use/jur-auth.md.
Merge pull request !21 from AppleOfGray/N/A
2021-03-16 17:23:41 +08:00
AppleOfGray
71f2522f08 update sa-token-doc/doc/use/jur-auth.md. 2021-03-16 16:43:05 +08:00
省长
a5e9bd6714 !20 update sa-token-doc/doc/use/session.md.
Merge pull request !20 from AppleOfGray/N/A
2021-03-16 16:16:44 +08:00
AppleOfGray
27bb436a90 update sa-token-doc/doc/use/session.md. 2021-03-16 16:13:39 +08:00
省长
d75c91f0ab !19 update sa-token-doc/doc/use/many-account.md.
Merge pull request !19 from AppleOfGray/N/A
2021-03-16 15:56:38 +08:00
AppleOfGray
8ee9f215a6 update sa-token-doc/doc/use/many-account.md. 2021-03-16 15:55:36 +08:00
shengzhang
1df216879c 文档集成Redis章节新增redis配置示例说明,感谢群友 @-) 提供的建议 2021-03-16 14:44:38 +08:00
click33
91998af186 Merge pull request #48 from zhangzi0291/patch-1
修改cookie中没有path的问题
2021-03-16 00:36:34 +08:00
zhangzi0291
f33df38f64 修改cookie中没有path的问题
addCookies时如果path没有指定,path默认为/
2021-03-16 00:33:12 +08:00
shengzhang
aaf4bb8931 Session新增getString(key)方法,方便取值时类型转换 2021-03-15 17:05:07 +08:00
shengzhang
5808710bbf 修复错别字Request->Response 2021-03-14 14:39:21 +08:00
shengzhang
62787c3257 更改路由拦截示例 2021-03-14 00:04:11 +08:00
省长
6dd39505f9 增加友链 2021-03-13 17:28:03 +08:00
省长
d86f72b469 update README.md. 2021-03-13 17:25:41 +08:00
省长
7b7920123a update README.md. 2021-03-13 17:23:46 +08:00
shengzhang
8872982455 优化文档 2021-03-13 00:54:39 +08:00
shengzhang
80f07ae01e 技术栈 2021-03-13 00:47:43 +08:00
shengzhang
540730d9a4 优化文档 2021-03-12 09:51:37 +08:00
省长
03c8abb269 update README.md. 2021-03-12 09:46:11 +08:00
23 changed files with 313 additions and 174 deletions

View File

@@ -58,6 +58,8 @@ sa-token是一个轻量级Java权限认证框架主要解决登录认证
- **路由拦截式鉴权** —— 根据路由拦截鉴权可适配restful模式
- **自动续签** —— 提供两种token过期策略灵活搭配使用还可自动续签
- **会话治理** —— 提供方便灵活的会话查询接口
- **记住我模式** —— 适配[记住我]模式,重启浏览器免验证
- **密码加密** —— 提供密码加密模块可快速MD5、SHA1、SHA256、AES、RSA加密
- **组件自动注入** —— 零配置与Spring等框架集成
- **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
@@ -180,7 +182,9 @@ sa-token秉承着开放的思想欢迎大家贡献代码为框架添砖加
## 友情链接
[**[ okhttps ]** 一个轻量级http通信框架API设计无比优雅支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
[**[ OkHttps ]** 一个轻量级http通信框架API设计无比优雅支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
[**[ 小诺快速开发平台 ]** 基于SpringBoot2 + AntDesignVue全新快速开发平台同时拥有三个版本](https://xiaonuo.vip/index#pricing)
## 交流群

View File

@@ -1,5 +1,8 @@
package cn.dev33.satoken;
import java.util.HashMap;
import java.util.Map;
import cn.dev33.satoken.action.SaTokenAction;
import cn.dev33.satoken.action.SaTokenActionDefaultImpl;
import cn.dev33.satoken.config.SaTokenConfig;
@@ -12,6 +15,7 @@ import cn.dev33.satoken.servlet.SaTokenServlet;
import cn.dev33.satoken.servlet.SaTokenServletDefaultImpl;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpInterfaceDefaultImpl;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.util.SaTokenInsideUtil;
/**
@@ -21,129 +25,148 @@ import cn.dev33.satoken.util.SaTokenInsideUtil;
*/
public class SaTokenManager {
/**
* 配置文件 Bean
*/
private static SaTokenConfig config;
public static SaTokenConfig getConfig() {
if (config == null) {
initConfig();
}
return config;
}
public static void setConfig(SaTokenConfig config) {
SaTokenManager.config = config;
if(config.getIsV()) {
SaTokenInsideUtil.printSaToken();
}
}
public synchronized static void initConfig() {
public static SaTokenConfig getConfig() {
if (config == null) {
setConfig(SaTokenConfigFactory.createConfig());
// 如果对象为空,则使用框架默认方式初始化
synchronized (SaTokenManager.class) {
if (config == null) {
setConfig(SaTokenConfigFactory.createConfig());
}
}
}
return config;
}
/**
* 持久化 Bean
*/
public static SaTokenDao saTokenDao;
public static SaTokenDao getSaTokenDao() {
if (saTokenDao == null) {
initSaTokenDao();
}
return saTokenDao;
}
private static SaTokenDao saTokenDao;
public static void setSaTokenDao(SaTokenDao saTokenDao) {
if(SaTokenManager.saTokenDao != null && (SaTokenManager.saTokenDao instanceof SaTokenDaoDefaultImpl)) {
((SaTokenDaoDefaultImpl)SaTokenManager.saTokenDao).endRefreshTimer();
}
SaTokenManager.saTokenDao = saTokenDao;
}
public synchronized static void initSaTokenDao() {
public static SaTokenDao getSaTokenDao() {
if (saTokenDao == null) {
setSaTokenDao(new SaTokenDaoDefaultImpl());
// 如果对象为空,则使用框架默认方式初始化
synchronized (SaTokenManager.class) {
if (saTokenDao == null) {
setSaTokenDao(new SaTokenDaoDefaultImpl());
}
}
}
return saTokenDao;
}
/**
* 权限认证 Bean
*/
public static StpInterface stpInterface;
public static StpInterface getStpInterface() {
if (stpInterface == null) {
initStpInterface();
}
return stpInterface;
}
private static StpInterface stpInterface;
public static void setStpInterface(StpInterface stpInterface) {
SaTokenManager.stpInterface = stpInterface;
}
public synchronized static void initStpInterface() {
public static StpInterface getStpInterface() {
if (stpInterface == null) {
setStpInterface(new StpInterfaceDefaultImpl());
// 如果对象为空,则使用框架默认方式初始化
synchronized (SaTokenManager.class) {
if (stpInterface == null) {
setStpInterface(new StpInterfaceDefaultImpl());
}
}
}
return stpInterface;
}
/**
* 框架行为 Bean
*/
public static SaTokenAction saTokenAction;
public static SaTokenAction getSaTokenAction() {
if (saTokenAction == null) {
initSaTokenAction();
}
return saTokenAction;
}
private static SaTokenAction saTokenAction;
public static void setSaTokenAction(SaTokenAction saTokenAction) {
SaTokenManager.saTokenAction = saTokenAction;
}
public synchronized static void initSaTokenAction() {
public static SaTokenAction getSaTokenAction() {
if (saTokenAction == null) {
setSaTokenAction(new SaTokenActionDefaultImpl());
// 如果对象为空,则使用框架默认方式初始化
synchronized (SaTokenManager.class) {
if (saTokenAction == null) {
setSaTokenAction(new SaTokenActionDefaultImpl());
}
}
}
return saTokenAction;
}
/**
* Cookie操作 Bean
*/
public static SaTokenCookie saTokenCookie;
public static SaTokenCookie getSaTokenCookie() {
if (saTokenCookie == null) {
initSaTokenCookie();
}
return saTokenCookie;
}
private static SaTokenCookie saTokenCookie;
public static void setSaTokenCookie(SaTokenCookie saTokenCookie) {
SaTokenManager.saTokenCookie = saTokenCookie;
}
public synchronized static void initSaTokenCookie() {
public static SaTokenCookie getSaTokenCookie() {
if (saTokenCookie == null) {
setSaTokenCookie(new SaTokenCookieDefaultImpl());
// 如果对象为空,则使用框架默认方式初始化
synchronized (SaTokenManager.class) {
if (saTokenCookie == null) {
setSaTokenCookie(new SaTokenCookieDefaultImpl());
}
}
}
return saTokenCookie;
}
/**
* Servlet操作 Bean
*/
public static SaTokenServlet saTokenServlet;
public static SaTokenServlet getSaTokenServlet() {
if (saTokenServlet == null) {
initSaTokenServlet();
}
return saTokenServlet;
}
private static SaTokenServlet saTokenServlet;
public static void setSaTokenServlet(SaTokenServlet saTokenServlet) {
SaTokenManager.saTokenServlet = saTokenServlet;
}
public synchronized static void initSaTokenServlet() {
public static SaTokenServlet getSaTokenServlet() {
if (saTokenServlet == null) {
setSaTokenServlet(new SaTokenServletDefaultImpl());
// 如果对象为空,则使用框架默认方式初始化
if (saTokenServlet == null) {
setSaTokenServlet(new SaTokenServletDefaultImpl());
}
}
return saTokenServlet;
}
/**
* StpLogic集合, 记录框架所有成功初始化的StpLogic
*/
public static Map<String, StpLogic> stpLogicMap = new HashMap<String, StpLogic>();
/**
* 向集合中 put 一个 StpLogic
* @param stpLogic
*/
public static void putStpLogic(StpLogic stpLogic) {
stpLogicMap.put(stpLogic.getLoginKey(), stpLogic);
}
/**
* 根据 LoginKey 获取对应的StpLogic如果不存在则返回null
* @param loginKey 对应的LoginKey
* @return 对应的StpLogic
*/
public static StpLogic getStpLogic(String loginKey) {
for (String key : stpLogicMap.keySet()) {
if(key.equals(loginKey)) {
return stpLogicMap.get(key);
}
}
return null;
}
}

View File

@@ -51,6 +51,9 @@ public class SaTokenConfig {
/** 写入Cookie时显式指定的作用域, 常用于单点登录二级域名共享Cookie的场景 */
private String cookieDomain;
/** token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx) */
private String tokenPrefix;
/** 是否在初始化配置时打印版本字符画 */
private Boolean isV = true;
@@ -269,6 +272,22 @@ public class SaTokenConfig {
return this;
}
/**
* @return token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx)
*/
public String getTokenPrefix() {
return tokenPrefix;
}
/**
* @param tokenPrefix token前缀, 格式样例(satoken: Bearer xxxx-xxxx-xxxx-xxxx)
* @return 对象自身
*/
public SaTokenConfig setTokenPrefix(String tokenPrefix) {
this.tokenPrefix = tokenPrefix;
return this;
}
/**
* @return 是否在初始化配置时打印版本字符画
*/
@@ -285,6 +304,7 @@ public class SaTokenConfig {
return this;
}
/**
* toString
@@ -295,8 +315,8 @@ public class SaTokenConfig {
+ ", allowConcurrentLogin=" + allowConcurrentLogin + ", isShare=" + isShare + ", isReadBody="
+ isReadBody + ", isReadHead=" + isReadHead + ", isReadCookie=" + isReadCookie + ", tokenStyle="
+ tokenStyle + ", dataRefreshPeriod=" + dataRefreshPeriod + ", tokenSessionCheckLogin="
+ tokenSessionCheckLogin + ", autoRenew=" + autoRenew + ", cookieDomain=" + cookieDomain + ", isV="
+ isV + "]";
+ tokenSessionCheckLogin + ", autoRenew=" + autoRenew + ", cookieDomain=" + cookieDomain
+ ", tokenPrefix=" + tokenPrefix + ", isV=" + isV + "]";
}

View File

@@ -44,7 +44,7 @@ public class SaTokenCookieUtil {
*/
public static void addCookie(HttpServletResponse response, String name, String value, String path, String domain, int timeout) {
Cookie cookie = new Cookie(name, value);
if(SaTokenInsideUtil.isEmpty(path) == false) {
if(SaTokenInsideUtil.isEmpty(path) == true) {
path = "/";
}
if(SaTokenInsideUtil.isEmpty(domain) == false) {
@@ -95,4 +95,4 @@ public class SaTokenCookieUtil {
}
}
}
}

View File

@@ -237,27 +237,6 @@ public class SaSession implements Serializable {
}
// ----------------------- 存取值 (类型转换)
/**
* 从Session中取值转化为Object类型
* @param key key
* @return 值
*/
public Object getObject(String key) {
return getAttribute(key);
}
/**
* 判断一个值是否为null
* @param value 指定值
* @return 此value是否为null
*/
public boolean valueIsNull(Object value) {
return value == null || value.equals("");
}
// ----------------------- 一些操作
/**
@@ -279,4 +258,65 @@ public class SaSession implements Serializable {
}
}
// ----------------------- 存取值 (类型转换)
/**
* 判断一个值是否为null
* @param value 指定值
* @return 此value是否为null
*/
public boolean valueIsNull(Object value) {
return value == null || value.equals("");
}
/**
* 从Session中取值并转化为Object类型
* @param key key
* @return 值
*/
public Object getObject(String key) {
return getAttribute(key);
}
/**
* 从Session中取值并转化为String类型
* @param key key
* @return 值
*/
public String getString(String key) {
Object value = getObject(key);
if(value == null) {
return null;
}
return String.valueOf(value);
}
/**
* 从Session中取值并转化为int类型如果value为空则返回0
* @param key key
* @return 值
*/
public int getInt(String key) {
Object value = getObject(key);
if(valueIsNull(value)) {
return 0;
}
return Integer.valueOf(String.valueOf(value));
}
/**
* 从Session中取值并转化为long类型如果value为空则返回0
* @param key key
* @return 值
*/
public long getLong(String key) {
Object value = getObject(key);
if(valueIsNull(value)) {
return 0;
}
return Long.valueOf(String.valueOf(value));
}
}

View File

@@ -24,6 +24,7 @@ import cn.dev33.satoken.fun.SaFunction;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.TokenSign;
import cn.dev33.satoken.util.SaTokenConsts;
import cn.dev33.satoken.util.SaTokenInsideUtil;
/**
* sa-token 权限验证,逻辑实现类
@@ -44,6 +45,8 @@ public class StpLogic {
*/
public StpLogic(String loginKey) {
this.loginKey = loginKey;
// 在 SaTokenManager 中记录下此 StpLogic以便根据 LoginKey 进行查找此对象
SaTokenManager.putStpLogic(this);
}
/**
@@ -84,6 +87,23 @@ public class StpLogic {
return SaTokenManager.getSaTokenAction().createToken(loginId, loginKey);
}
/**
* 在当前会话写入当前tokenValue
* @param tokenValue token值
*/
public void setTokenValue(String tokenValue, int cookieTimeout){
// 将token保存到本次request里
HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest();
request.setAttribute(splicingKeyJustCreatedSave(), tokenValue);
// 注入Cookie
SaTokenConfig config = getConfig();
if(config.getIsReadCookie() == true){
HttpServletResponse response = SaTokenManager.getSaTokenServlet().getResponse();
SaTokenManager.getSaTokenCookie().addCookie(response, getTokenName(), tokenValue,
"/", config.getCookieDomain(), cookieTimeout);
}
}
/**
* 获取当前tokenValue
* @return 当前tokenValue
@@ -115,7 +135,16 @@ public class StpLogic {
}
}
// 5. 返回
// 5. 如果打开了前缀模式
String tokenPrefix = getConfig().getTokenPrefix();
if(SaTokenInsideUtil.isEmpty(tokenPrefix) == false && SaTokenInsideUtil.isEmpty(tokenValue) == false) {
// 如果token以指定的前缀开头, 则裁剪掉它
if(tokenValue.startsWith(tokenPrefix + " ")) {
tokenValue = tokenValue.substring(tokenPrefix.length() + 1);
}
}
// 6. 返回
return tokenValue;
}
@@ -175,7 +204,6 @@ public class StpLogic {
public void setLoginId(Object loginId, SaLoginModel loginModel) {
// ------ 1、获取相应对象
HttpServletRequest request = SaTokenManager.getSaTokenServlet().getRequest();
SaTokenConfig config = getConfig();
SaTokenDao dao = SaTokenManager.getSaTokenDao();
loginModel.build(config);
@@ -190,7 +218,7 @@ public class StpLogic {
}
} else {
// --- 如果不允许并发登录
// 如果此时[id-session]不为null说明此账号在其他地正在登录现在需要先把其它地的同设备token标记为被顶下线
// 如果此时[user-session]不为null说明此账号在其他地正在登录现在需要先把其它地的同设备token标记为被顶下线
SaSession session = getSessionByLoginId(loginId, false);
if(session != null) {
List<TokenSign> tokenSignList = session.getTokenSignList();
@@ -200,7 +228,7 @@ public class StpLogic {
dao.update(splicingKeyTokenValue(tokenSign.getValue()), NotLoginException.BE_REPLACED);
// 2. 清理掉[token-最后操作时间]
clearLastActivity(tokenSign.getValue());
// 3. 清理账号session上的token签名记录
// 3. 清理user-session上的token签名记录
session.removeTokenSign(tokenSign.getValue());
}
}
@@ -227,16 +255,10 @@ public class StpLogic {
// ------ 4. 持久化其它数据
// token -> uid
dao.set(splicingKeyTokenValue(tokenValue), String.valueOf(loginId), loginModel.getTimeout());
// 将token保存到本次request里
request.setAttribute(splicingKeyJustCreatedSave(), tokenValue);
// 写入 [最后操作时间]
setLastActivityToNow(tokenValue);
// 注入Cookie
if(config.getIsReadCookie() == true){
HttpServletResponse response = SaTokenManager.getSaTokenServlet().getResponse();
SaTokenManager.getSaTokenCookie().addCookie(response, getTokenName(), tokenValue,
"/", config.getCookieDomain(), loginModel.getCookieTimeout());
}
// 在当前会话写入当前tokenValue
setTokenValue(tokenValue, loginModel.getCookieTimeout());
}
/**
@@ -577,16 +599,11 @@ public class StpLogic {
if(tokenValue == null || Objects.equals(tokenValue, "")) {
// 随机一个token送给Ta
tokenValue = createTokenValue(null);
// Request做上标记
SaTokenManager.getSaTokenServlet().getRequest().setAttribute(splicingKeyJustCreatedSave(), tokenValue);
// 写入 [最后操作时间]
setLastActivityToNow(tokenValue);
// cookie注入
if(getConfig().getIsReadCookie() == true){
int cookieTimeout = (int)(getConfig().getTimeout() == SaTokenDao.NEVER_EXPIRE ? Integer.MAX_VALUE : getConfig().getTimeout());
SaTokenManager.getSaTokenCookie().addCookie(SaTokenManager.getSaTokenServlet().getResponse(), getTokenName(), tokenValue,
"/", getConfig().getCookieDomain(), cookieTimeout);
}
// 在当前会话写入这个tokenValue
int cookieTimeout = (int)(getConfig().getTimeout() == SaTokenDao.NEVER_EXPIRE ? Integer.MAX_VALUE : getConfig().getTimeout());
setTokenValue(tokenValue, cookieTimeout);
}
}
// 返回这个token对应的专属session
@@ -1092,7 +1109,7 @@ public class StpLogic {
* @return key
*/
public String splicingKeySwitch() {
return SaTokenConsts.SWITCH_TO_SAVE_KEY + getLoginKey();
return SaTokenConsts.SWITCH_TO_SAVE_KEY + loginKey;
}
/**
@@ -1100,7 +1117,7 @@ public class StpLogic {
* @return key
*/
public String splicingKeyJustCreatedSave() {
return SaTokenConsts.JUST_CREATED_SAVE_KEY + getLoginKey();
return SaTokenConsts.JUST_CREATED_SAVE_KEY + loginKey;
}

View File

@@ -36,6 +36,14 @@ public class StpUtil {
return stpLogic.getTokenName();
}
/**
* 在当前会话写入当前tokenValue
* @param tokenValue token值
*/
public static void setTokenValue(String tokenValue, int cookieTimeout){
stpLogic.setTokenValue(tokenValue, cookieTimeout);
}
/**
* 获取当前tokenValue
* @return 当前tokenValue

View File

@@ -8,7 +8,7 @@ import cn.dev33.satoken.SaTokenManager;
@SpringBootApplication
public class SaTokenDemoApplication {
public static void main(String[] args) throws Exception {
public static void main(String[] args) {
SpringApplication.run(SaTokenDemoApplication.class, args);
System.out.println("\n启动成功sa-token配置如下" + SaTokenManager.getConfig());
}

View File

@@ -9,6 +9,8 @@ import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
/**
* sa-token代码方式进行配置
* @author kong
*
*/
@Configuration
public class MySaTokenConfig implements WebMvcConfigurer {
@@ -34,5 +36,4 @@ public class MySaTokenConfig implements WebMvcConfigurer {
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
}
}

View File

@@ -248,6 +248,7 @@ public class TestController {
// .setIsLastingCookie(true) // 是否为持久Cookie临时Cookie在浏览器关闭时会自动删除持久Cookie在重新打开后依然存在
// .setTimeout(60 * 60 * 24 * 7) // 指定此次登录token的有效期, 单位:秒 如未指定自动取全局配置的timeout值
// );
StpUtil.getTokenSession();
return AjaxJson.getSuccess("访问成功");
}

View File

@@ -19,6 +19,7 @@ spring:
token-style: uuid
# redis配置
redis:
# Redis数据库索引默认为0

View File

@@ -58,6 +58,8 @@ sa-token是一个轻量级Java权限认证框架主要解决登录认证
- **路由拦截式鉴权** —— 根据路由拦截鉴权可适配restful模式
- **自动续签** —— 提供两种token过期策略灵活搭配使用还可自动续签
- **会话治理** —— 提供方便灵活的会话查询接口
- **记住我模式** —— 适配[记住我]模式,重启浏览器免验证
- **密码加密** —— 提供密码加密模块可快速MD5、SHA1、SHA256、AES、RSA加密
- **组件自动注入** —— 零配置与Spring等框架集成
- **更多功能正在集成中...** —— 如有您有好想法或者建议,欢迎加群交流
@@ -182,6 +184,7 @@ sa-token秉承着开放的思想欢迎大家贡献代码为框架添砖加
## 友情链接
[**[ okhttps ]** 一个轻量级http通信框架API设计无比优雅支持 WebSocket 以及 Stomp 协议](https://gitee.com/ejlchina-zhxu/okhttps)
[**[ 小诺快速开发平台 ]** 基于SpringBoot2 + AntDesignVue全新快速开发平台同时拥有三个版本](https://xiaonuo.vip/index#pricing)
## 交流群
QQ交流群[1002350610 点击加入](https://jq.qq.com/?_wv=1027&k=45H977HM)

View File

@@ -37,6 +37,7 @@
- [token有效期详解](/fun/token-timeout)
- [Session模型详解](/fun/session-model)
- [TokenInfo参数详解](/fun/token-info)
- [框架源码所有技术栈](/fun/tech-stack)

View File

@@ -0,0 +1,15 @@
# sa-token 源码用到的所有技术栈
包括但不限于以下:
- Maven多模块项目
- Servlet API、临时Cookie与永久Cookie、Request参数获取
- SpringBoot2.0、Redis、Jackson、Hutool、jwt
- SpringBoot自定义starter、Spring包扫码 + 依赖注入、AOP注解切面、yml配置映射、拦截器
- Java8 接口与default实现、静态方法、枚举、定时器、异常类、泛型、反射、IO流、自定义注解、Lambda表达式、函数式编程
- package-info注释、Serializable序列化接口、synchronized锁
- java加密算法MD5、SHA1、SHA256、AES、RSA
- OAuth2.0、同域单点登录、集群与分布式、路由Ant匹配

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>sa-token</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="sa-token是一个java权限认证框架功能全面上手简单登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
<meta name="description" content="sa-token是一个java权限认证框架功能全面上手简单登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成...有了sa-token你所有的权限认证问题都不再是问题">
<meta name="keywords" content="sa-token,sa-token框架,sa-token文档,java权限认证">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="logo.png">

View File

@@ -19,6 +19,7 @@
[[vue-next-admin] - 一套为开发者快速开发准备的基于 vue2.x 越看越精彩的后台管理系统一站式平台模板](https://gitee.com/lyt-top/vue-admin-wonderful)
[[小诺快速开发平台] - 基于SpringBoot2 + AntDesignVue全新快速开发平台同时拥有三个版本](https://xiaonuo.vip/index#pricing)
<br>
虚位以待...

View File

@@ -47,7 +47,33 @@ Sa-token默认将会话数据保存在内存中此模式读写速度最快
```
**2. 引入了依赖我还需要为Redis配置连接信息吗** <br>
需要只有项目初始化了正确的Redis实例`sa-token`才可以使用Redis进行数据持久化参考[application-dev.yml](https://gitee.com/sz6/sa-plus/blob/master/sp-server/src/main/resources/application-dev.yml)
需要只有项目初始化了正确的Redis实例`sa-token`才可以使用Redis进行数据持久化参考以下`yml配置`
``` java
# 端口
spring:
# redis配置
redis:
# Redis数据库索引默认为0
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
# password:
# 连接超时时间(毫秒)
timeout: 1000ms
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
```
**3. 集成Redis后是我额外手动保存数据还是框架自动保存** <br>

View File

@@ -9,6 +9,7 @@
再往底了说,就是每个账号都会拥有一个权限码集合,我来验证这个集合中是否包含指定的权限码 <br/>
例如:当前账号拥有权限码集合:`["user:add", "user:delete", "user:get"]`,这时候我来验证权限 `"user:update"`,则其结果就是:**验证失败,禁止访问**
(注意: 冒号无特殊含义,可有可无)
![无权限](../static/not-jur.png)

View File

@@ -44,11 +44,11 @@
``` java
// 底层的 StpLogic 对象
public static StpLogic stpLogic = new StpLogic("user") {
// 重写 `getTokenName` 函数,返回一个与 `StpUtil` 不同的token名称, 防止冲突
// 重写 `splicingKeyTokenName` 函数,返回一个与 `StpUtil` 不同的token名称, 防止冲突
@Override
public String getTokenName() {
return super.getKeyTokenName() + "-user";
}
public String splicingKeyTokenName() {
return super.splicingKeyTokenName()+"-user";
}
};
```

View File

@@ -10,7 +10,7 @@
## 1、注册路由拦截器
`springboot2.0`为例, 新建配置类`MySaTokenConfig.java`
`SpringBoot2.0`为例, 新建配置类`MySaTokenConfig.java`
``` java
@Configuration
public class MySaTokenConfig implements WebMvcConfigurer {
@@ -22,49 +22,19 @@ public class MySaTokenConfig implements WebMvcConfigurer {
}
}
```
以上代码,我们注册了一个登录验证拦截器,并且排除了`/user/doLogin`接口用来开放登录 <br>
以上代码,我们注册了一个登录验证拦截器,并且排除了`/user/doLogin`接口用来开放登录(除了`/user/doLogin`以外的所有接口都需要登录才能访问) <br>
那么我们如何进行权限认证拦截呢,且往下看
## 2、所有拦截器示例
``` java
@Configuration
public class MySaTokenConfig implements WebMvcConfigurer {
// 注册sa-token的所有拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册一个登录验证拦截器
registry.addInterceptor(SaRouteInterceptor.createLoginVal()).addPathPatterns("/**").excludePathPatterns("/user/doLogin");
// 注册一个角色认证拦截器
registry.addInterceptor(SaRouteInterceptor.createRoleVal("super-admin")).addPathPatterns("/**");
// 注册一个权限认证拦截器
registry.addInterceptor(SaRouteInterceptor.createPermissionVal("user:add", "user:deelete")).addPathPatterns("/UserController/**");
// 注册一个自定义认证拦截器 (可以写任意认证代码)
registry.addInterceptor(new SaRouteInterceptor((request, response, handler)->{
System.out.println("---------- 进入自定义认证 --------------- ");
// 你可以在这里写任意认证代码, 例如: StpUtil.checkLogin();
})).addPathPatterns("/**");
}
}
```
(你不必像上面的示例一样注册所有拦截器,只要按需注册即可
## 3、让我们利用自定义拦截器做点快活的事情
你可以根据路由划分模块,不同模块不同鉴权
## 2、自定义权限验证规则
你可以使用函数式编程自定义验证规则
``` java
@Configuration
public class MySaTokenConfig implements WebMvcConfigurer {
// 注册sa-token的所有拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册路由拦截器,自定义验证规则
registry.addInterceptor(new SaRouteInterceptor((request, response, handler)->{
// 根据路由划分模块,不同模块不同鉴权
SaRouterUtil.match("/user/**", () -> StpUtil.checkPermission("user"));
@@ -78,27 +48,8 @@ public class MySaTokenConfig implements WebMvcConfigurer {
}
```
## 4、多账号模式下使用方式
很简单,把StpUtil类换成新的权限验证类(比如多账号验证示例里面的StpUserUtil)即可其它调用方法不变
``` java
@Configuration
public class MySaTokenConfig implements WebMvcConfigurer {
// 注册sa-token的所有拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaRouteInterceptor((request, response, handler)->{
// 根据路由划分模块,不同模块不同鉴权
SaRouterUtil.match("/user/**", () -> StpUserUtil.checkPermission("user"));
SaRouterUtil.match("/admin/**", () -> StpUtil.checkPermission("admin"));
})).addPathPatterns("/**");
}
}
```
## 5、完整示例
最终的代码,可能会类似于下面的样子:
## 3、完整示例
所有用法示例:
``` java
@Configuration
@@ -106,12 +57,15 @@ public class MySaTokenConfig implements WebMvcConfigurer {
// 注册sa-token的拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 自定义验证拦截器
// 注册路由拦截器,自定义验证规则
registry.addInterceptor(new SaRouteInterceptor((request, response, handler) -> {
// 登录验证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouterUtil.match("/**", "/user/doLogin", () -> StpUtil.checkLogin());
// 登录验证 -- 排除多个路径
SaRouterUtil.match(Arrays.asList("/**"), Arrays.asList("/user/doLogin", "/user/reg"), () -> StpUtil.checkLogin());
// 角色认证 -- 拦截以 admin 开头的路由,必须具备[admin]角色或者[super-admin]角色才可以通过认证
SaRouterUtil.match("/admin/**", () -> StpUtil.checkRoleOr("admin", "super-admin"));
@@ -126,6 +80,16 @@ public class MySaTokenConfig implements WebMvcConfigurer {
// 匹配 restful 风格路由
SaRouterUtil.match("/article/get/{id}", () -> StpUtil.checkPermission("article"));
// 检查请求方式
SaRouterUtil.match("/notice/**", () -> {
if(request.getMethod().equals(HttpMethod.GET.toString())) {
StpUtil.checkPermission("notice");
}
});
// 在多账号模式下可以使用任意StpUtil进行校验
SaRouterUtil.match("/user/**", () -> StpUserUtil.checkLogin());
})).addPathPatterns("/**");
}
}

View File

@@ -81,3 +81,16 @@ session.logout(); // 注销此Session会话 (从持久
具体可参考`javax.servlet.http.HttpSession``SaSession`所含方法与其大体类似
### Session环境隔离说明
在springboot环境下取得的session环境和通过StpUtil获取的session环境并不相通, 示例
``` java
@PostMapping("/resetPoints")
public void reset(HttpSession session) {
session.setAttribute("pointsKey", 66);
SaSession session2 = StpUtil.getSession();
Object value = session2.getAttribute("pointsKey");
System.out.println(value)
// 输出null
}
```
而且, 在使用sa-token多账号模式下, 不同的StpUtil获取的session之间也是环境隔离

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>sa-token</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="sa-token是一个java权限认证框架功能全面上手简单登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有">
<meta name="description" content="sa-token是一个java权限认证框架功能全面上手简单登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成...有了sa-token你所有的权限认证问题都不再是问题">
<meta name="keywords" content="sa-token,sa-token框架,sa-token文档,java权限认证">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="doc/logo.png">
@@ -44,7 +44,7 @@
<h1>sa-token<small>v1.14.0</small></h1>
<div class="sub-title">这可能是史上功能最全的java权限认证框架</div>
<!-- <p>0配置开箱即用低学习成本</p> -->
<p>登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、Spring集成...</p>
<p>登录验证、权限验证、Session会话、踢人下线、集成Redis、分布式会话、单点登录、前后台分离、记住我模式、模拟他人账号、临时身份切换、多账号体系、注解式鉴权、路由拦截式鉴权、花式token、自动续签、同端互斥登录、会话治理、密码加密、jwt集成、Spring集成...</p>
<!-- <p>零配置开箱即用,覆盖所有应用场景,你所需要的功能,这里都有</p> -->
<p>有了sa-token你所有的权限认证问题都不再是问题</p>
<div class="btn-box">

View File

@@ -34,7 +34,7 @@ public class SpringMVCUtil {
public static HttpServletResponse getResponse() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(servletRequestAttributes == null) {
throw new SaTokenException("非Web上下文无法获取Request");
throw new SaTokenException("非Web上下文无法获取Response");
}
return servletRequestAttributes.getResponse();
}