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 * */