From 369d3ffb5bd4508f8ffbcf7df32b8fb0f69b7f3b Mon Sep 17 00:00:00 2001
From: click33 <2393584716@qq.com>
Date: Wed, 9 Feb 2022 23:24:08 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=8D=95=E5=85=83=E6=B5=8B?=
=?UTF-8?q?=E8=AF=95.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sa-token-doc/doc/start/download.md | 5 +
sa-token-test/pom.xml | 1 +
sa-token-test/sa-token-jwt-test/.gitignore | 12 +
sa-token-test/sa-token-jwt-test/pom.xml | 31 +
.../test/java/com/pj/test/JwtForMixTest.java | 0
.../java/com/pj/test/JwtForStatelessTest.java | 0
.../java/com/pj/test/JwtForStyleTest.java | 0
.../java/com/pj/test/StartUpApplication.java | 0
.../com/pj/test/satoken/StpInterfaceImpl.java | 0
.../src/test/resources/application.yml | 46 ++
.../.gitignore | 12 +
.../pom.xml | 28 +
.../java/com/pj/test/LoginController.java | 49 ++
.../java/com/pj/test/LoginControllerTest.java | 112 +++
.../java/com/pj/test/StartUpApplication.java | 16 +
.../src/test/java/com/pj/test/util/SoMap.java | 747 ++++++++++++++++++
.../sa-token-springboot-test/pom.xml | 2 -
.../src/test/java/com/pj/test/BasicsTest.java | 1 +
18 files changed, 1060 insertions(+), 2 deletions(-)
create mode 100644 sa-token-test/sa-token-jwt-test/.gitignore
create mode 100644 sa-token-test/sa-token-jwt-test/pom.xml
rename {sa-token-demo/sa-token-demo-jwt => sa-token-test/sa-token-jwt-test}/src/test/java/com/pj/test/JwtForMixTest.java (100%)
rename {sa-token-demo/sa-token-demo-jwt => sa-token-test/sa-token-jwt-test}/src/test/java/com/pj/test/JwtForStatelessTest.java (100%)
rename {sa-token-demo/sa-token-demo-jwt => sa-token-test/sa-token-jwt-test}/src/test/java/com/pj/test/JwtForStyleTest.java (100%)
rename {sa-token-demo/sa-token-demo-jwt => sa-token-test/sa-token-jwt-test}/src/test/java/com/pj/test/StartUpApplication.java (100%)
rename {sa-token-demo/sa-token-demo-jwt => sa-token-test/sa-token-jwt-test}/src/test/java/com/pj/test/satoken/StpInterfaceImpl.java (100%)
create mode 100644 sa-token-test/sa-token-jwt-test/src/test/resources/application.yml
create mode 100644 sa-token-test/sa-token-springboot-integrate-test/.gitignore
create mode 100644 sa-token-test/sa-token-springboot-integrate-test/pom.xml
create mode 100644 sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/LoginController.java
create mode 100644 sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/LoginControllerTest.java
create mode 100644 sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/StartUpApplication.java
create mode 100644 sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/util/SoMap.java
diff --git a/sa-token-doc/doc/start/download.md b/sa-token-doc/doc/start/download.md
index 9b9cf331..a6921673 100644
--- a/sa-token-doc/doc/start/download.md
+++ b/sa-token-doc/doc/start/download.md
@@ -120,6 +120,11 @@ implementation 'cn.dev33:sa-token-core:${sa.top.version}'
├── sa-token-demo-sso-client-h5 // [示例] Sa-Token 集成 SSO单点登录-client应用端 (前后端分离)
├── sa-token-demo-oauth2-server // [示例] Sa-Token 集成 OAuth2.0 (服务端)
├── sa-token-demo-oauth2-client // [示例] Sa-Token 集成 OAuth2.0 (客户端)
+ ├── sa-token-test // [测试] Sa-Token 单元测试合集
+ ├── sa-token-core-test // [测试] Sa-Token Core核心包单元测试
+ ├── sa-token-springboot-test // [测试] Sa-Token SpringBoot 整合测试
+ ├── sa-token-springboot-integrate-test // [测试] Sa-Token SpringBoot 整合客户端测试
+ ├── sa-token-jwt-test // [测试] Sa-Token jwt 整合测试
├── sa-token-doc // [文档] Sa-Token 开发文档
├──pom.xml // [依赖] 顶级pom文件
```
diff --git a/sa-token-test/pom.xml b/sa-token-test/pom.xml
index e7ab8b31..a6b1860e 100644
--- a/sa-token-test/pom.xml
+++ b/sa-token-test/pom.xml
@@ -21,6 +21,7 @@
sa-token-core-test
sa-token-springboot-test
+ sa-token-springboot-integrate-test
diff --git a/sa-token-test/sa-token-jwt-test/.gitignore b/sa-token-test/sa-token-jwt-test/.gitignore
new file mode 100644
index 00000000..f56feec7
--- /dev/null
+++ b/sa-token-test/sa-token-jwt-test/.gitignore
@@ -0,0 +1,12 @@
+target/
+
+node_modules/
+bin/
+.settings/
+unpackage/
+.classpath
+.project
+
+.factorypath
+
+.idea/
\ No newline at end of file
diff --git a/sa-token-test/sa-token-jwt-test/pom.xml b/sa-token-test/sa-token-jwt-test/pom.xml
new file mode 100644
index 00000000..076d8544
--- /dev/null
+++ b/sa-token-test/sa-token-jwt-test/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+
+ cn.dev33
+ sa-token-test
+ 1.28.0
+
+ jar
+
+ sa-token-jwt-test
+ sa-token-jwt-test
+ sa-token-jwt-test
+
+
+
+ cn.dev33
+ sa-token-spring-boot-starter
+ ${sa-token-version}
+
+
+ cn.dev33
+ sa-token-jwt
+ ${sa-token-version}
+
+
+
+
diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForMixTest.java b/sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/JwtForMixTest.java
similarity index 100%
rename from sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForMixTest.java
rename to sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/JwtForMixTest.java
diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForStatelessTest.java b/sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/JwtForStatelessTest.java
similarity index 100%
rename from sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForStatelessTest.java
rename to sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/JwtForStatelessTest.java
diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForStyleTest.java b/sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/JwtForStyleTest.java
similarity index 100%
rename from sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForStyleTest.java
rename to sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/JwtForStyleTest.java
diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/StartUpApplication.java b/sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/StartUpApplication.java
similarity index 100%
rename from sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/StartUpApplication.java
rename to sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/StartUpApplication.java
diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/satoken/StpInterfaceImpl.java b/sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/satoken/StpInterfaceImpl.java
similarity index 100%
rename from sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/satoken/StpInterfaceImpl.java
rename to sa-token-test/sa-token-jwt-test/src/test/java/com/pj/test/satoken/StpInterfaceImpl.java
diff --git a/sa-token-test/sa-token-jwt-test/src/test/resources/application.yml b/sa-token-test/sa-token-jwt-test/src/test/resources/application.yml
new file mode 100644
index 00000000..0f148e62
--- /dev/null
+++ b/sa-token-test/sa-token-jwt-test/src/test/resources/application.yml
@@ -0,0 +1,46 @@
+# 端口
+server:
+ port: 8081
+
+# sa-token配置
+sa-token:
+ # token名称 (同时也是cookie名称)
+ token-name: satoken
+ # token有效期,单位s 默认30天, -1代表永不过期
+ timeout: 2592000
+ # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
+ activity-timeout: -1
+ # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
+ is-concurrent: true
+ # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
+ is-share: true
+ # token风格
+ token-style: uuid
+ # jwt秘钥
+ jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk
+
+spring:
+ # 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
+
+
\ No newline at end of file
diff --git a/sa-token-test/sa-token-springboot-integrate-test/.gitignore b/sa-token-test/sa-token-springboot-integrate-test/.gitignore
new file mode 100644
index 00000000..f56feec7
--- /dev/null
+++ b/sa-token-test/sa-token-springboot-integrate-test/.gitignore
@@ -0,0 +1,12 @@
+target/
+
+node_modules/
+bin/
+.settings/
+unpackage/
+.classpath
+.project
+
+.factorypath
+
+.idea/
\ No newline at end of file
diff --git a/sa-token-test/sa-token-springboot-integrate-test/pom.xml b/sa-token-test/sa-token-springboot-integrate-test/pom.xml
new file mode 100644
index 00000000..507522c8
--- /dev/null
+++ b/sa-token-test/sa-token-springboot-integrate-test/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+
+
+ cn.dev33
+ sa-token-test
+ 1.28.0
+
+ jar
+
+ sa-token-springboot-integrate-test
+ sa-token-springboot-integrate-test
+ sa-token-springboot-integrate-test
+
+
+
+ cn.dev33
+ sa-token-spring-boot-starter
+ ${sa-token-version}
+
+
+
+
+
+
diff --git a/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/LoginController.java b/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/LoginController.java
new file mode 100644
index 00000000..2d9d6bb8
--- /dev/null
+++ b/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/LoginController.java
@@ -0,0 +1,49 @@
+package com.pj.test;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+
+/**
+ * 登录测试
+ *
+ * @author kong
+ *
+ */
+@RestController
+@RequestMapping("/acc/")
+public class LoginController {
+
+ // 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
+ @RequestMapping("doLogin")
+ public SaResult doLogin(String name, String pwd) {
+ // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
+ if("zhang".equals(name) && "123456".equals(pwd)) {
+ StpUtil.login(10001);
+ return SaResult.ok("登录成功").set("token", StpUtil.getTokenValue());
+ }
+ return SaResult.error("登录失败");
+ }
+
+ // 查询登录状态 ---- http://localhost:8081/acc/isLogin
+ @RequestMapping("isLogin")
+ public SaResult isLogin() {
+ return SaResult.data(StpUtil.isLogin());
+ }
+
+ // 查询 Token 信息 ---- http://localhost:8081/acc/tokenInfo
+ @RequestMapping("tokenInfo")
+ public SaResult tokenInfo() {
+ return SaResult.data(StpUtil.getTokenInfo());
+ }
+
+ // 测试注销 ---- http://localhost:8081/acc/logout
+ @RequestMapping("logout")
+ public SaResult logout() {
+ StpUtil.logout();
+ return SaResult.ok();
+ }
+
+}
diff --git a/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/LoginControllerTest.java b/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/LoginControllerTest.java
new file mode 100644
index 00000000..a694c3d8
--- /dev/null
+++ b/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/LoginControllerTest.java
@@ -0,0 +1,112 @@
+package com.pj.test;
+
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.pj.test.util.SoMap;
+
+/**
+ * Sa-Token 登录API测试
+ *
+ * @author Auster
+ *
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = StartUpApplication.class)
+public class LoginControllerTest {
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ private MockMvc mvc;
+
+ // 开始
+ @Before
+ public void before() {
+ mvc = MockMvcBuilders.webAppContextSetup(wac).build();
+ }
+
+ @Test
+ public void testLogin() throws Exception{
+ // 请求
+ MvcResult mvcResult = mvc.perform(
+ MockMvcRequestBuilders.post("/acc/doLogin")
+ .param("name", "zhang")
+ .param("pwd", "123456")
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andReturn();
+
+ // 拿到结果
+ SoMap so = SoMap.getSoMap().setJsonString(
+ mvcResult.getResponse().getContentAsString()
+ );
+ String token = so.getString("token");
+
+ // 断言
+ Assert.assertTrue(mvcResult.getResponse().getHeader("Set-Cookie") != null);
+ Assert.assertEquals(so.getInt("code"), 200);
+ Assert.assertNotNull(token);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testLogin2() throws Exception{
+ // 获取token
+ SoMap so = request("/acc/doLogin?name=zhang&pwd=123456");
+ Assert.assertNotNull(so.getString("token"));
+
+ String token = so.getString("token");
+
+ // 是否登录
+ SoMap so2 = request("/acc/isLogin?satoken=" + token);
+ Assert.assertTrue(so2.getBoolean("data"));
+
+ // tokenInfo
+ SoMap so3 = request("/acc/tokenInfo?satoken=" + token);
+ SoMap so4 = SoMap.getSoMap((Map)so3.get("data"));
+ Assert.assertEquals(so4.getString("tokenName"), "satoken");
+ Assert.assertEquals(so4.getString("tokenValue"), token);
+
+ // 注销
+ request("/acc/logout?satoken=" + token);
+
+ // 是否登录
+ SoMap so5 = request("/acc/isLogin?satoken=" + token);
+ Assert.assertFalse(so5.getBoolean("data"));
+ }
+
+ // 封装请求
+ private SoMap request(String path) throws Exception {
+ MvcResult mvcResult = mvc.perform(
+ MockMvcRequestBuilders.post(path)
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .accept(MediaType.APPLICATION_JSON_UTF8)
+ )
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andReturn();
+
+ SoMap so = SoMap.getSoMap().setJsonString(
+ mvcResult.getResponse().getContentAsString()
+ );
+
+ return so;
+ }
+
+}
diff --git a/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/StartUpApplication.java b/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/StartUpApplication.java
new file mode 100644
index 00000000..2a174efd
--- /dev/null
+++ b/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/StartUpApplication.java
@@ -0,0 +1,16 @@
+package com.pj.test;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 启动类
+ * @author Auster
+ *
+ */
+@SpringBootApplication
+public class StartUpApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(StartUpApplication.class, args);
+ }
+}
diff --git a/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/util/SoMap.java b/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/util/SoMap.java
new file mode 100644
index 00000000..2ff88103
--- /dev/null
+++ b/sa-token-test/sa-token-springboot-integrate-test/src/test/java/com/pj/test/util/SoMap.java
@@ -0,0 +1,747 @@
+package com.pj.test.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦
+ * 所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用
+ *
最新:2020-12-10 新增部分构造方法
+ * @author kong
+ */
+public class SoMap extends LinkedHashMap {
+
+ private static final long serialVersionUID = 1L;
+
+ public SoMap() {
+ }
+
+
+ /** 以下元素会在isNull函数中被判定为Null, */
+ public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
+ public static final List NULL_ELEMENT_LIST;
+
+
+ static {
+ NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
+ }
+
+ // ============================= 读值 =============================
+
+ /** 获取一个值 */
+ @Override
+ public Object get(Object key) {
+ if("this".equals(key)) {
+ return this;
+ }
+ return super.get(key);
+ }
+
+ /** 如果为空,则返回默认值 */
+ public Object get(Object key, Object defaultValue) {
+ Object value = get(key);
+ if(valueIsNull(value)) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ /** 转为String并返回 */
+ public String getString(String key) {
+ Object value = get(key);
+ if(value == null) {
+ return null;
+ }
+ return String.valueOf(value);
+ }
+
+ /** 如果为空,则返回默认值 */
+ public String getString(String key, String defaultValue) {
+ Object value = get(key);
+ if(valueIsNull(value)) {
+ return defaultValue;
+ }
+ return String.valueOf(value);
+ }
+
+ /** 转为int并返回 */
+ public int getInt(String key) {
+ Object value = get(key);
+ if(valueIsNull(value)) {
+ return 0;
+ }
+ return Integer.valueOf(String.valueOf(value));
+ }
+ /** 转为int并返回,同时指定默认值 */
+ public int getInt(String key, int defaultValue) {
+ Object value = get(key);
+ if(valueIsNull(value)) {
+ return defaultValue;
+ }
+ return Integer.valueOf(String.valueOf(value));
+ }
+
+ /** 转为long并返回 */
+ public long getLong(String key) {
+ Object value = get(key);
+ if(valueIsNull(value)) {
+ return 0;
+ }
+ return Long.valueOf(String.valueOf(value));
+ }
+
+ /** 转为double并返回 */
+ public double getDouble(String key) {
+ Object value = get(key);
+ if(valueIsNull(value)) {
+ return 0.0;
+ }
+ return Double.valueOf(String.valueOf(value));
+ }
+
+ /** 转为boolean并返回 */
+ public boolean getBoolean(String key) {
+ Object value = get(key);
+ if(valueIsNull(value)) {
+ return false;
+ }
+ return Boolean.valueOf(String.valueOf(value));
+ }
+
+ /** 转为Date并返回,根据自定义格式 */
+ public Date getDateByFormat(String key, String format) {
+ try {
+ return new SimpleDateFormat(format).parse(getString(key));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** 转为Date并返回,根据格式: yyyy-MM-dd */
+ public Date getDate(String key) {
+ return getDateByFormat(key, "yyyy-MM-dd");
+ }
+
+ /** 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss */
+ public Date getDateTime(String key) {
+ return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
+ }
+
+ /** 获取集合(必须原先就是个集合,否则会创建个新集合并返回) */
+ @SuppressWarnings("unchecked")
+ public List getList(String key) {
+ Object value = get(key);
+ List list = null;
+ if(value == null || value.equals("")) {
+ list = new ArrayList();
+ }
+ else if(value instanceof List) {
+ list = (List)value;
+ } else {
+ list = new ArrayList();
+ list.add(value);
+ }
+ return list;
+ }
+
+ /** 获取集合 (指定泛型类型) */
+ public List getList(String key, Class cs) {
+ List list = getList(key);
+ List list2 = new ArrayList();
+ for (Object obj : list) {
+ T objC = getValueByClass(obj, cs);
+ list2.add(objC);
+ }
+ return list2;
+ }
+
+ /** 获取集合(逗号分隔式),(指定类型) */
+ public List getListByComma(String key, Class cs) {
+ String listStr = getString(key);
+ if(listStr == null || listStr.equals("")) {
+ return new ArrayList<>();
+ }
+ // 开始转化
+ String [] arr = listStr.split(",");
+ List list = new ArrayList();
+ for (String str : arr) {
+ if(cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
+ str = str.trim();
+ }
+ T objC = getValueByClass(str, cs);
+ list.add(objC);
+ }
+ return list;
+ }
+
+
+ /** 根据指定类型从map中取值,返回实体对象 */
+ public T getModel(Class cs) {
+ try {
+ return getModelByObject(cs.newInstance());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** 从map中取值,塞到一个对象中 */
+ public T getModelByObject(T obj) {
+ // 获取类型
+ Class> cs = obj.getClass();
+ // 循环复制
+ for (Field field : cs.getDeclaredFields()) {
+ try {
+ // 获取对象
+ Object value = this.get(field.getName());
+ if(value == null) {
+ continue;
+ }
+ field.setAccessible(true);
+ Object valueConvert = getValueByClass(value, field.getType());
+ field.set(obj, valueConvert);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new RuntimeException("属性取值出错:" + field.getName(), e);
+ }
+ }
+ return obj;
+ }
+
+
+
+ /**
+ * 将指定值转化为指定类型并返回
+ * @param obj
+ * @param cs
+ * @param
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public static T getValueByClass(Object obj, Class cs) {
+ String obj2 = String.valueOf(obj);
+ Object obj3 = null;
+ if (cs.equals(String.class)) {
+ obj3 = obj2;
+ } else if (cs.equals(int.class) || cs.equals(Integer.class)) {
+ obj3 = Integer.valueOf(obj2);
+ } else if (cs.equals(long.class) || cs.equals(Long.class)) {
+ obj3 = Long.valueOf(obj2);
+ } else if (cs.equals(short.class) || cs.equals(Short.class)) {
+ obj3 = Short.valueOf(obj2);
+ } else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
+ obj3 = Byte.valueOf(obj2);
+ } else if (cs.equals(float.class) || cs.equals(Float.class)) {
+ obj3 = Float.valueOf(obj2);
+ } else if (cs.equals(double.class) || cs.equals(Double.class)) {
+ obj3 = Double.valueOf(obj2);
+ } else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
+ obj3 = Boolean.valueOf(obj2);
+ } else {
+ obj3 = (T)obj;
+ }
+ return (T)obj3;
+ }
+
+
+ // ============================= 写值 =============================
+
+ /**
+ * 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去)
+ */
+ public void setDefaultValue(String key, Object defaultValue) {
+ if(isNull(key)) {
+ set(key, defaultValue);
+ }
+ }
+
+ /** set一个值,连缀风格 */
+ public SoMap set(String key, Object value) {
+ // 防止敏感key
+ if(key.toLowerCase().equals("this")) {
+ return this;
+ }
+ put(key, value);
+ return this;
+ }
+
+ /** 将一个Map塞进SoMap */
+ public SoMap setMap(Map map) {
+ if(map != null) {
+ for (String key : map.keySet()) {
+ this.set(key, map.get(key));
+ }
+ }
+ return this;
+ }
+
+ /** 将一个对象解析塞进SoMap */
+ public SoMap setModel(Object model) {
+ if(model == null) {
+ return this;
+ }
+ Field[] fields = model.getClass().getDeclaredFields();
+ for (Field field : fields) {
+ try{
+ field.setAccessible(true);
+ boolean isStatic = Modifier.isStatic(field.getModifiers());
+ if(!isStatic) {
+ this.set(field.getName(), field.get(model));
+ }
+ }catch (Exception e){
+ throw new RuntimeException(e);
+ }
+ }
+ return this;
+ }
+
+ /** 将json字符串解析后塞进SoMap */
+ public SoMap setJsonString(String jsonString) {
+ try {
+ @SuppressWarnings("unchecked")
+ Map map = new ObjectMapper().readValue(jsonString, Map.class);
+ return this.setMap(map);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ // ============================= 删值 =============================
+
+ /** delete一个值,连缀风格 */
+ public SoMap delete(String key) {
+ remove(key);
+ return this;
+ }
+
+ /** 清理所有value为null的字段 */
+ public SoMap clearNull() {
+ Iterator iterator = this.keySet().iterator();
+ while(iterator.hasNext()) {
+ String key = iterator.next();
+ if(this.isNull(key)) {
+ iterator.remove();
+ this.remove(key);
+ }
+
+ }
+ return this;
+ }
+ /** 清理指定key */
+ public SoMap clearIn(String ...keys) {
+ List keys2 = Arrays.asList(keys);
+ Iterator iterator = this.keySet().iterator();
+ while(iterator.hasNext()) {
+ String key = iterator.next();
+ if(keys2.contains(key) == true) {
+ iterator.remove();
+ this.remove(key);
+ }
+ }
+ return this;
+ }
+ /** 清理掉不在列表中的key */
+ public SoMap clearNotIn(String ...keys) {
+ List keys2 = Arrays.asList(keys);
+ Iterator iterator = this.keySet().iterator();
+ while(iterator.hasNext()) {
+ String key = iterator.next();
+ if(keys2.contains(key) == false) {
+ iterator.remove();
+ this.remove(key);
+ }
+
+ }
+ return this;
+ }
+ /** 清理掉所有key */
+ public SoMap clearAll() {
+ clear();
+ return this;
+ }
+
+
+ // ============================= 快速构建 =============================
+
+ /** 构建一个SoMap并返回 */
+ public static SoMap getSoMap() {
+ return new SoMap();
+ }
+ /** 构建一个SoMap并返回 */
+ public static SoMap getSoMap(String key, Object value) {
+ return new SoMap().set(key, value);
+ }
+ /** 构建一个SoMap并返回 */
+ public static SoMap getSoMap(Map map) {
+ return new SoMap().setMap(map);
+ }
+
+ /** 将一个对象集合解析成为SoMap */
+ public static SoMap getSoMapByModel(Object model) {
+ return SoMap.getSoMap().setModel(model);
+ }
+
+ /** 将一个对象集合解析成为SoMap集合 */
+ public static List getSoMapByList(List> list) {
+ List listMap = new ArrayList();
+ for (Object model : list) {
+ listMap.add(getSoMapByModel(model));
+ }
+ return listMap;
+ }
+
+ /** 克隆指定key,返回一个新的SoMap */
+ public SoMap cloneKeys(String... keys) {
+ SoMap so = new SoMap();
+ for (String key : keys) {
+ so.set(key, this.get(key));
+ }
+ return so;
+ }
+ /** 克隆所有key,返回一个新的SoMap */
+ public SoMap cloneSoMap() {
+ SoMap so = new SoMap();
+ for (String key : this.keySet()) {
+ so.set(key, this.get(key));
+ }
+ return so;
+ }
+
+ /** 将所有key转为大写 */
+ public SoMap toUpperCase() {
+ SoMap so = new SoMap();
+ for (String key : this.keySet()) {
+ so.set(key.toUpperCase(), this.get(key));
+ }
+ this.clearAll().setMap(so);
+ return this;
+ }
+ /** 将所有key转为小写 */
+ public SoMap toLowerCase() {
+ SoMap so = new SoMap();
+ for (String key : this.keySet()) {
+ so.set(key.toLowerCase(), this.get(key));
+ }
+ this.clearAll().setMap(so);
+ return this;
+ }
+ /** 将所有key中下划线转为中划线模式 (kebab-case风格) */
+ public SoMap toKebabCase() {
+ SoMap so = new SoMap();
+ for (String key : this.keySet()) {
+ so.set(wordEachKebabCase(key), this.get(key));
+ }
+ this.clearAll().setMap(so);
+ return this;
+ }
+ /** 将所有key中下划线转为小驼峰模式 */
+ public SoMap toHumpCase() {
+ SoMap so = new SoMap();
+ for (String key : this.keySet()) {
+ so.set(wordEachBigFs(key), this.get(key));
+ }
+ this.clearAll().setMap(so);
+ return this;
+ }
+ /** 将所有key中小驼峰转为下划线模式 */
+ public SoMap humpToLineCase() {
+ SoMap so = new SoMap();
+ for (String key : this.keySet()) {
+ so.set(wordHumpToLine(key), this.get(key));
+ }
+ this.clearAll().setMap(so);
+ return this;
+ }
+
+
+
+
+ // ============================= 辅助方法 =============================
+
+
+ /** 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素 */
+ public boolean isNull(String key) {
+ return valueIsNull(get(key));
+ }
+
+ /** 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true */
+ public boolean isContainNull(String ...keys) {
+ for (String key : keys) {
+ if(this.isNull(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** 与isNull()相反 */
+ public boolean isNotNull(String key) {
+ return !isNull(key);
+ }
+ /** 指定key的value是否为null,作用同isNotNull() */
+ public boolean has(String key) {
+ return !isNull(key);
+ }
+
+ /** 指定value在此SoMap的判断标准中是否为null */
+ public boolean valueIsNull(Object value) {
+ return NULL_ELEMENT_LIST.contains(value);
+ }
+
+ /** 验证指定key不为空,为空则抛出异常 */
+ public SoMap checkNull(String ...keys) {
+ for (String key : keys) {
+ if(this.isNull(key)) {
+ throw new RuntimeException("参数" + key + "不能为空");
+ }
+ }
+ return this;
+ }
+
+ static Pattern patternNumber = Pattern.compile("[0-9]*");
+ /** 指定key是否为数字 */
+ public boolean isNumber(String key) {
+ String value = getString(key);
+ if(value == null) {
+ return false;
+ }
+ return patternNumber.matcher(value).matches();
+ }
+
+
+
+
+ /**
+ * 转为JSON字符串
+ */
+ public String toJsonString() {
+ try {
+// SoMap so = SoMap.getSoMap(this);
+ return new ObjectMapper().writeValueAsString(this);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+//
+// /**
+// * 转为JSON字符串, 带格式的
+// */
+// public String toJsonFormatString() {
+// try {
+// return JSON.toJSONString(this, true);
+// } catch (Exception e) {
+// throw new RuntimeException(e);
+// }
+// }
+
+ // ============================= web辅助 =============================
+
+
+ /**
+ * 返回当前request请求的的所有参数
+ * @return
+ */
+ public static SoMap getRequestSoMap() {
+ // 大善人SpringMVC提供的封装
+ ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ if(servletRequestAttributes == null) {
+ throw new RuntimeException("当前线程非JavaWeb环境");
+ }
+ // 当前request
+ HttpServletRequest request = servletRequestAttributes.getRequest();
+ if (request.getAttribute("currentSoMap") == null || request.getAttribute("currentSoMap") instanceof SoMap == false ) {
+ initRequestSoMap(request);
+ }
+ return (SoMap)request.getAttribute("currentSoMap");
+ }
+
+ /** 初始化当前request的 SoMap */
+ private static void initRequestSoMap(HttpServletRequest request) {
+ SoMap soMap = new SoMap();
+ Map parameterMap = request.getParameterMap(); // 获取所有参数
+ for (String key : parameterMap.keySet()) {
+ try {
+ String[] values = parameterMap.get(key); // 获得values
+ if(values.length == 1) {
+ soMap.set(key, values[0]);
+ } else {
+ List list = new ArrayList();
+ for (String v : values) {
+ list.add(v);
+ }
+ soMap.set(key, list);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ request.setAttribute("currentSoMap", soMap);
+ }
+
+ /**
+ * 验证返回当前线程是否为JavaWeb环境
+ * @return
+ */
+ public static boolean isJavaWeb() {
+ ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
+ if(servletRequestAttributes == null) {
+ return false;
+ }
+ return true;
+ }
+
+
+
+ // ============================= 常见key (以下key经常用,所以封装以下,方便写代码) =============================
+
+ /** get 当前页 */
+ public int getKeyPageNo() {
+ int pageNo = getInt("pageNo", 1);
+ if(pageNo <= 0) {
+ pageNo = 1;
+ }
+ return pageNo;
+ }
+ /** get 页大小 */
+ public int getKeyPageSize() {
+ int pageSize = getInt("pageSize", 10);
+ if(pageSize <= 0 || pageSize > 1000) {
+ pageSize = 10;
+ }
+ return pageSize;
+ }
+
+ /** get 排序方式 */
+ public int getKeySortType() {
+ return getInt("sortType");
+ }
+
+
+
+
+
+ // ============================= 分页相关(封装mybatis的page-help插件 ) =============================
+
+// /** 分页插件 */
+// private com.github.pagehelper.Page> pagePlug;
+// /** 分页插件 - 开始分页 */
+// public SoMap startPage() {
+// this.pagePlug= com.github.pagehelper.PageHelper.startPage(getKeyPageNo(), getKeyPageSize());
+// return this;
+// }
+// /** 获取上次分页的记录总数 */
+// public long getDataCount() {
+// if(pagePlug == null) {
+// return -1;
+// }
+// return pagePlug.getTotal();
+// }
+// /** 分页插件 - 结束分页, 返回总条数 (该方法已过时,请调用更加符合语义化的getDataCount() ) */
+// @Deprecated
+// public long endPage() {
+// return getDataCount();
+// }
+
+
+
+
+
+ // ============================= 工具方法 =============================
+
+
+ /**
+ * 将一个一维集合转换为树形集合
+ * @param list 集合
+ * @param idKey id标识key
+ * @param parentIdKey 父id标识key
+ * @param childListKey 子节点标识key
+ * @return 转换后的tree集合
+ */
+ public static List listToTree(List list, String idKey, String parentIdKey, String childListKey) {
+ // 声明新的集合,存储tree形数据
+ List newTreeList = new ArrayList();
+ // 声明hash-Map,方便查找数据
+ SoMap hash = new SoMap();
+ // 将数组转为Object的形式,key为数组中的id
+ for (int i = 0; i < list.size(); i++) {
+ SoMap json = (SoMap) list.get(i);
+ hash.put(json.getString(idKey), json);
+ }
+ // 遍历结果集
+ for (int j = 0; j < list.size(); j++) {
+ // 单条记录
+ SoMap aVal = (SoMap) list.get(j);
+ // 在hash中取出key为单条记录中pid的值
+ SoMap hashVp = (SoMap) hash.get(aVal.get(parentIdKey, "").toString());
+ // 如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中
+ if (hashVp != null) {
+ // 检查是否有child属性,有则添加,没有则新建
+ if (hashVp.get(childListKey) != null) {
+ @SuppressWarnings("unchecked")
+ List ch = (List) hashVp.get(childListKey);
+ ch.add(aVal);
+ hashVp.put(childListKey, ch);
+ } else {
+ List ch = new ArrayList();
+ ch.add(aVal);
+ hashVp.put(childListKey, ch);
+ }
+ } else {
+ newTreeList.add(aVal);
+ }
+ }
+ return newTreeList;
+ }
+
+
+
+ /** 指定字符串的字符串下划线转大写模式 */
+ private static String wordEachBig(String str){
+ String newStr = "";
+ for (String s : str.split("_")) {
+ newStr += wordFirstBig(s);
+ }
+ return newStr;
+ }
+ /** 返回下划线转小驼峰形式 */
+ private static String wordEachBigFs(String str){
+ return wordFirstSmall(wordEachBig(str));
+ }
+
+ /** 将指定单词首字母大写 */
+ private static String wordFirstBig(String str) {
+ return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
+ }
+
+ /** 将指定单词首字母小写 */
+ private static String wordFirstSmall(String str) {
+ return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
+ }
+
+ /** 下划线转中划线 */
+ private static String wordEachKebabCase(String str) {
+ return str.replaceAll("_", "-");
+ }
+
+ /** 驼峰转下划线 */
+ private static String wordHumpToLine(String str) {
+ return str.replaceAll("[A-Z]", "_$0").toLowerCase();
+ }
+
+
+}
diff --git a/sa-token-test/sa-token-springboot-test/pom.xml b/sa-token-test/sa-token-springboot-test/pom.xml
index 08bed033..efbdecea 100644
--- a/sa-token-test/sa-token-springboot-test/pom.xml
+++ b/sa-token-test/sa-token-springboot-test/pom.xml
@@ -23,6 +23,4 @@
-
-
diff --git a/sa-token-test/sa-token-springboot-test/src/test/java/com/pj/test/BasicsTest.java b/sa-token-test/sa-token-springboot-test/src/test/java/com/pj/test/BasicsTest.java
index ecd4371a..c93b7bdc 100644
--- a/sa-token-test/sa-token-springboot-test/src/test/java/com/pj/test/BasicsTest.java
+++ b/sa-token-test/sa-token-springboot-test/src/test/java/com/pj/test/BasicsTest.java
@@ -24,6 +24,7 @@ import cn.dev33.satoken.util.SaTokenConsts;
/**
* Sa-Token 基础API测试
*
+ *
注解详解参考: https://www.cnblogs.com/flypig666/p/11505277.html
* @author Auster
*
*/