添加solon适配及demo

This commit is contained in:
noear 2021-05-30 21:24:04 +08:00
parent 0748f7941d
commit a2d1121b66
24 changed files with 1360 additions and 1 deletions

View File

@ -21,7 +21,8 @@
<module>sa-token-core</module>
<module>sa-token-starter</module>
<module>sa-token-plugin</module>
<!-- <module>sa-token-demo\sa-token-demo-quick-login</module> -->
<module>sa-token-demo/sa-token-demo-solon</module>
<module>sa-token-demo/sa-token-demo-springboot</module>
</modules>
<!-- 开源协议 apache 2.0 -->

View File

@ -0,0 +1,85 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-demo-solon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 定义sa-token版本号 -->
<properties>
<sa-token-version>1.19.0</sa-token-version>
</properties>
<dependencies>
<!-- springboot依赖 -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-web</artifactId>
<version>1.4.8</version>
</dependency>
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-solon-plugin</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
<!-- <dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis</artifactId>
<version>${sa-token-version}</version>
</dependency> -->
<!-- sa-token整合redis (使用jackson序列化方式) -->
<!-- <dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${sa-token-version}</version>
</dependency> -->
<!-- 提供redis连接池 -->
<!-- <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency> -->
<!-- sa-token整合SpringAOP实现注解鉴权 -->
<!-- <dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-aop</artifactId>
<version>${sa-token-version}</version>
</dependency> -->
<!-- hutool工具类用来生成雪花算法唯一id -->
<!-- <dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.4</version>
</dependency> -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,17 @@
package com.pj;
import cn.dev33.satoken.SaManager;
import org.noear.solon.Solon;
/**
* sa-token整合 solon 示例
* @author noear
*
*/
public class SaTokenDemoApp {
public static void main(String[] args) {
Solon.start(SaTokenDemoApp.class, args);
System.out.println("\n启动成功sa-token配置如下" + SaManager.getConfig());
}
}

View File

@ -0,0 +1,60 @@
package com.pj.satoken;
import cn.dev33.satoken.solon.integration.SaTokenPathFilter;
import com.pj.util.AjaxJson;
import cn.dev33.satoken.context.SaHolder;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
/**
* [Sa-Token 权限认证] 配置类
* @author kong
*
*/
@Configuration
public class SaTokenConfigure {
/**
* 注册 [sa-token全局过滤器]
*/
@Bean
public SaTokenPathFilter tokenPathFilter() {
return new SaTokenPathFilter()
// 指定 [拦截路由] [放行路由]
.addInclude("/**").addExclude("/favicon.ico")
// 认证函数: 每次请求执行
.setAuth(r -> {
// System.out.println("---------- sa全局认证");
// SaRouterUtil.match("/test/test", () -> new Object());
})
// 异常处理函数每次认证函数发生异常时执行此函数
.setError(e -> {
System.out.println("---------- sa全局异常 ");
return AjaxJson.getError(e.getMessage());
})
// 前置函数在每次认证函数之前执行
.setBeforeAuth(r -> {
// ---------- 设置一些安全响应头 ----------
SaHolder.getResponse()
// 服务器名称
.setServer("sa-server")
// 是否可以在iframe显示视图 DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
.setHeader("X-Frame-Options", "SAMEORIGIN")
// 是否启用浏览器默认XSS防护 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时停止渲染页面
.setHeader("X-Frame-Options", "1; mode=block")
// 禁用浏览器内容嗅探
.setHeader("X-Content-Type-Options", "nosniff")
;
})
;
}
}

View File

@ -0,0 +1,44 @@
package com.pj.satoken;
import java.util.ArrayList;
import java.util.List;
import cn.dev33.satoken.stp.StpInterface;
import org.noear.solon.annotation.Component;
/**
* 自定义权限验证接口扩展
*/
@Component // 打开此注解保证此类被springboot扫描即可完成sa-token的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {
/**
* 返回一个账号所拥有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginKey) {
// 本list仅做模拟实际项目中要根据具体业务逻辑来查询权限
List<String> list = new ArrayList<String>();
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;
}
/**
* 返回一个账号所拥有的角色标识集合
*/
@Override
public List<String> getRoleList(Object loginId, String loginKey) {
// 本list仅做模拟实际项目中要根据具体业务逻辑来查询角色
List<String> list = new ArrayList<String>();
list.add("admin");
list.add("super-admin");
return list;
}
}

View File

@ -0,0 +1,44 @@
package com.pj.test;
import cn.dev33.satoken.exception.*;
import com.pj.util.AjaxJson;
import org.noear.solon.annotation.Component;
import org.noear.solon.core.event.EventListener;
import org.noear.solon.core.handle.Context;
/**
* 全局异常处理
*/
@Component
public class GlobalException implements EventListener<Exception> {
@Override
public void onEvent(Exception e) {
if (e instanceof SaTokenException) {
Context c = Context.current();
if (c != null) {
// 不同异常返回不同状态码
AjaxJson aj = null;
if (e instanceof NotLoginException) { // 如果是未登录异常
NotLoginException ee = (NotLoginException) e;
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
} else if (e instanceof NotRoleException) { // 如果是角色异常
NotRoleException ee = (NotRoleException) e;
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
} else if (e instanceof NotPermissionException) { // 如果是权限异常
NotPermissionException ee = (NotPermissionException) e;
aj = AjaxJson.getNotJur("无此权限:" + ee.getCode());
} else if (e instanceof DisableLoginException) { // 如果是被封禁异常
DisableLoginException ee = (DisableLoginException) e;
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
} else { // 普通异常, 输出500 + 异常信息
aj = AjaxJson.getError(e.getMessage());
}
c.result = aj;
}
}
}
}

View File

@ -0,0 +1,35 @@
package com.pj.test;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Mapping;
import com.pj.util.AjaxJson;
import cn.dev33.satoken.stp.StpUtil;
import org.noear.solon.annotation.Param;
/**
* 测试: 同域单点登录
* @author kong
*/
@Controller
@Mapping("/sso/")
public class SSOController {
// 测试进行登录
@Mapping("doLogin")
public AjaxJson doLogin(@Param(defaultValue = "10001") String id) {
System.out.println("---------------- 进行登录 ");
StpUtil.setLoginId(id);
return AjaxJson.getSuccess("登录成功: " + id);
}
// 测试是否登录
@Mapping("isLogin")
public AjaxJson isLogin() {
System.out.println("---------------- 是否登录 ");
boolean isLogin = StpUtil.isLogin();
return AjaxJson.getSuccess("是否登录: " + isLogin);
}
}

View File

@ -0,0 +1,61 @@
package com.pj.test;
import java.util.ArrayList;
import java.util.List;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Mapping;
import com.pj.util.AjaxJson;
import com.pj.util.Ttime;
import cn.dev33.satoken.stp.StpUtil;
/**
* 压力测试
* @author kong
*
*/
@Controller
@Mapping("/s-test/")
public class StressTestController {
// 测试 浏览器访问 http://localhost:8081/s-test/login
// 测试前请先将 is-read-cookie 配置为 false
@Mapping("login")
public AjaxJson login() {
// StpUtil.getTokenSession().logout();
// StpUtil.logoutByLoginId(10001);
int count = 10; // 循环多少轮
int loginCount = 10000; // 每轮循环多少次
// 循环10次 取平均时间
List<Double> list = new ArrayList<>();
for (int i = 1; i <= count; i++) {
System.out.println("\n---------------------第" + i + "轮---------------------");
Ttime t = new Ttime().start();
// 每次登录的次数
for (int j = 1; j <= loginCount; j++) {
StpUtil.setLoginId("1000" + j, "PC-" + j);
if(j % 1000 == 0) {
System.out.println("已登录:" + j);
}
}
t.end();
list.add((t.returnMs() + 0.0) / 1000);
System.out.println("" + i + "" + "用时:" + t.toString());
}
// System.out.println(((SaTokenDaoDefaultImpl)SaTokenManager.getSaTokenDao()).dataMap.size());
System.out.println("\n---------------------测试结果---------------------");
System.out.println(list.size() + "次测试: " + list);
double ss = 0;
for (int i = 0; i < list.size(); i++) {
ss += list.get(i);
}
System.out.println("平均用时: " + ss / list.size());
return AjaxJson.getSuccess();
}
}

View File

@ -0,0 +1,252 @@
package com.pj.test;
import java.util.Date;
import java.util.List;
import com.pj.util.AjaxJson;
import com.pj.util.Ttime;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.annotation.Param;
/**
* 测试专用Controller
* @author kong
*
*/
@Controller
@Mapping("/test/")
public class TestController {
// 测试登录接口 浏览器访问 http://localhost:8081/test/login
@Mapping("login")
public AjaxJson login(@Param(defaultValue="10001") String id) {
System.out.println("======================= 进入方法,测试登录接口 ========================= ");
System.out.println("当前会话的token" + StpUtil.getTokenValue());
System.out.println("当前是否登录:" + StpUtil.isLogin());
System.out.println("当前登录账号:" + StpUtil.getLoginIdDefaultNull());
StpUtil.setLoginId(id); // 在当前会话登录此账号
System.out.println("登录成功");
System.out.println("当前是否登录:" + StpUtil.isLogin());
System.out.println("当前登录账号:" + StpUtil.getLoginId());
// System.out.println("当前登录账号并转为int" + StpUtil.getLoginIdAsInt());
System.out.println("当前登录设备:" + StpUtil.getLoginDevice());
// System.out.println("当前token信息" + StpUtil.getTokenInfo());
return AjaxJson.getSuccess();
}
// 测试退出登录 浏览器访问 http://localhost:8081/test/logout
@Mapping("logout")
public AjaxJson logout() {
StpUtil.logout();
// StpUtil.logoutByLoginId(10001);
return AjaxJson.getSuccess();
}
// 测试角色接口 浏览器访问 http://localhost:8081/test/testRole
@Mapping("testRole")
public AjaxJson testRole() {
System.out.println("======================= 进入方法,测试角色接口 ========================= ");
System.out.println("是否具有角色标识 user " + StpUtil.hasRole("user"));
System.out.println("是否具有角色标识 admin " + StpUtil.hasRole("admin"));
System.out.println("没有admin权限就抛出异常");
StpUtil.checkRole("admin");
System.out.println("在【admin、user】中只要拥有一个就不会抛出异常");
StpUtil.checkRoleOr("admin", "user");
System.out.println("在【admin、user】中必须全部拥有才不会抛出异常");
StpUtil.checkRoleAnd("admin", "user");
System.out.println("角色测试通过");
return AjaxJson.getSuccess();
}
// 测试权限接口 浏览器访问 http://localhost:8081/test/testJur
@Mapping("testJur")
public AjaxJson testJur() {
System.out.println("======================= 进入方法,测试权限接口 ========================= ");
System.out.println("是否具有权限101" + StpUtil.hasPermission("101"));
System.out.println("是否具有权限user-add" + StpUtil.hasPermission("user-add"));
System.out.println("是否具有权限article-get" + StpUtil.hasPermission("article-get"));
System.out.println("没有user-add权限就抛出异常");
StpUtil.checkPermission("user-add");
System.out.println("在【101、102】中只要拥有一个就不会抛出异常");
StpUtil.checkPermissionOr("101", "102");
System.out.println("在【101、102】中必须全部拥有才不会抛出异常");
StpUtil.checkPermissionAnd("101", "102");
System.out.println("权限测试通过");
return AjaxJson.getSuccess();
}
// 测试会话session接口 浏览器访问 http://localhost:8081/test/session
@Mapping("session")
public AjaxJson session() {
System.out.println("======================= 进入方法测试会话session接口 ========================= ");
System.out.println("当前是否登录:" + StpUtil.isLogin());
System.out.println("当前登录账号session的id" + StpUtil.getSession().getId());
System.out.println("当前登录账号session的id" + StpUtil.getSession().getId());
System.out.println("测试取值name" + StpUtil.getSession().getAttribute("name"));
StpUtil.getSession().setAttribute("name", new Date()); // 写入一个值
System.out.println("测试取值name" + StpUtil.getSession().getAttribute("name"));
System.out.println( ONode.stringify(StpUtil.getSession()));
return AjaxJson.getSuccess();
}
// 测试自定义session接口 浏览器访问 http://localhost:8081/test/session2
@Mapping("session2")
public AjaxJson session2() {
System.out.println("======================= 进入方法测试自定义session接口 ========================= ");
// 自定义session就是无需登录也可以使用 的session 比如拿用户的手机号当做 key 来获取 session
System.out.println("自定义 session的id为" + SaSessionCustomUtil.getSessionById("1895544896").getId());
System.out.println("测试取值name" + SaSessionCustomUtil.getSessionById("1895544896").getAttribute("name"));
SaSessionCustomUtil.getSessionById("1895544896").setAttribute("name", "张三"); // 写入值
System.out.println("测试取值name" + SaSessionCustomUtil.getSessionById("1895544896").getAttribute("name"));
System.out.println("测试取值name" + SaSessionCustomUtil.getSessionById("1895544896").getAttribute("name"));
return AjaxJson.getSuccess();
}
// ----------
// 测试token专属session 浏览器访问 http://localhost:8081/test/getTokenSession
@Mapping("getTokenSession")
public AjaxJson getTokenSession() {
System.out.println("======================= 进入方法测试会话session接口 ========================= ");
System.out.println("当前是否登录:" + StpUtil.isLogin());
System.out.println("当前token专属session: " + StpUtil.getTokenSession().getId());
System.out.println("测试取值name" + StpUtil.getTokenSession().getAttribute("name"));
StpUtil.getTokenSession().setAttribute("name", "张三"); // 写入一个值
System.out.println("测试取值name" + StpUtil.getTokenSession().getAttribute("name"));
return AjaxJson.getSuccess();
}
// 打印当前token信息 浏览器访问 http://localhost:8081/test/tokenInfo
@Mapping("tokenInfo")
public AjaxJson tokenInfo() {
System.out.println("======================= 进入方法打印当前token信息 ========================= ");
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
System.out.println(tokenInfo);
return AjaxJson.getSuccessData(tokenInfo);
}
// 测试注解式鉴权 浏览器访问 http://localhost:8081/test/atCheck
@SaCheckLogin // 注解式鉴权当前会话必须登录才能通过
@SaCheckRole("super-admin") // 注解式鉴权当前会话必须具有指定角色标识才能通过
@SaCheckPermission("user-add") // 注解式鉴权当前会话必须具有指定权限才能通过
@Mapping("atCheck")
public AjaxJson atCheck() {
System.out.println("======================= 进入方法,测试注解鉴权接口 ========================= ");
System.out.println("只有通过注解鉴权,才能进入此方法");
// StpUtil.checkActivityTimeout();
// StpUtil.updateLastActivityToNow();
return AjaxJson.getSuccess();
}
// 测试注解式鉴权 浏览器访问 http://localhost:8081/test/atJurOr
@Mapping("atJurOr")
@SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR) // 注解式鉴权只要具有其中一个权限即可通过校验
public AjaxJson atJurOr() {
return AjaxJson.getSuccessData("用户信息");
}
// [活动时间] 续签 http://localhost:8081/test/rene
@Mapping("rene")
public AjaxJson rene() {
StpUtil.checkActivityTimeout();
StpUtil.updateLastActivityToNow();
return AjaxJson.getSuccess("续签成功");
}
// 测试踢人下线 浏览器访问 http://localhost:8081/test/kickOut
@Mapping("kickOut")
public AjaxJson kickOut() {
// 先登录上
StpUtil.setLoginId(10001);
// 踢下线
StpUtil.logoutByLoginId(10001);
// 再尝试获取
StpUtil.getLoginId();
// 返回
return AjaxJson.getSuccess();
}
// 测试登录接口, 按照设备登录 浏览器访问 http://localhost:8081/test/login2
@Mapping("login2")
public AjaxJson login2(@Param(defaultValue="10001") String id, @Param(defaultValue="PC") String device) {
StpUtil.setLoginId(id, device);
return AjaxJson.getSuccess();
}
// 测试身份临时切换 http://localhost:8081/test/switchTo
@Mapping("switchTo")
public AjaxJson switchTo() {
System.out.println("当前会话身份:" + StpUtil.getLoginIdDefaultNull());
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
StpUtil.switchTo(10044, () -> {
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
System.out.println("当前会话身份已被切换为:" + StpUtil.getLoginId());
});
System.out.println("是否正在身份临时切换中: " + StpUtil.isSwitch());
return AjaxJson.getSuccess();
}
// 测试会话治理 浏览器访问 http://localhost:8081/test/search
@Mapping("search")
public AjaxJson search() {
System.out.println("--------------");
Ttime t = new Ttime().start();
List<String> tokenValue = StpUtil.searchTokenValue("8feb8265f773", 0, 10);
for (String v : tokenValue) {
// SaSession session = StpUtil.getSessionBySessionId(sid);
System.out.println(v);
}
System.out.println("用时:" + t.end().toString());
return AjaxJson.getSuccess();
}
// 测试指定设备登录 浏览器访问 http://localhost:8081/test/loginByDevice
@Mapping("loginByDevice")
public AjaxJson loginByDevice() {
System.out.println("--------------");
StpUtil.setLoginId(10001, "PC");
return AjaxJson.getSuccessData("登录成功");
}
// 测试 浏览器访问 http://localhost:8081/test/test
@Mapping("test")
public AjaxJson test() {
System.out.println("进来了");
return AjaxJson.getSuccess("访问成功");
}
// 测试 浏览器访问 http://localhost:8081/test/test2
@Mapping("test2")
public AjaxJson test2() {
return AjaxJson.getSuccess();
}
}

View File

@ -0,0 +1,33 @@
package com.pj.test;
import cn.dev33.satoken.stp.StpUtil;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Mapping;
/**
* 登录测试
* @author kong
*
*/
@Controller
@Mapping("/user/")
public class UserController {
// 测试登录浏览器访问 http://localhost:8081/user/doLogin?username=zhang&password=123456
@Mapping("doLogin")
public String doLogin(String username, String password) {
// 此处仅作模拟示例真实项目需要从数据库中查询数据进行比对
if("zhang".equals(username) && "123456".equals(password)) {
StpUtil.setLoginId(10001);
return "登录成功";
}
return "登录失败";
}
// 查询登录状态浏览器访问 http://localhost:8081/user/isLogin
@Mapping("isLogin")
public String isLogin(String username, String password) {
return "当前会话是否登录:" + StpUtil.isLogin();
}
}

View File

@ -0,0 +1,162 @@
package com.pj.util;
import java.io.Serializable;
import java.util.List;
/**
* ajax请求返回Json格式数据的封装
*/
public class AjaxJson implements Serializable{
private static final long serialVersionUID = 1L; // 序列化版本号
public static final int CODE_SUCCESS = 200; // 成功状态码
public static final int CODE_ERROR = 500; // 错误状态码
public static final int CODE_WARNING = 501; // 警告状态码
public static final int CODE_NOT_JUR = 403; // 无权限状态码
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
public int code; // 状态码
public String msg; // 描述信息
public Object data; // 携带对象
public Long dataCount; // 数据总数用于分页
/**
* 返回code
* @return
*/
public int getCode() {
return this.code;
}
/**
* 给msg赋值连缀风格
*/
public AjaxJson setMsg(String msg) {
this.msg = msg;
return this;
}
public String getMsg() {
return this.msg;
}
/**
* 给data赋值连缀风格
*/
public AjaxJson setData(Object data) {
this.data = data;
return this;
}
/**
* 将data还原为指定类型并返回
*/
@SuppressWarnings("unchecked")
public <T> T getData(Class<T> cs) {
return (T) data;
}
// ============================ 构建 ==================================
public AjaxJson(int code, String msg, Object data, Long dataCount) {
this.code = code;
this.msg = msg;
this.data = data;
this.dataCount = dataCount;
}
// 返回成功
public static AjaxJson getSuccess() {
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
}
public static AjaxJson getSuccess(String msg) {
return new AjaxJson(CODE_SUCCESS, msg, null, null);
}
public static AjaxJson getSuccess(String msg, Object data) {
return new AjaxJson(CODE_SUCCESS, msg, data, null);
}
public static AjaxJson getSuccessData(Object data) {
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
}
public static AjaxJson getSuccessArray(Object... data) {
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
}
// 返回失败
public static AjaxJson getError() {
return new AjaxJson(CODE_ERROR, "error", null, null);
}
public static AjaxJson getError(String msg) {
return new AjaxJson(CODE_ERROR, msg, null, null);
}
// 返回警告
public static AjaxJson getWarning() {
return new AjaxJson(CODE_ERROR, "warning", null, null);
}
public static AjaxJson getWarning(String msg) {
return new AjaxJson(CODE_WARNING, msg, null, null);
}
// 返回未登录
public static AjaxJson getNotLogin() {
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
}
// 返回没有权限的
public static AjaxJson getNotJur(String msg) {
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
}
// 返回一个自定义状态码的
public static AjaxJson get(int code, String msg){
return new AjaxJson(code, msg, null, null);
}
// 返回分页和数据的
public static AjaxJson getPageData(Long dataCount, Object data){
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
}
// 返回根据受影响行数的(大于0=ok小于0=error)
public static AjaxJson getByLine(int line){
if(line > 0){
return getSuccess("ok", line);
}
return getError("error").setData(line);
}
// 返回根据布尔值来确定最终结果的 (true=okfalse=error)
public static AjaxJson getByBoolean(boolean b){
return b ? getSuccess("ok") : getError("error");
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@SuppressWarnings("rawtypes")
@Override
public String toString() {
String data_string = null;
if(data == null){
} else if(data instanceof List){
data_string = "List(length=" + ((List)data).size() + ")";
} else {
data_string = data.toString();
}
return "{"
+ "\"code\": " + this.getCode()
+ ", \"msg\": \"" + this.getMsg() + "\""
+ ", \"data\": " + data_string
+ ", \"dataCount\": " + dataCount
+ "}";
}
}

View File

@ -0,0 +1,63 @@
package com.pj.util;
/**
* 用于测试用时
* @author kong
*
*/
public class Ttime {
private long start=0; //开始时间
private long end=0; //结束时间
public static Ttime t = new Ttime(); //static快捷使用
/**
* 开始计时
* @return
*/
public Ttime start() {
start=System.currentTimeMillis();
return this;
}
/**
* 结束计时
*/
public Ttime end() {
end=System.currentTimeMillis();
return this;
}
/**
* 返回所用毫秒数
*/
public long returnMs() {
return end-start;
}
/**
* 格式化输出结果
*/
public void outTime() {
System.out.println(this.toString());
}
/**
* 结束并格式化输出结果
*/
public void endOutTime() {
this.end().outTime();
}
@Override
public String toString() {
return (returnMs() + 0.0) / 1000 + "s"; // 格式化为0.01s
}
}

View File

@ -0,0 +1,49 @@
# 端口
server:
port: 8081
solon:
# sa-token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: satoken
# token有效期单位s 默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
allow-concurrent-login: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: true
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
# redis配置
redis:
# Redis数据库索引默认为0
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
# 连接超时时间(毫秒)
timeout: 10000ms
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0

View File

@ -20,6 +20,7 @@
<module>sa-token-servlet</module>
<module>sa-token-spring-boot-starter</module>
<module>sa-token-reactor-spring-boot-starter</module>
<module>sa-token-solon-plugin</module>
</modules>
</project>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-starter</artifactId>
<version>1.19.0</version>
</parent>
<packaging>jar</packaging>
<name>sa-token-solon-plugin</name>
<artifactId>sa-token-solon-plugin</artifactId>
<description>solon integrate sa-token</description>
<dependencies>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon</artifactId>
<version>1.4.8</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>1.19.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,60 @@
package cn.dev33.satoken.solon;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.action.SaTokenAction;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.solon.integration.SaTokenMethodInterceptor;
import cn.dev33.satoken.solon.integration.SaContextForSolon;
import cn.dev33.satoken.stp.StpInterface;
import org.noear.solon.Solon;
import org.noear.solon.SolonApp;
import org.noear.solon.core.Aop;
import org.noear.solon.core.Plugin;
/**
* @author noear
* @since 1.4
*/
public class XPluginImp implements Plugin {
@Override
public void start(SolonApp app) {
Aop.context().beanAroundAdd(SaCheckPermission.class, SaTokenMethodInterceptor.instance);
Aop.context().beanAroundAdd(SaCheckRole.class, SaTokenMethodInterceptor.instance);
Aop.context().beanAroundAdd(SaCheckLogin.class, SaTokenMethodInterceptor.instance);
//集成初始化
//注入配置Bean
SaTokenConfig saTokenConfig = Solon.cfg().getBean("solon.sa-token", SaTokenConfig.class);
SaManager.setConfig(saTokenConfig);
//注入容器交互Bean
SaManager.setSaTokenContext(new SaContextForSolon());
//注入侦听器Bean
Aop.getAsyn(SaTokenListener.class, bw->{
SaManager.setSaTokenListener(bw.raw());
});
//注入框架行为Bean
Aop.getAsyn(SaTokenAction.class, bw->{
SaManager.setSaTokenAction(bw.raw());
});
//注入权限认证Bean
Aop.getAsyn(StpInterface.class, bw->{
SaManager.setStpInterface(bw.raw());
});
//注入持久化Bean
Aop.getAsyn(SaTokenDao.class, bw->{
SaManager.setSaTokenDao(bw.raw());
});
}
}

View File

@ -0,0 +1,48 @@
package cn.dev33.satoken.solon.integration;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.solon.model.SaRequestForSolon;
import cn.dev33.satoken.solon.model.SaResponseForSolon;
import cn.dev33.satoken.solon.model.SaStorageForSolon;
import org.noear.solon.core.util.PathAnalyzer;
/**
* @author noear
* @since 1.4
*/
public class SaContextForSolon implements SaTokenContext {
/**
* 获取当前请求的Request对象
*/
@Override
public SaRequest getRequest() {
return new SaRequestForSolon();
}
/**
* 获取当前请求的Response对象
*/
@Override
public SaResponse getResponse() {
return new SaResponseForSolon();
}
/**
* 获取当前请求的 [存储器] 对象
*/
@Override
public SaStorage getStorage() {
return new SaStorageForSolon();
}
/**
* 校验指定路由匹配符是否可以匹配成功指定路径
*/
@Override
public boolean matchPath(String pattern, String path) {
return PathAnalyzer.get(path).matches(path);
}
}

View File

@ -0,0 +1,22 @@
package cn.dev33.satoken.solon.integration;
import cn.dev33.satoken.SaManager;
import org.noear.solon.core.aspect.Interceptor;
import org.noear.solon.core.aspect.Invocation;
/**
* @author noear
* @since 1.4
*/
public class SaTokenMethodInterceptor implements Interceptor {
public static final SaTokenMethodInterceptor instance = new SaTokenMethodInterceptor();
@Override
public Object doIntercept(Invocation inv) throws Throwable {
// 注解鉴权
SaManager.getSaTokenAction().checkMethodAnnotation(inv.method().getMethod());
// 执行原有逻辑
return inv.invoke();
}
}

View File

@ -0,0 +1,162 @@
package cn.dev33.satoken.solon.integration;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.filter.SaFilterAuthStrategy;
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
import cn.dev33.satoken.router.SaRouterUtil;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Filter;
import org.noear.solon.core.handle.FilterChain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author noear 2021/5/30 created
*/
public class SaTokenPathFilter implements Filter {
// ------------------------ 设置此过滤器 拦截 & 放行 的路由
/**
* 拦截路由
*/
private List<String> includeList = new ArrayList<>();
/**
* 放行路由
*/
private List<String> excludeList = new ArrayList<>();
/**
* 添加 [拦截路由]
* @param paths 路由
* @return 对象自身
*/
public SaTokenPathFilter addInclude(String... paths) {
includeList.addAll(Arrays.asList(paths));
return this;
}
/**
* 添加 [放行路由]
* @param paths 路由
* @return 对象自身
*/
public SaTokenPathFilter addExclude(String... paths) {
excludeList.addAll(Arrays.asList(paths));
return this;
}
/**
* 写入 [拦截路由] 集合
* @param pathList 路由集合
* @return 对象自身
*/
public SaTokenPathFilter setIncludeList(List<String> pathList) {
includeList = pathList;
return this;
}
/**
* 写入 [放行路由] 集合
* @param pathList 路由集合
* @return 对象自身
*/
public SaTokenPathFilter setExcludeList(List<String> pathList) {
excludeList = pathList;
return this;
}
/**
* 获取 [拦截路由] 集合
* @return see note
*/
public List<String> getIncludeList() {
return includeList;
}
/**
* 获取 [放行路由] 集合
* @return see note
*/
public List<String> getExcludeList() {
return excludeList;
}
// ------------------------ 钩子函数
/**
* 认证函数每次请求执行
*/
public SaFilterAuthStrategy auth = r -> {};
/**
* 异常处理函数每次[认证函数]发生异常时执行此函数
*/
public SaFilterErrorStrategy error = e -> {
throw new SaTokenException(e);
};
/**
* 前置函数在每次[认证函数]之前执行
*/
public SaFilterAuthStrategy beforeAuth = r -> {};
/**
* 写入[认证函数]: 每次请求执行
* @param auth see note
* @return 对象自身
*/
public SaTokenPathFilter setAuth(SaFilterAuthStrategy auth) {
this.auth = auth;
return this;
}
/**
* 写入[异常处理函数]每次[认证函数]发生异常时执行此函数
* @param error see note
* @return 对象自身
*/
public SaTokenPathFilter setError(SaFilterErrorStrategy error) {
this.error = error;
return this;
}
/**
* 写入[前置函数]在每次[认证函数]之前执行
* @param beforeAuth see note
* @return 对象自身
*/
public SaTokenPathFilter setBeforeAuth(SaFilterAuthStrategy beforeAuth) {
this.beforeAuth = beforeAuth;
return this;
}
@Override
public void doFilter(Context ctx, FilterChain chain) throws Throwable {
try {
// 执行全局过滤器
SaRouterUtil.match(includeList, excludeList, () -> {
beforeAuth.run(null);
auth.run(null);
});
} catch (Throwable e) {
// 1. 获取异常处理策略结果
Object result = error.run(e);
String resultString = String.valueOf(result);
// 2. 写入输出流
ctx.contentType("text/plain; charset=utf-8");
ctx.output(resultString);
return;
}
// 执行
chain.doFilter(ctx);
}
}

View File

@ -0,0 +1,45 @@
package cn.dev33.satoken.solon.model;
import cn.dev33.satoken.context.model.SaRequest;
import org.noear.solon.core.handle.Context;
/**
* @author noear
* @since 1.4
*/
public class SaRequestForSolon implements SaRequest {
Context ctx;
public SaRequestForSolon(){
ctx = Context.current();
}
@Override
public Object getSource() {
return ctx;
}
@Override
public String getParameter(String s) {
return ctx.param(s);
}
@Override
public String getHeader(String s) {
return ctx.header(s);
}
@Override
public String getCookieValue(String s) {
return ctx.cookie(s);
}
@Override
public String getRequestPath() {
return ctx.pathNew();
}
@Override
public String getMethod() {
return ctx.method();
}
}

View File

@ -0,0 +1,42 @@
package cn.dev33.satoken.solon.model;
import cn.dev33.satoken.context.model.SaResponse;
import org.noear.solon.Utils;
import org.noear.solon.core.handle.Context;
/**
* @author noear
* @since 1.4
*/
public class SaResponseForSolon implements SaResponse {
Context ctx;
public SaResponseForSolon() {
ctx = Context.current();
}
@Override
public Object getSource() {
return ctx;
}
@Override
public void deleteCookie(String s) {
ctx.cookieRemove(s);
}
@Override
public void addCookie(String name, String value, String path, String domain, int timeout) {
if (Utils.isNotEmpty(path)) {
path = "/";
}
ctx.cookieSet(name, value, domain, path, timeout);
}
@Override
public SaResponse setHeader(String name, String value) {
ctx.headerSet(name, value);
return this;
}
}

View File

@ -0,0 +1,36 @@
package cn.dev33.satoken.solon.model;
import cn.dev33.satoken.context.model.SaStorage;
import org.noear.solon.core.handle.Context;
/**
* @author noear
* @since 1.4
*/
public class SaStorageForSolon implements SaStorage {
Context ctx;
public SaStorageForSolon() {
ctx = Context.current();
}
@Override
public Object getSource() {
return ctx;
}
@Override
public void set(String key, Object value) {
ctx.attrSet(key, value);
}
@Override
public Object get(String key) {
return ctx.attr(key);
}
@Override
public void delete(String key) {
ctx.attrMap().remove(key);
}
}

View File

@ -0,0 +1,4 @@
/**
* sa-token集成SpringBoot的各个组件
*/
package cn.dev33.satoken.solon;

View File

@ -0,0 +1 @@
solon.plugin=cn.dev33.satoken.solon.XPluginImp