Compare commits

...

62 Commits

Author SHA1 Message Date
曾文豪
2b70c8121d publish 2.0.1 2024-08-27 13:41:58 +08:00
曾文豪
fd30c5cf36 perf:key不用实现TsAuthorityHandler类 2024-08-27 13:41:00 +08:00
曾文豪
0310bd4a15 perf:更新用户职位的时候,清除缓存 2024-08-27 13:38:57 +08:00
曾文豪
80ec6d2e3d perf:操作日志实时插入 2024-08-27 13:32:15 +08:00
曾文豪
d96f17b847 perf:调用日志只保存json,xml,text格式的返回值 2024-08-27 11:08:36 +08:00
曾文豪
7648eef981 perf:hutool.version放到parent中 2024-08-26 14:28:12 +08:00
曾文豪
79087f33e4 perf:过程日志改造 2024-08-26 13:47:31 +08:00
曾文豪
d0289d38b3 publish 2.0.0.rc46 2024-08-26 13:31:03 +08:00
曾文豪
90029faa53 publish 2.0.0.rc44 2024-08-26 13:23:51 +08:00
曾文豪
ecb5aa1b29 perf:过程日志改造 2024-08-26 13:20:45 +08:00
曾文豪
1db8ef9d85 publish 2.0.0.rc42 2024-08-23 15:04:40 +08:00
曾文豪
4bcae2f8d1 perf:移除hutool-json,直接使用fastjson 2024-08-23 15:04:20 +08:00
曾文豪
9bab4cdb25 feat:jwt中的hutool-json改为fastjson 2024-08-23 14:10:47 +08:00
曾文豪
856a9f01dd feat:把hutool的jwt直接放入util中 2024-08-23 11:38:29 +08:00
曾文豪
e57cd5e1c8 publish 2.0.0.rc40 2024-08-23 11:15:48 +08:00
曾文豪
542e23ac1b publish 2.0.0.rc38 2024-08-23 11:12:16 +08:00
曾文豪
6949f50c7c publish 2.0.0.rc37 2024-08-22 17:13:29 +08:00
曾文豪
7b43ae3ce1 publish 2.0.0.rc36 2024-08-22 17:09:14 +08:00
曾文豪
8ca283ca85 perf:登录的时候清除授权信息,相当于刷新一下权限 2024-08-22 09:46:04 +08:00
曾文豪
3728291e73 publish 2.0.0.rc35 2024-08-19 19:15:56 +08:00
曾文豪
6997dea968 publish 2.0.0.rc34 2024-08-16 11:50:48 +08:00
曾文豪
7f601c8e60 publish 2.0.0.rc33 2024-08-16 11:46:30 +08:00
曾文豪
7adc2a7c4b fix:修复无法删除权限的bug 2024-08-15 15:00:26 +08:00
曾文豪
f4f082cc86 publish 2.0.0.rc32 2024-08-13 16:52:08 +08:00
曾文豪
d21d84bc87 publish 2.0.0.rc30 2024-08-13 15:59:50 +08:00
曾文豪
60f996137f publish 2.0.0.rc28 2024-08-13 15:19:03 +08:00
曾文豪
11c8d234ff publish 2.0.0.rc26 2024-08-13 15:07:10 +08:00
曾文豪
125c681669 feat:新增deps和授权类型,用于解决依赖问题 2024-08-13 15:06:31 +08:00
曾文豪
b9347013f0 perf:权限增加一个source字段,用于标记权限来源 2024-08-13 14:49:22 +08:00
曾文豪
3415f0836f fix:修复platform获取方式异常的bug 2024-08-13 14:42:11 +08:00
曾文豪
22672c8a9e publish 2.0.0.rc25 2024-08-12 14:57:08 +08:00
曾文豪
a405aa7057 publish 2.0.0.rc24 2024-08-12 14:56:57 +08:00
曾文豪
e39dd646f5 publish 2.0.0.rc23 2024-08-12 14:38:13 +08:00
曾文豪
2ca224f7a5 fix:允许取消所有的授权 2024-08-11 20:19:57 +08:00
曾文豪
d4e97babf4 publish 2.0.0.rc22 2024-08-09 18:24:47 +08:00
曾文豪
a1c75d010b publish 2.0.0.rc21 2024-08-09 17:20:56 +08:00
曾文豪
9b6b3af33e publish 2.0.0.rc20 2024-08-08 16:20:34 +08:00
曾文豪
91833a44bb feat:增加RoleIgnore 2024-08-08 16:20:05 +08:00
曾文豪
8dc8709499 perf:代码结构调整 2024-08-08 16:16:21 +08:00
曾文豪
b34d9d6242 publish 2.0.0.rc19 2024-08-08 11:27:50 +08:00
曾文豪
2c9519a35e publish 2.0.0.rc18 2024-08-08 11:11:40 +08:00
曾文豪
f5dfce08ed publish 2.0.0.rc17 2024-08-08 09:24:00 +08:00
曾文豪
5d4e97b206 feat:增加权限校验 2024-08-08 09:23:30 +08:00
曾文豪
82d3f471f9 perf:TokenBean移除无用的属性 2024-08-07 23:43:57 +08:00
曾文豪
316909dd61 perf:调整授权缓存 2024-08-07 23:13:55 +08:00
曾文豪
5e308be3ba publish 2.0.0.rc16 2024-08-07 22:16:12 +08:00
曾文豪
ba80cebc45 feat:提交角色授权 2024-08-07 22:15:40 +08:00
曾文豪
0f5cdb49c0 feat:增加权限校验 2024-08-07 19:02:54 +08:00
曾文豪
1ee4b4af30 perf:请求时将通过getCurrentUserName()方法返回的对象检验用户是否非法 2024-08-07 14:09:28 +08:00
曾文豪
bb38428708 publish 2.0.0.rc15 2024-08-05 19:11:17 +08:00
曾文豪
a22467d4f1 publish 2.0.0.rc14 2024-08-05 14:59:55 +08:00
曾文豪
ad90c83cf7 perf:调整消息日志相关代码 2024-08-05 14:46:54 +08:00
曾文豪
cdae6a9868 publish 2.0.0.rc13 2024-08-05 14:19:06 +08:00
曾文豪
be08001f3f publish 2.0.0.rc13 2024-08-05 14:18:50 +08:00
曾文豪
333d283e24 perf:移除hutool的http模块,使用okhttp3 2024-07-30 13:39:45 +08:00
曾文豪
450d1fb869 publish 2.0.0.rc12 2024-07-29 14:28:21 +08:00
曾文豪
1412782327 publish 2.0.0.rc11 2024-07-29 10:31:03 +08:00
曾文豪
6127cd50a9 publish 2.0.0.rc10 2024-07-29 09:53:44 +08:00
曾文豪
19f4d0395e publish 2.0.0.rc9 2024-07-29 09:42:53 +08:00
曾文豪
d52edbb23c perf:钉钉对接调整 2024-07-26 16:12:29 +08:00
曾文豪
6c6d713c01 publish 2.0.0.rc8 2024-07-25 17:35:51 +08:00
曾文豪
8ea34c3ee0 perf:移除TimedCacheService、TimedCacheHelper,新增TsCacheService,同时实现了内存缓存、Redis缓存 2024-07-25 17:35:01 +08:00
147 changed files with 5274 additions and 1878 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,3 +1,12 @@
## 2.0.0.rc46
现在导入导出都接入底层流程,无需新增接口,只需要实现接口即可。
- 过程日志不再和操作日志同时存在
- 过程日志新增params参数
- 通过实现*TsImportHandler*接口编写导入逻辑
- 通过实现*TsExportHandler*接口编写导出逻辑
## 2.0.0.rc4 ## 2.0.0.rc4
- perfTsTokenConfig不在提供静态方法 - perfTsTokenConfig不在提供静态方法

24
pom.xml
View File

@@ -6,7 +6,7 @@
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>springboot-parent</name> <name>springboot-parent</name>
<description>杭州铁晟科技有限公司基础依赖</description> <description>杭州铁晟科技有限公司基础依赖</description>
@@ -35,6 +35,8 @@
<maven.compiler.source>8</maven.compiler.source> <maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target> <maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hutool.version>5.8.31</hutool.version>
</properties> </properties>
<developers> <developers>
@@ -58,61 +60,61 @@
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-database</artifactId> <artifactId>springboot-database</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-login</artifactId> <artifactId>springboot-login</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-web</artifactId> <artifactId>springboot-web</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-util</artifactId> <artifactId>springboot-util</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-platform</artifactId> <artifactId>springboot-platform</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-message</artifactId> <artifactId>springboot-message</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-encrypt</artifactId> <artifactId>springboot-encrypt</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-role</artifactId> <artifactId>springboot-role</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-annotation</artifactId> <artifactId>springboot-annotation</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-poi</artifactId> <artifactId>springboot-poi</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -6,11 +6,11 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-ademo</artifactId> <artifactId>springboot-ademo</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
<properties> <properties>
<maven.compiler.source>8</maven.compiler.source> <maven.compiler.source>8</maven.compiler.source>
@@ -36,6 +36,12 @@
<artifactId>easyexcel</artifactId> <artifactId>easyexcel</artifactId>
</dependency> </dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
</dependencies> </dependencies>
<repositories> <repositories>

View File

@@ -1,11 +1,12 @@
package com.tiesheng.demo.config; package com.tiesheng.demo.config;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.tiesheng.web.pojos.RequestUserInfo; import com.tiesheng.login.pojos.RequestUserInfo;
import com.tiesheng.web.pojos.dao.CorePlatformUnique; import com.tiesheng.login.pojos.dao.CorePlatformUnique;
import com.tiesheng.web.service.TieshengWebConfigurer; import com.tiesheng.login.service.TsLoginConfigurer;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.util.config.GlobalConfig; import com.tiesheng.util.config.GlobalConfig;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.web.service.TieshengWebConfigurer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -13,24 +14,30 @@ import javax.servlet.http.HttpServletResponse;
import java.util.Objects; import java.util.Objects;
@Component @Component
public class DemoWebConfigurer implements TieshengWebConfigurer { public class DemoWebConfigurer implements TieshengWebConfigurer, TsLoginConfigurer {
@Autowired @Autowired
GlobalConfig globalConfig; GlobalConfig globalConfig;
@Override @Override
public RequestUserInfo getCurrentUserName(TokenBean tokenBean) { public RequestUserInfo getCurrentUserName(TokenBean tokenBean) {
RequestUserInfo info = new RequestUserInfo(); RequestUserInfo info = new RequestUserInfo();
info.setId("1"); info.setId(tokenBean.getId());
info.setName("test"); info.setName(tokenBean.getExtra());
return info; return info;
} }
@Override
public TokenBean login(CorePlatformUnique platformUnique) { public TokenBean login(CorePlatformUnique platformUnique) {
TokenBean tokenBean = null; TokenBean tokenBean = null;
if (!StrUtil.isEmpty(platformUnique.getUserId())) { if (!StrUtil.isEmpty(platformUnique.getUserId())) {
tokenBean = new TokenBean(platformUnique.getUserId(), "", globalConfig.getService()); tokenBean = new TokenBean(platformUnique.getUserId(), "super");
} else if (Objects.equals(platformUnique.getUniqueId(), "1110290049")) {
tokenBean = new TokenBean("1", "super");
}
if (tokenBean != null) {
tokenBean.setExtra("1110290049");
} }
return tokenBean; return tokenBean;
@@ -39,13 +46,11 @@ public class DemoWebConfigurer implements TieshengWebConfigurer {
@Override @Override
public void redirect(TokenBean bean, String to, String extra, HttpServletResponse response) { public void redirect(TokenBean bean, String to, String extra, HttpServletResponse response) {
// 默认跳转到mobile
to = StrUtil.emptyToDefault(to, "static/mobile");
bean.setEnvironmentType(to);
if (Objects.equals(bean.getEnvironmentType(), "static/mobile")) {
globalConfig.redirect("static/mobile", "/?token=" + bean.toToken(), response);
}
} }
@Override
public TsLoginConfigurer configureLogin() {
return this;
}
} }

View File

@@ -1,15 +0,0 @@
package com.tiesheng.demo.config;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class DemoWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/mobile/**").addResourceLocations("classpath:/mobile/0.0.13/");
}
}

View File

@@ -1,42 +1,49 @@
package com.tiesheng.demo.controller; package com.tiesheng.demo.controller;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval; import cn.hutool.core.date.TimeInterval;
import cn.hutool.json.JSONUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.log.LogFactory; import cn.hutool.log.LogFactory;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tiesheng.annotation.role.RoleAuthority;
import com.tiesheng.annotation.token.TokenIgnore; import com.tiesheng.annotation.token.TokenIgnore;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.web.service.CoreLogService;
import com.tiesheng.web.service.CoreMessageService;
import com.tiesheng.web.util.ProcessImportConsumer;
import com.tiesheng.database.config.DbBackupConfig; import com.tiesheng.database.config.DbBackupConfig;
import com.tiesheng.demo.pojos.JsonTest;
import com.tiesheng.demo.pojos.PoiBean; import com.tiesheng.demo.pojos.PoiBean;
import com.tiesheng.demo.pojos.TestFile; import com.tiesheng.demo.pojos.TestFile;
import com.tiesheng.util.config.TsTokenConfig; import com.tiesheng.platform.config.ding.PlatformDingConfig;
import com.tiesheng.message.pojos.MessageReqResp; import com.tiesheng.platform.config.ding.bean.DingUserInfo;
import com.tiesheng.message.pojos.UserChannel;
import com.tiesheng.util.config.EncryptConfig; import com.tiesheng.util.config.EncryptConfig;
import com.tiesheng.util.config.GlobalConfig; import com.tiesheng.util.config.GlobalConfig;
import com.tiesheng.util.config.Ip2regionConfig; import com.tiesheng.util.config.Ip2regionConfig;
import com.tiesheng.util.config.TsTokenConfig;
import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.pojos.ApiResp; import com.tiesheng.util.pojos.ApiResp;
import com.tiesheng.util.pojos.FileUploadPath; import com.tiesheng.util.pojos.FileUploadPath;
import com.tiesheng.util.service.TsCacheService;
import com.tiesheng.util.service.http.OkHttpUtil;
import com.tiesheng.web.service.CoreLogService;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
/** /**
* @author hao * @author hao
*/ */
@RestController @RestController
@RequestMapping("/test") @RequestMapping("/test")
@RoleAuthority(value = "test", group = "test")
public class TestController { public class TestController {
@Autowired @Autowired
@@ -48,11 +55,13 @@ public class TestController {
@Autowired @Autowired
EncryptConfig encryptConfig; EncryptConfig encryptConfig;
@Autowired @Autowired
CoreMessageService coreMessageService;
@Autowired
CoreLogService coreLogService; CoreLogService coreLogService;
@Autowired @Autowired
DbBackupConfig dbBackupConfig; DbBackupConfig dbBackupConfig;
@Autowired
TsCacheService tsCacheService;
@Autowired
PlatformDingConfig platformDingConfig;
@RequestMapping("/index") @RequestMapping("/index")
@@ -63,45 +72,67 @@ public class TestController {
@RequestMapping("/redirect") @RequestMapping("/redirect")
@TokenIgnore @TokenIgnore
public void redirect(HttpServletResponse response) { @RoleAuthority("redirect")
public ApiResp<JsonTest> redirect(@RequestBody JsonTest dto, HttpServletResponse response) {
ArrayList<String> strings = CollUtil.newArrayList("11111", "22222"); JsonTest jsonTest = new JsonTest();
coreLogService.addProcess("fdfd", strings, new ProcessImportConsumer<String>() { jsonTest.setNow(DateUtil.date());
@Override jsonTest.setNow1(DateUtil.date());
public int accept(List<String> list) {
LogFactory.get().info("list: " + list.size());
return 0;
}
@Override String jsonString = JSON.toJSONString(jsonTest);
public String getFailFile() { LogFactory.get().info(jsonString);
LogFactory.get().info("getFailFile: " + strings.size());
return null;
}
});
LogFactory.get().info("strings: " + strings.size()); LogFactory.get().info(JSON.toJSONString(dto));
// ArrayList<String> strings = CollUtil.newArrayList("11111", "22222");
// coreLogService.addProcess("fdfd", strings, new ProcessImportConsumer<String>() {
// @Override
// public int accept(List<String> list) {
// LogFactory.get().info("list: " + list.size());
// return 0;
// }
//
// @Override
// public String getFailFile() {
// LogFactory.get().info("getFailFile: " + strings.size());
// return null;
// }
// });
//
// LogFactory.get().info("strings: " + strings.size());
// tsTokenConfig.validToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NzYwMDY4NzUsImlkIjoiMSIsImVudmlyb25tZW50VHlwZSI6Im1vYmlsZSIsInNlcnZpY2UiOiJjb250ZXN0LXJlc2VydmUiLCJleHRyYSI6IiJ9.nsfxEFpCNHC7eNCS5DJXdu1VDdnHrTjSfgrozND70Lc", true); // tsTokenConfig.validToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NzYwMDY4NzUsImlkIjoiMSIsImVudmlyb25tZW50VHlwZSI6Im1vYmlsZSIsInNlcnZpY2UiOiJjb250ZXN0LXJlc2VydmUiLCJleHRyYSI6IiJ9.nsfxEFpCNHC7eNCS5DJXdu1VDdnHrTjSfgrozND70Lc", true);
globalConfig.redirect("mobile", "/test", response); // globalConfig.redirect("mobile", "/test", response);
return ApiResp.respOK(jsonTest);
} }
@GetMapping("/send") @GetMapping("/send")
@TokenIgnore @TokenIgnore
public ApiResp<MessageReqResp> sendMessage() { public ApiResp<String> sendMessage() {
MessageReqResp reqResp = coreMessageService.send(new UserChannel("13567116463", "sms"), // MessageReqResp reqResp = coreMessageService.send(new UserChannel("13567116463", "sms"),
JSONUtil.createObj().putOpt("action", "sms-visitor-invite")); // JSONUtil.createObj().putOpt("action", "sms-visitor-invite"));
return ApiResp.respOK(reqResp); return ApiResp.respOK(null);
} }
@RequestMapping("/export") @RequestMapping("/export")
@TokenIgnore
public ApiResp<String> export() { public ApiResp<String> export() {
// tsCacheService.put("fdfhdfd", "fdfdfdfd");
// String s = tsCacheService.get("fdfhdfd");
// LogFactory.get().info("export: " + s);
platformDingConfig.syncDeptUser("", null, new Consumer<DingUserInfo>() {
@Override
public void accept(DingUserInfo dingUserInfo) {
}
});
// List<TestFile> list = new ArrayList<>(); // List<TestFile> list = new ArrayList<>();
// list.add(new TestFile("11111")); // list.add(new TestFile("11111"));
// list.add(new TestFile("22222")); // list.add(new TestFile("22222"));
@@ -122,14 +153,13 @@ public class TestController {
} }
@RequestMapping("searchIP") @PostMapping("searchIP")
@TokenIgnore @TokenIgnore
public ApiResp<String> searchIp() { public ApiResp<String> searchIp() {
String search = ip2regionConfig.search("127.0.0.1"); String search = ip2regionConfig.search("127.0.0.1");
return ApiResp.respOK(search); return ApiResp.respOK(search);
} }
@RequestMapping("desensitize") @RequestMapping("desensitize")
@TokenIgnore @TokenIgnore
public ApiResp<List<TestFile>> desensitize() { public ApiResp<List<TestFile>> desensitize() {
@@ -177,18 +207,31 @@ public class TestController {
return ApiResp.respOK(""); return ApiResp.respOK("");
} }
@RequestMapping("poiTool")
@GetMapping("getwxacode")
@TokenIgnore @TokenIgnore
public ApiResp<String> poiTool() { public ApiResp<String> getwxacode() {
TimeInterval timeInterval = new TimeInterval(); String id = "test_1111111";
FileUploadPath path = FileUploadPath.random("png");
FileUploadPath file = FileUploadPath.file("/upload/test.xlsx"); JSONObject paramJson = new JSONObject();
paramJson.put("page", "pages/login/index");
ExcelUtil.getReader(new File(file.getAbsolutePath())).read(); paramJson.put("scene", "no=" + id);
paramJson.put("env_version", "develop");
System.out.println("timeInterval: " + timeInterval.interval()); paramJson.put("width", 430);
paramJson.put("is_hyaline", false);
return ApiResp.respOK(""); paramJson.put("auto_color", false);
FileUploadPath file = FileUploadPath.file(id + ".png");
try {
Request request = OkHttpUtil.ofPost("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" +
"83_7xqG36kdgwuf8zzWLY3jtz7bg4ucziN-0oxbE0X9zBzwbjZ4S4Ss2RM9uHeSIcRp2K-wEp6MLzWhqo2AXj0Jpzd6IiJdUsRxqdHPvEWqAdOgt83vzZwdDf7tZBkGNGeAFASZS",
paramJson);
Response execute1 = OkHttpUtil.ofHttpClient().build().newCall(request).execute();
FileUtil.writeFromStream(execute1.body().byteStream(), file.getAbsolutePath());
execute1.close();
} catch (Exception e) {
throw new ApiException("每分钟最多生成5000个二维码请稍后再试");
}
return ApiResp.respOK(path.getHttpPath());
} }
} }

View File

@@ -0,0 +1,60 @@
package com.tiesheng.demo.pojos;
import com.alibaba.fastjson.annotation.JSONField;
import java.util.Date;
public class JsonTest {
private Date now;
@JSONField(format = "yyyy-MM-dd")
private Date now1;
private String no;
private String name;
private JsonTest child;
///////////////////////////////////////////////////////////////////////////
// setter\getter
///////////////////////////////////////////////////////////////////////////
public Date getNow() {
return now;
}
public void setNow(Date now) {
this.now = now;
}
public Date getNow1() {
return now1;
}
public void setNow1(Date now1) {
this.now1 = now1;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public JsonTest getChild() {
return child;
}
public void setChild(JsonTest child) {
this.child = child;
}
}

View File

@@ -1,6 +1,5 @@
package com.tiesheng.demo.pojos; package com.tiesheng.demo.pojos;
import com.tiesheng.annotation.desensitize.Desensitize;
import com.tiesheng.poi.pojos.PoiWriteBase; import com.tiesheng.poi.pojos.PoiWriteBase;
@@ -8,7 +7,6 @@ public class TestParent implements PoiWriteBase {
private String id; private String id;
@Desensitize()
private String test; private String test;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@@ -1,10 +1,9 @@
package com.tiesheng.demo.service; package com.tiesheng.demo.service;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tiesheng.message.config.aliyun.AliyunSmsHandler; import com.tiesheng.message.config.aliyun.AliyunSmsHandler;
import com.tiesheng.message.pojos.AliyunTempParam; import com.tiesheng.message.config.aliyun.AliyunTempParam;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Objects; import java.util.Objects;
@@ -15,16 +14,17 @@ public class DemoSmsHandler implements AliyunSmsHandler {
@Override @Override
public AliyunTempParam handler(JSONObject obj) { public AliyunTempParam handler(JSONObject obj) {
String action = obj.getStr("action"); String action = obj.getString("action");
AliyunTempParam param = new AliyunTempParam(); AliyunTempParam param = new AliyunTempParam();
param.setSignName("智慧校园"); param.setSignName("智慧校园");
if (Objects.equals(action, "sms-visitor-invite")) { if (Objects.equals(action, "sms-visitor-invite")) {
param.setTemplateCode("SMS_276125463"); param.setTemplateCode("SMS_276125463");
param.setTemplateParam(JSONUtil.createObj()
.putOpt("date", DateUtil.today()) JSONObject object = new JSONObject();
); object.put("date", DateUtil.today());
param.setTemplateParam(object);
} }
return param; return param;

View File

@@ -0,0 +1,39 @@
package com.tiesheng.demo.service;
import cn.hutool.core.collection.CollUtil;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.web.pojos.imex.ImportDealDTO;
import com.tiesheng.web.service.imex.TsImportHandler;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserImportHandler implements TsImportHandler<String> {
@Override
public List<String> ready(ImportDealDTO dto, TokenBean token) {
return CollUtil.newArrayList("11111");
}
@Override
public String getTemplateUrl() {
return "/template/xsxxzx_teacher_leader.xlsx";
}
@Override
public String getAction() {
return "user_import";
}
@Override
public int batchHandler(List<String> list) {
return 0;
}
@Override
public String getResultFile() {
return "";
}
}

View File

@@ -7,8 +7,17 @@ spring:
url: jdbc:mysql://47.96.30.85:3306/com_tiesheng_web?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&queryTimeout=5400&allowMultiQueries=true&serverTimezone=GMT%2B8 url: jdbc:mysql://47.96.30.85:3306/com_tiesheng_web?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&queryTimeout=5400&allowMultiQueries=true&serverTimezone=GMT%2B8
username: com_tiesheng_web username: com_tiesheng_web
password: 4Xo$XheGFc password: 4Xo$XheGFc
redis:
url: redis://kyF0zUL3011111@47.96.30.85:6234
database: 1
platform: platform:
ding:
global:
corpId: dingebe664cb3baf3a52a39a90f97fcb1e09
agentId: 2474444028
appKey: dingyvf1niiajfaajyo5
appSecret: y6SGZ4fvPbdbj4RVbDWLeMBkGgplaOdnTA_9wN5TDdOV_Y47alYc1LDlIHjsBJE1
wxmp: wxmp:
global: global:
app-id: wxa0343fe519824651 app-id: wxa0343fe519824651
@@ -18,11 +27,12 @@ tiesheng:
token: token:
test-map: test-map:
"1111": "1111":
id: "1111" id: "1"
role-id: "student_bks" role-id: "1822829641023524865"
global: global:
version: 2 version: 2
host: http://localhost:8100 host: http://localhost:8100
service: yrx
aliyun: aliyun:
access-key-id: LTAI5tJtbgBCnTY5eS4SmrTf access-key-id: LTAI5tJtbgBCnTY5eS4SmrTf
access-key-secret: JIHqpRUFffCHhXaJEVvWN31WcexWqG access-key-secret: JIHqpRUFffCHhXaJEVvWN31WcexWqG

View File

@@ -0,0 +1,42 @@
package com.tiesheng.demo;
import cn.hutool.core.date.DateUtil;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONPath;
import com.tiesheng.demo.pojos.JsonTest;
import com.tiesheng.login.pojos.RequestUserInfo;
public class MainTest {
public static void main(String[] args) {
MainTest test = new MainTest();
test.testJsonSelf();
}
private void testJsonSelf() {
RequestUserInfo info = new RequestUserInfo();
info.setId("1");
info.setName("name");
info.setData(info);
String jsonStr = JSON.toJSONString(info);
LogFactory.get().info(jsonStr);
}
private void testJsonPath() {
JsonTest jsonTest = new JsonTest();
jsonTest.setNo("1111");
jsonTest.setName("1111");
jsonTest.setNow(DateUtil.date());
jsonTest.setChild(jsonTest);
JSONPath jsonPath = JSONPath.compile("child.no");
String eval = jsonPath.eval(jsonTest, String.class);
LogFactory.get().info(eval);
}
}

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-annotation</artifactId> <artifactId>springboot-annotation</artifactId>

View File

@@ -1,16 +0,0 @@
package com.tiesheng.annotation.desensitize;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @author hao
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Desensitize {
int prefix() default 1;
int suffix() default 1;
}

View File

@@ -0,0 +1,41 @@
package com.tiesheng.annotation.role;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface RoleAuthority {
/**
* 编号
*
* @return
*/
String value();
/**
* 平台类型
*
* @return
*/
String platform() default "";
/**
* 分组
*
* @return
*/
String[] group() default {};
/**
* 依赖其他权限
*
* @return
*/
String[] deps() default {};
}

View File

@@ -0,0 +1,9 @@
package com.tiesheng.annotation.role;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface RoleIgnore {
}

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-database</artifactId> <artifactId>springboot-database</artifactId>

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-encrypt</artifactId> <artifactId>springboot-encrypt</artifactId>

View File

@@ -3,7 +3,7 @@ package com.tiesheng.encrypt.config;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON;
import com.tiesheng.util.config.EncryptConfig; import com.tiesheng.util.config.EncryptConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
@@ -66,7 +66,7 @@ public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
DecryptHttpInputMessage(HttpInputMessage inputMessage, EncryptConfig encryptConfig) throws Exception { DecryptHttpInputMessage(HttpInputMessage inputMessage, EncryptConfig encryptConfig) throws Exception {
this.headers = inputMessage.getHeaders(); this.headers = inputMessage.getHeaders();
String bodyStr = IoUtil.read(inputMessage.getBody(), CharsetUtil.CHARSET_UTF_8); String bodyStr = IoUtil.read(inputMessage.getBody(), CharsetUtil.CHARSET_UTF_8);
String encryptData = JSONUtil.parseObj(bodyStr).getStr("encryptData"); String encryptData = JSON.parseObject(bodyStr).getString("encryptData");
if (!StrUtil.isEmpty(encryptData)) { if (!StrUtil.isEmpty(encryptData)) {
String decrypt = encryptConfig.decrypt(encryptData); String decrypt = encryptConfig.decrypt(encryptData);
this.body = IoUtil.toStream(decrypt, Charset.defaultCharset()); this.body = IoUtil.toStream(decrypt, Charset.defaultCharset());

View File

@@ -2,7 +2,7 @@ package com.tiesheng.encrypt.config;
import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.log.LogFactory; import cn.hutool.log.LogFactory;
import com.tiesheng.util.CommonUtil; import com.alibaba.fastjson.JSON;
import com.tiesheng.util.config.EncryptConfig; import com.tiesheng.util.config.EncryptConfig;
import com.tiesheng.util.pojos.ApiResp; import com.tiesheng.util.pojos.ApiResp;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -42,7 +42,7 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<ApiResp> {
} }
body.setEncrypt(true); body.setEncrypt(true);
body.setData(encryptConfig.encrypt(CommonUtil.writeJsonString(data))); body.setData(encryptConfig.encrypt(JSON.toJSONString(data)));
return body; return body;
} catch (Exception var17) { } catch (Exception var17) {
LogFactory.get().info("加密数据异常", var17); LogFactory.get().info("加密数据异常", var17);

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-login</artifactId> <artifactId>springboot-login</artifactId>

View File

@@ -1,16 +0,0 @@
package com.tiesheng.login;
import com.tiesheng.platform.PlatformAutoConfigurer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
/**
* @author hao
*/
@ComponentScan({
"com.tiesheng.login.**.*",
})
@Import(PlatformAutoConfigurer.class)
public class LoginAutoConfigurer {
}

View File

@@ -3,7 +3,10 @@ package com.tiesheng.login.config;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.tiesheng.annotation.token.TokenIgnore; import com.tiesheng.annotation.token.TokenIgnore;
import com.tiesheng.login.pojos.RequestUserInfo;
import com.tiesheng.login.service.TsLoginConfigurer;
import com.tiesheng.util.config.TsTokenConfig; import com.tiesheng.util.config.TsTokenConfig;
import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.pojos.TokenBean; import com.tiesheng.util.pojos.TokenBean;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -29,6 +32,8 @@ public class TokenWebMvcConfigurer implements WebMvcConfigurer {
@Resource @Resource
TsTokenConfig tsTokenConfig; TsTokenConfig tsTokenConfig;
@Resource
TsLoginConfigurer tsLoginConfigurer;
@Override @Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
@@ -96,8 +101,14 @@ public class TokenWebMvcConfigurer implements WebMvcConfigurer {
return true; return true;
} }
// token验证 // 验证TOKEN是否存在
tsTokenConfig.validToken(request, true); TokenBean tokenBean = tsTokenConfig.validToken(request, true);
// 验证用户是否存在
RequestUserInfo cachedUserInfo = tsLoginConfigurer.getCachedUserInfo(tokenBean, false);
if (cachedUserInfo == null) {
throw new ApiException("非法TOKEN请重新登录");
}
return true; return true;
} }

View File

@@ -6,13 +6,11 @@ import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.tiesheng.annotation.operation.OperationIgnore; import com.tiesheng.annotation.operation.OperationIgnore;
import com.tiesheng.annotation.token.TokenIgnore; import com.tiesheng.annotation.token.TokenIgnore;
import com.tiesheng.util.config.TsTokenConfig;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.login.pojos.CodeExtraDTO; import com.tiesheng.login.pojos.CodeExtraDTO;
import com.tiesheng.login.pojos.DoLoginInfo; import com.tiesheng.login.pojos.DoLoginInfo;
import com.tiesheng.login.pojos.UniqueIndexDTO; import com.tiesheng.login.pojos.UniqueIndexDTO;
import com.tiesheng.login.pojos.WxminiLoginVo; import com.tiesheng.login.pojos.WxminiLoginVo;
import com.tiesheng.login.service.TieshengLoginConfigurer; import com.tiesheng.login.service.CorePlatformUniqueService;
import com.tiesheng.platform.config.ding.PlatformDingConfig; import com.tiesheng.platform.config.ding.PlatformDingConfig;
import com.tiesheng.platform.config.ding.bean.DingJsapiSignature; import com.tiesheng.platform.config.ding.bean.DingJsapiSignature;
import com.tiesheng.platform.config.ding.bean.DingUserInfo; import com.tiesheng.platform.config.ding.bean.DingUserInfo;
@@ -23,8 +21,10 @@ import com.tiesheng.platform.config.wxmp.bean.WxConfigBean;
import com.tiesheng.platform.config.wxmp.bean.WxJsapiSignature; import com.tiesheng.platform.config.wxmp.bean.WxJsapiSignature;
import com.tiesheng.platform.config.wxmp.bean.WxUserInfo; import com.tiesheng.platform.config.wxmp.bean.WxUserInfo;
import com.tiesheng.util.config.GlobalConfig; import com.tiesheng.util.config.GlobalConfig;
import com.tiesheng.util.config.TsTokenConfig;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.pojos.ApiResp; import com.tiesheng.util.pojos.ApiResp;
import com.tiesheng.util.pojos.TokenBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -49,7 +49,7 @@ public class LoginController {
@Autowired @Autowired
PlatformWxminiConfig platformWxminiConfig; PlatformWxminiConfig platformWxminiConfig;
@Autowired @Autowired
TieshengLoginConfigurer tieshengLoginConfigurer; CorePlatformUniqueService corePlatformUniqueService;
@Autowired @Autowired
GlobalConfig globalConfig; GlobalConfig globalConfig;
@Autowired @Autowired
@@ -65,13 +65,13 @@ public class LoginController {
@OperationIgnore @OperationIgnore
public void uniqueIndex(UniqueIndexDTO dto, HttpServletResponse response) { public void uniqueIndex(UniqueIndexDTO dto, HttpServletResponse response) {
if (tsTokenConfig.isValidLoginSign() && !dto.validSign()) { if (tsTokenConfig.isValidLoginSign() && !dto.validSign()) {
tieshengLoginConfigurer.onSignError(response); corePlatformUniqueService.onSignError(response);
return; return;
} }
TokenBean tokenBean = tieshengLoginConfigurer.doLogin(new DoLoginInfo("web_unique_redirect", TokenBean tokenBean = corePlatformUniqueService.login(new DoLoginInfo("web_unique_redirect",
dto.getNo(), dto.getPlatform(), dto.getInfo())); dto.getNo(), dto.getPlatform(), dto.getInfo()));
tieshengLoginConfigurer.onLoginRedirect(tokenBean, dto.getTo(), dto.getExtra(), response); corePlatformUniqueService.redirect(tokenBean, dto.getTo(), dto.getExtra(), response);
} }
@@ -84,7 +84,7 @@ public class LoginController {
@PostMapping("/unique/index") @PostMapping("/unique/index")
@OperationIgnore @OperationIgnore
public ApiResp<String> uniqueIndex(@RequestBody UniqueIndexDTO dto) { public ApiResp<String> uniqueIndex(@RequestBody UniqueIndexDTO dto) {
TokenBean tokenBean = tieshengLoginConfigurer.doLogin(new DoLoginInfo("web_unique_index", TokenBean tokenBean = corePlatformUniqueService.login(new DoLoginInfo("web_unique_index",
dto.getNo(), dto.getPlatform(), dto.getInfo())); dto.getNo(), dto.getPlatform(), dto.getInfo()));
if (tokenBean == null || StrUtil.isEmpty(tokenBean.getId())) { if (tokenBean == null || StrUtil.isEmpty(tokenBean.getId())) {
throw new ApiException("登录失败"); throw new ApiException("登录失败");
@@ -134,9 +134,9 @@ public class LoginController {
public void dingOauth2(@PathVariable String service, CodeExtraDTO dto, HttpServletResponse response) { public void dingOauth2(@PathVariable String service, CodeExtraDTO dto, HttpServletResponse response) {
DingUserSimple userSimple = platformDingConfig.getUserIdByCode(service, dto.getCode()); DingUserSimple userSimple = platformDingConfig.getUserIdByCode(service, dto.getCode());
DingUserInfo dingUserInfo = platformDingConfig.topapiV2UserGet(service, userSimple.getUserid()); DingUserInfo dingUserInfo = platformDingConfig.topapiV2UserGet(service, userSimple.getUserid());
TokenBean tokenBean = tieshengLoginConfigurer.doLogin(new DoLoginInfo(dingUserInfo.getAppId(), TokenBean tokenBean = corePlatformUniqueService.login(new DoLoginInfo(dingUserInfo.getAppId(),
dingUserInfo.getUserid(), "ding", JSON.toJSONString(dingUserInfo))); dingUserInfo.getUserid(), "ding", JSON.toJSONString(dingUserInfo)));
tieshengLoginConfigurer.onLoginRedirect(tokenBean, dto.getTo(), dto.getExtra(), response); corePlatformUniqueService.redirect(tokenBean, dto.getTo(), dto.getExtra(), response);
} }
@@ -182,9 +182,9 @@ public class LoginController {
@OperationIgnore @OperationIgnore
public void wxmpOauth2(@PathVariable String service, CodeExtraDTO dto, HttpServletResponse response) { public void wxmpOauth2(@PathVariable String service, CodeExtraDTO dto, HttpServletResponse response) {
WxUserInfo wxUserInfo = platformWxmpConfig.getOAuth2AccessToken(service, dto.getCode()); WxUserInfo wxUserInfo = platformWxmpConfig.getOAuth2AccessToken(service, dto.getCode());
TokenBean tokenBean = tieshengLoginConfigurer.doLogin(new DoLoginInfo(wxUserInfo.getAppId(), TokenBean tokenBean = corePlatformUniqueService.login(new DoLoginInfo(wxUserInfo.getAppId(),
wxUserInfo.getOpenid(), "wxmp", JSON.toJSONString(wxUserInfo))); wxUserInfo.getOpenid(), "wxmp", JSON.toJSONString(wxUserInfo)));
tieshengLoginConfigurer.onLoginRedirect(tokenBean, dto.getTo(), dto.getExtra(), response); corePlatformUniqueService.redirect(tokenBean, dto.getTo(), dto.getExtra(), response);
} }
@@ -214,7 +214,7 @@ public class LoginController {
public ApiResp<WxminiLoginVo> wxminiIndex(@PathVariable String service, String code) { public ApiResp<WxminiLoginVo> wxminiIndex(@PathVariable String service, String code) {
String openid = platformWxminiConfig.jscode2session(service, code); String openid = platformWxminiConfig.jscode2session(service, code);
WxConfigBean configBean = platformWxminiConfig.getConfigBean(service); WxConfigBean configBean = platformWxminiConfig.getConfigBean(service);
TokenBean tokenBean = tieshengLoginConfigurer.doLogin(new DoLoginInfo(configBean.getAppId(), TokenBean tokenBean = corePlatformUniqueService.login(new DoLoginInfo(configBean.getAppId(),
openid, "wxmini", "{}")); openid, "wxmini", "{}"));
WxminiLoginVo loginVo = new WxminiLoginVo(); WxminiLoginVo loginVo = new WxminiLoginVo();

View File

@@ -1,7 +1,7 @@
package com.tiesheng.web.mapper; package com.tiesheng.login.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tiesheng.web.pojos.dao.CoreLogLogin; import com.tiesheng.login.pojos.dao.CoreLogLogin;
public interface CoreLogLoginMapper extends BaseMapper<CoreLogLogin> { public interface CoreLogLoginMapper extends BaseMapper<CoreLogLogin> {
} }

View File

@@ -1,7 +1,7 @@
package com.tiesheng.web.mapper; package com.tiesheng.login.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tiesheng.web.pojos.dao.CorePlatformUnique; import com.tiesheng.login.pojos.dao.CorePlatformUnique;
public interface CorePlatformUniqueMapper extends BaseMapper<CorePlatformUnique> { public interface CorePlatformUniqueMapper extends BaseMapper<CorePlatformUnique> {
} }

View File

@@ -1,4 +1,4 @@
package com.tiesheng.web.pojos; package com.tiesheng.login.pojos;
/** /**
* 当前token的数据 * 当前token的数据

View File

@@ -1,4 +1,4 @@
package com.tiesheng.web.pojos.dao; package com.tiesheng.login.pojos.dao;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;

View File

@@ -1,4 +1,4 @@
package com.tiesheng.web.pojos.dao; package com.tiesheng.login.pojos.dao;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;

View File

@@ -0,0 +1,119 @@
package com.tiesheng.login.service;
import cn.hutool.core.util.StrUtil;
import com.tiesheng.login.mapper.CoreLogLoginMapper;
import com.tiesheng.login.mapper.CorePlatformUniqueMapper;
import com.tiesheng.login.pojos.DoLoginInfo;
import com.tiesheng.login.pojos.RequestUserInfo;
import com.tiesheng.login.pojos.dao.CoreLogLogin;
import com.tiesheng.login.pojos.dao.CorePlatformUnique;
import com.tiesheng.util.ServletKit;
import com.tiesheng.util.config.Ip2regionConfig;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.util.service.TsCacheService;
import com.tiesheng.util.service.TsServiceBase;
import com.tiesheng.util.service.role.TsAuthorityHandler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
/**
* @author hao
*/
@Service
public class CorePlatformUniqueService extends TsServiceBase<CorePlatformUniqueMapper, CorePlatformUnique> {
@Resource
CoreLogLoginMapper coreLogLoginMapper;
@Resource
Ip2regionConfig ip2regionConfig;
@Resource
TsLoginConfigurer tsLoginConfigurer;
@Transactional(rollbackFor = Exception.class)
public TokenBean login(DoLoginInfo loginInfo) {
CorePlatformUnique platformUnique = getOneByColumn("unique_id", loginInfo.getUnique());
if (platformUnique == null) {
platformUnique = new CorePlatformUnique();
platformUnique.setAppId(loginInfo.getAppId());
platformUnique.setUniqueId(loginInfo.getUnique());
}
platformUnique.setPlatform(loginInfo.getPlatform());
platformUnique.setInfo(loginInfo.getInfo());
saveOrUpdate(platformUnique);
String oldUserId = platformUnique.getUserId();
TokenBean tokenBean = tsLoginConfigurer.login(platformUnique);
if (tokenBean != null) {
// 清除授权信息
TsCacheService.of().remove(StrUtil.format(TsAuthorityHandler.CACHE_AUTHORITY,
tokenBean.getRoleId(), tokenBean.getId()));
// 添加登录日志
addLoginLog(platformUnique, tokenBean);
// 更新唯一值
if (!StrUtil.isEmpty(tokenBean.getId()) &&
!Objects.equals(oldUserId, tokenBean.getId())) {
platformUnique.setUserId(tokenBean.getId());
saveOrUpdate(platformUnique);
}
}
return tokenBean;
}
/**
* 授权登录回调
*
* @param bean
*/
public void redirect(TokenBean bean, String to, String extra, HttpServletResponse response) {
tsLoginConfigurer.redirect(bean, to, extra, response);
}
/**
* 签名错误的时候
*/
public void onSignError(HttpServletResponse response) {
tsLoginConfigurer.onSignError(response);
}
///////////////////////////////////////////////////////////////////////////
// 登录日志
///////////////////////////////////////////////////////////////////////////
/**
* 添加登录日志
*
* @param platformUnique
* @param tokenBean
*/
public void addLoginLog(CorePlatformUnique platformUnique, TokenBean tokenBean) {
HttpServletRequest request = ServletKit.getRequest();
String ip = ServletKit.getClientIP(request);
CoreLogLogin login = new CoreLogLogin();
login.setUserId(tokenBean.getId());
login.setPlatform(platformUnique.getPlatform());
RequestUserInfo requestUserInfo = tsLoginConfigurer.getCachedUserInfo(tokenBean, true);
login.setUserName(requestUserInfo.getName());
login.setIp(ip);
login.setAddress(ip2regionConfig.search(login.getIp()));
coreLogLoginMapper.insert(login);
}
}

View File

@@ -1,36 +0,0 @@
package com.tiesheng.login.service;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.login.pojos.DoLoginInfo;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletResponse;
/**
* @author hao
*/
@Configuration
public interface TieshengLoginConfigurer {
/**
* 执行登录
*
* @return
*/
TokenBean doLogin(DoLoginInfo loginInfo);
/**
* 授权登录回调
*
* @param bean
*/
void onLoginRedirect(TokenBean bean, String to, String extra, HttpServletResponse response);
/**
* 签名错误的时候
*/
void onSignError(HttpServletResponse response);
}

View File

@@ -0,0 +1,69 @@
package com.tiesheng.login.service;
import cn.hutool.core.util.StrUtil;
import com.tiesheng.login.pojos.RequestUserInfo;
import com.tiesheng.login.pojos.dao.CorePlatformUnique;
import com.tiesheng.util.ServletKit;
import com.tiesheng.util.config.TsTokenConfig;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.util.service.TsCacheService;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletResponse;
/**
* @author hao
*/
@Configuration
public interface TsLoginConfigurer {
/**
* 执行登录
*
* @return
*/
TokenBean login(CorePlatformUnique platformUnique);
/**
* 授权登录回调
*
* @param bean
*/
void redirect(TokenBean bean, String to, String extra, HttpServletResponse response);
/**
* 签名错误的时候
*/
default void onSignError(HttpServletResponse response) {
ServletKit.write(response, "404", "text");
}
/**
* 获取当前用户的信息
*
* @param tokenBean
* @return
*/
default RequestUserInfo getCachedUserInfo(TokenBean tokenBean, boolean force) {
String key = StrUtil.format(TsTokenConfig.CACHE_REQUEST_LOGIN_KEY, tokenBean.getId());
RequestUserInfo obj = TsCacheService.of().getObj(key, RequestUserInfo.class, -1);
if (obj == null || force) {
obj = getCurrentUserName(tokenBean);
}
if (obj != null) {
TsCacheService.of().putObj(key, obj, 30 * 60);
}
return obj;
}
/**
* 获取当前用户的姓名
*
* @return
*/
RequestUserInfo getCurrentUserName(TokenBean userId);
}

View File

@@ -1,32 +0,0 @@
package com.tiesheng.login.service.impl;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.login.pojos.DoLoginInfo;
import com.tiesheng.login.service.TieshengLoginConfigurer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletResponse;
/**
* @author hao
*/
@Configuration
@ConditionalOnMissingBean(value = TieshengLoginConfigurer.class, ignored = DefaultLoginConfigurer.class)
public class DefaultLoginConfigurer implements TieshengLoginConfigurer {
@Override
public TokenBean doLogin(DoLoginInfo loginInfo) {
return null;
}
@Override
public void onLoginRedirect(TokenBean bean, String to, String extra, HttpServletResponse response) {
}
@Override
public void onSignError(HttpServletResponse response) {
}
}

View File

@@ -1,6 +1,24 @@
SET NAMES utf8mb4; SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0; SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for core_log_login
-- ----------------------------
CREATE TABLE `core_log_login`
(
`id` varchar(50) NOT NULL,
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
`is_deleted` int(6) NOT NULL DEFAULT '0',
`user_id` varchar(50) DEFAULT NULL COMMENT '用户id',
`user_name` varchar(255) DEFAULT NULL COMMENT '用户姓名',
`platform` varchar(50) DEFAULT NULL COMMENT '登录方式',
`ip` varchar(100) DEFAULT NULL COMMENT 'ip',
`address` varchar(255) DEFAULT NULL COMMENT 'ip地址',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='日志-登录';
-- ---------------------------- -- ----------------------------
-- Table structure for core_platform_unique -- Table structure for core_platform_unique
-- ---------------------------- -- ----------------------------

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tiesheng.web.mapper.CoreLogLoginMapper"> <mapper namespace="com.tiesheng.login.mapper.CoreLogLoginMapper">
<resultMap id="BaseResultMap" type="com.tiesheng.web.pojos.dao.CoreLogLogin"> <resultMap id="BaseResultMap" type="com.tiesheng.login.pojos.dao.CoreLogLogin">
<!--@mbg.generated--> <!--@mbg.generated-->
<!--@Table core_log_login--> <!--@Table core_log_login-->
<id column="id" jdbcType="VARCHAR" property="id" /> <id column="id" jdbcType="VARCHAR" property="id" />

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tiesheng.web.mapper.CorePlatformUniqueMapper"> <mapper namespace="com.tiesheng.login.mapper.CorePlatformUniqueMapper">
<resultMap id="BaseResultMap" type="com.tiesheng.web.pojos.dao.CorePlatformUnique"> <resultMap id="BaseResultMap" type="com.tiesheng.login.pojos.dao.CorePlatformUnique">
<!--@mbg.generated--> <!--@mbg.generated-->
<!--@Table core_platform_unique--> <!--@Table core_platform_unique-->
<id column="id" jdbcType="VARCHAR" property="id" /> <id column="id" jdbcType="VARCHAR" property="id" />

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-message</artifactId> <artifactId>springboot-message</artifactId>

View File

@@ -1,7 +1,6 @@
package com.tiesheng.message.config.aliyun; package com.tiesheng.message.config.aliyun;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.tiesheng.message.pojos.AliyunTempParam;
public interface AliyunSmsHandler { public interface AliyunSmsHandler {

View File

@@ -5,13 +5,12 @@ import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSONObject;
import cn.hutool.json.JSONUtil; import com.tiesheng.message.service.TsMessageSender;
import com.tiesheng.message.pojos.AliyunTempParam;
import com.tiesheng.message.pojos.MessageReqResp;
import com.tiesheng.message.service.TieshengMessageSender;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.pojos.ApiResp;
import com.tiesheng.util.service.http.OkHttpUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -31,7 +30,7 @@ import java.util.stream.Collectors;
*/ */
@Configuration @Configuration
@ConfigurationProperties(prefix = "tiesheng.aliyun") @ConfigurationProperties(prefix = "tiesheng.aliyun")
public class AliyunSmsSender implements TieshengMessageSender { public class AliyunSmsSender implements TsMessageSender {
/** /**
* 请求地址 * 请求地址
@@ -102,12 +101,12 @@ public class AliyunSmsSender implements TieshengMessageSender {
String respBody = ""; String respBody = "";
try { try {
respBody = HttpUtil.get(ENDPOINT + "?Signature=" + signature + sortQueryStringTmp, 10 * 1000); respBody = OkHttpUtil.get(ENDPOINT + "?Signature=" + signature + sortQueryStringTmp);
} catch (Exception e) { } catch (Exception e) {
respBody = JSONUtil.createObj() JSONObject object = new JSONObject();
.putOpt("Code", "Error") object.put("Code", "Error");
.putOpt("Message", "消息通道异常") object.put("Message", "消息通道异常");
.toString(); respBody = object.toJSONString();
} }
return respBody; return respBody;
} }
@@ -120,17 +119,7 @@ public class AliyunSmsSender implements TieshengMessageSender {
* @param tempParam 短信模板 * @param tempParam 短信模板
* @return * @return
*/ */
public MessageReqResp sendSms(String phoneNumbers, AliyunTempParam tempParam) { public ApiResp<String> sendSms(String phoneNumbers, AliyunTempParam tempParam) {
MessageReqResp reqResp = new MessageReqResp("阿里云短信");
reqResp.setTarget(phoneNumbers);
reqResp.setResult(1);
reqResp.setContent(JSONUtil.createObj()
.putOpt("PhoneNumbers", phoneNumbers)
.putOpt("SignName", tempParam.getSignName())
.putOpt("TemplateCode", tempParam.getTemplateCode())
.putOpt("TemplateParam", tempParam.getTemplateParam())
.toString());
// 业务API参数 // 业务API参数
ConcurrentHashMap<String, String> queryMap = new ConcurrentHashMap<>(); ConcurrentHashMap<String, String> queryMap = new ConcurrentHashMap<>();
@@ -141,18 +130,16 @@ public class AliyunSmsSender implements TieshengMessageSender {
queryMap.put("TemplateParam", tempParam.getTemplateParam().toString()); queryMap.put("TemplateParam", tempParam.getTemplateParam().toString());
} }
reqResp.setRespBody(request("SendSms", queryMap)); JSONObject respObj = JSON.parseObject(request("SendSms", queryMap));
JSONObject respObj = JSONUtil.parseObj(reqResp.getRespBody()); if (!Objects.equals(respObj.getString("Code"), "OK")) {
if (!Objects.equals(respObj.getStr("Code"), "OK")) { return ApiResp.resp130(respObj.getString("Message"));
reqResp.setResult(0);
reqResp.setToast(respObj.getStr("Message"));
} }
return reqResp; return ApiResp.respOK("");
} }
@Override @Override
public MessageReqResp send(String user, JSONObject body) { public ApiResp<String> send(String user, JSONObject body) {
boolean mobile = Validator.isMobile(user); boolean mobile = Validator.isMobile(user);
if (!mobile) { if (!mobile) {
return null; return null;

View File

@@ -1,6 +1,6 @@
package com.tiesheng.message.pojos; package com.tiesheng.message.config.aliyun;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSONObject;
public class AliyunTempParam { public class AliyunTempParam {

View File

@@ -1,87 +0,0 @@
package com.tiesheng.message.pojos;
public class MessageReqResp {
private String type;
/**
* 发送对象
*/
private String target;
/**
* 发送内容
*/
private String content;
/**
* 返回结果
*/
private String respBody;
/**
* 结果0-否1-是
*/
private Integer result;
/**
* 提示的异常信息
*/
private String toast;
public MessageReqResp(String type) {
this.type = type;
}
///////////////////////////////////////////////////////////////////////////
// setter\getter
///////////////////////////////////////////////////////////////////////////
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRespBody() {
return respBody;
}
public void setRespBody(String respBody) {
this.respBody = respBody;
}
public Integer getResult() {
return result;
}
public void setResult(Integer result) {
this.result = result;
}
public String getToast() {
return toast;
}
public void setToast(String toast) {
this.toast = toast;
}
}

View File

@@ -1,13 +1,13 @@
package com.tiesheng.message.service; package com.tiesheng.message.service;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.tiesheng.message.pojos.MessageReqResp; import com.tiesheng.util.pojos.ApiResp;
/** /**
* @author hao * @author hao
*/ */
public interface TieshengMessageSender { public interface TsMessageSender {
/** /**
@@ -20,7 +20,7 @@ public interface TieshengMessageSender {
* @param channel 消息通道如果为空表示所有通道 * @param channel 消息通道如果为空表示所有通道
* @return 返回的内容如果为空表示发送成功 * @return 返回的内容如果为空表示发送成功
*/ */
MessageReqResp send(String user, JSONObject body); ApiResp<String> send(String user, JSONObject body);
/** /**

View File

@@ -1,15 +1,11 @@
package com.tiesheng.web.service; package com.tiesheng.message.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.tiesheng.web.mapper.CoreLogMessageMapper;
import com.tiesheng.web.pojos.dao.CoreLogMessage;
import com.tiesheng.message.pojos.MessageReqResp;
import com.tiesheng.message.pojos.UserChannel; import com.tiesheng.message.pojos.UserChannel;
import com.tiesheng.message.service.TieshengMessageSender; import com.tiesheng.util.pojos.ApiResp;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -20,25 +16,10 @@ import java.util.Objects;
* @author hao * @author hao
*/ */
@Service @Service
public class CoreMessageService { public class TsMessageService {
@Autowired @Autowired
List<TieshengMessageSender> messageSenderList; List<TsMessageSender> messageSenderList;
@Autowired
CoreLogMessageMapper coreLogMessageMapper;
/**
* 插入数据
*
* @param reqResp
*/
private void insertMessageLog(MessageReqResp reqResp) {
if (reqResp == null) {
return;
}
coreLogMessageMapper.insert(BeanUtil.copyProperties(reqResp, CoreLogMessage.class));
}
/** /**
* 发送所有消息 * 发送所有消息
@@ -47,10 +28,10 @@ public class CoreMessageService {
* @param body * @param body
*/ */
public void all(List<String> userIds, JSONObject body) { public void all(List<String> userIds, JSONObject body) {
messageSenderList.stream().filter(TieshengMessageSender::support) messageSenderList.stream().filter(TsMessageSender::support)
.forEach(sender -> { .forEach(sender -> {
for (String user : userIds) { for (String user : userIds) {
insertMessageLog(sender.send(user, body)); sender.send(user, body);
} }
}); });
} }
@@ -68,9 +49,9 @@ public class CoreMessageService {
return; return;
} }
messageSenderList.stream().filter(TieshengMessageSender::support) messageSenderList.stream().filter(TsMessageSender::support)
.forEach(sender -> channels.stream().filter(it -> Objects.equals(it.getChannel(), sender.getChannel())) .forEach(sender -> channels.stream().filter(it -> Objects.equals(it.getChannel(), sender.getChannel()))
.forEach(it -> insertMessageLog(sender.send(it.getUser(), body)))); .forEach(it -> sender.send(it.getUser(), body)));
} }
@@ -80,20 +61,18 @@ public class CoreMessageService {
* @param userChannel * @param userChannel
* @param body * @param body
*/ */
public MessageReqResp send(UserChannel userChannel, JSONObject body) { public ApiResp<String> send(UserChannel userChannel, JSONObject body) {
if (StrUtil.isEmpty(userChannel.getUser()) || StrUtil.isEmpty(userChannel.getChannel())) { if (StrUtil.isEmpty(userChannel.getUser()) || StrUtil.isEmpty(userChannel.getChannel())) {
return null; return ApiResp.resp130("消息对象或消息通道不存在");
} }
TieshengMessageSender messageSender = CollUtil.findOne(messageSenderList, TsMessageSender messageSender = CollUtil.findOne(messageSenderList,
sender -> Objects.equals(sender.getChannel(), userChannel.getChannel()) && sender.support()); sender -> Objects.equals(sender.getChannel(), userChannel.getChannel()) && sender.support());
if (messageSender != null) { if (messageSender != null) {
MessageReqResp reqResp = messageSender.send(userChannel.getUser(), body); return messageSender.send(userChannel.getUser(), body);
insertMessageLog(reqResp);
return reqResp;
} }
return null; return ApiResp.resp130("消息未成功发送");
} }
} }

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-platform</artifactId> <artifactId>springboot-platform</artifactId>

View File

@@ -1,21 +1,25 @@
package com.tiesheng.platform.config.ding; package com.tiesheng.platform.config.ding;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.LogFactory; import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.tiesheng.platform.config.ding.bean.*; import com.tiesheng.platform.config.ding.bean.*;
import com.tiesheng.util.TimedCacheHelper;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.service.TsCacheService;
import com.tiesheng.util.service.http.OkHttpUtil;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@@ -57,28 +61,36 @@ public class PlatformDingConfig {
* @return * @return
*/ */
public <T> DingResponse<T> doRequest(String service, String url, JSONObject body, TypeReference<DingResponse<T>> typeReference) { public <T> DingResponse<T> doRequest(String service, String url, JSONObject body, TypeReference<DingResponse<T>> typeReference) {
String accessToken = getAccessToken(service);
if (StrUtil.contains(url, "?")) { if (StrUtil.contains(url, "?")) {
url = url + "&access_token=" + getAccessToken(service); url = url + "&access_token=" + accessToken;
} else { } else {
url = url + "?access_token=" + getAccessToken(service); url = url + "?access_token=" + accessToken;
} }
HttpResponse execute; Request request;
if (body == null) { if (body == null) {
execute = HttpUtil.createGet(url).execute(); request = OkHttpUtil.ofGet(url);
} else { } else {
execute = HttpUtil.createPost(url).body(body.toString()).execute(); request = OkHttpUtil.ofPost(url, body);
} }
request = request.newBuilder().header("x-acs-dingtalk-access-token", accessToken).build();
if (execute.isOk()) { try {
DingResponse<T> bean = JSONUtil.toBean(execute.body(), typeReference, true); Response response = OkHttpUtil.ofHttpClient().build().newCall(request).execute();
if (!bean.isOk()) { if (response.isSuccessful() && response.body() != null) {
LogFactory.get().info(bean.getErrmsg()); String rawBody = response.body().string();
DingResponse<T> bean = JSON.parseObject(rawBody, typeReference);
bean.setRawBody(rawBody);
return bean;
} else {
LogFactory.get().info(response.body().string());
} }
return bean; } catch (Exception e) {
LogFactory.get().error(e);
} }
return null; return DingResponse.ofError();
} }
@@ -92,9 +104,9 @@ public class PlatformDingConfig {
* @return accessToken * @return accessToken
* @see <a href="https://open.dingtalk.com/document/orgapp-server/obtain-orgapp-token" /> * @see <a href="https://open.dingtalk.com/document/orgapp-server/obtain-orgapp-token" />
*/ */
private String getAccessToken(String service) { public synchronized String getAccessToken(String service) {
DingConfigBean dingConfigBean = getConfigBean(service); DingConfigBean dingConfigBean = getConfigBean(service);
String accessToken = TimedCacheHelper.getTimedCache().get(CACHE_ACCESS_TOKEN + dingConfigBean.getAppKey(), false); String accessToken = TsCacheService.of().get(CACHE_ACCESS_TOKEN + dingConfigBean.getAppKey(), -1);
if (!StrUtil.isEmpty(accessToken)) { if (!StrUtil.isEmpty(accessToken)) {
return accessToken; return accessToken;
} }
@@ -103,10 +115,11 @@ public class PlatformDingConfig {
Map<String, Object> query = new HashMap<>(10); Map<String, Object> query = new HashMap<>(10);
query.put("appkey", dingConfigBean.getAppKey()); query.put("appkey", dingConfigBean.getAppKey());
query.put("appsecret", dingConfigBean.getAppSecret()); query.put("appsecret", dingConfigBean.getAppSecret());
String response = HttpUtil.get("https://oapi.dingtalk.com/gettoken", query); String response = OkHttpUtil.get("https://oapi.dingtalk.com/gettoken", query);
JSONObject respJson = JSONUtil.parseObj(response); JSONObject respJson = JSON.parseObject(response);
accessToken = respJson.getStr("access_token"); accessToken = respJson.getString("access_token");
TimedCacheHelper.getTimedCache().put(CACHE_ACCESS_TOKEN + dingConfigBean.getAppKey(), accessToken, respJson.getLong("expires_in")); TsCacheService.of().put(CACHE_ACCESS_TOKEN + dingConfigBean.getAppKey(),
accessToken, respJson.getLong("expires_in"));
return accessToken; return accessToken;
} }
@@ -120,7 +133,7 @@ public class PlatformDingConfig {
*/ */
private String getJsapiTicket(String service) { private String getJsapiTicket(String service) {
DingConfigBean dingConfigBean = getConfigBean(service); DingConfigBean dingConfigBean = getConfigBean(service);
String jsapiTicket = TimedCacheHelper.getTimedCache().get(CACHE_JSAPI_TICKET + dingConfigBean.getAppKey(), false); String jsapiTicket = TsCacheService.of().get(CACHE_JSAPI_TICKET + dingConfigBean.getAppKey(), -1);
if (StrUtil.isEmpty(jsapiTicket)) { if (StrUtil.isEmpty(jsapiTicket)) {
DingResponse<String> respJson = doRequest(service, "https://oapi.dingtalk.com/get_jsapi_ticket", null, DingResponse<String> respJson = doRequest(service, "https://oapi.dingtalk.com/get_jsapi_ticket", null,
new TypeReference<DingResponse<String>>() { new TypeReference<DingResponse<String>>() {
@@ -128,8 +141,9 @@ public class PlatformDingConfig {
if (!respJson.isOk()) { if (!respJson.isOk()) {
throw new ApiException(respJson.getErrmsg()); throw new ApiException(respJson.getErrmsg());
} }
jsapiTicket = respJson.getTicket();
TimedCacheHelper.getTimedCache().put(CACHE_JSAPI_TICKET + dingConfigBean.getAppKey(), jsapiTicket = respJson.getRawValue("ticket", String.class);
TsCacheService.of().put(CACHE_JSAPI_TICKET + dingConfigBean.getAppKey(),
jsapiTicket, respJson.getExpiresIn()); jsapiTicket, respJson.getExpiresIn());
} }
return jsapiTicket; return jsapiTicket;
@@ -160,8 +174,12 @@ public class PlatformDingConfig {
* @see <a href="https://open.dingtalk.com/document/isvapp-server/obtain-the-userid-of-a-user-by-using-the-log-free"></a> * @see <a href="https://open.dingtalk.com/document/isvapp-server/obtain-the-userid-of-a-user-by-using-the-log-free"></a>
*/ */
public DingUserSimple getUserIdByCode(String service, String code) { public DingUserSimple getUserIdByCode(String service, String code) {
JSONObject object = new JSONObject();
object.put("code", code);
DingResponse<DingUserSimple> resp = doRequest(service, "https://oapi.dingtalk.com/topapi/v2/user/getuserinfo", DingResponse<DingUserSimple> resp = doRequest(service, "https://oapi.dingtalk.com/topapi/v2/user/getuserinfo",
JSONUtil.createObj().putOpt("code", code), new TypeReference<DingResponse<DingUserSimple>>() { object, new TypeReference<DingResponse<DingUserSimple>>() {
}); });
return resp.getResult(); return resp.getResult();
} }
@@ -178,9 +196,13 @@ public class PlatformDingConfig {
public DingUserInfo topapiV2UserGet(String service, String ddUserId) { public DingUserInfo topapiV2UserGet(String service, String ddUserId) {
DingConfigBean dingConfigBean = getConfigBean(service); DingConfigBean dingConfigBean = getConfigBean(service);
JSONObject object = new JSONObject();
object.put("userid", ddUserId);
DingUserInfo userInfo = doRequest(service, "https://oapi.dingtalk.com/topapi/v2/user/get", DingUserInfo userInfo = doRequest(service, "https://oapi.dingtalk.com/topapi/v2/user/get",
JSONUtil.createObj().putOpt("userid", ddUserId), new TypeReference<DingResponse<DingUserInfo>>() { object, new TypeReference<DingResponse<DingUserInfo>>() {
}).getResult(); }).getResult();
// 设置一下job_number // 设置一下job_number
userInfo.setJobNumber(userInfo.getJobNumber()); userInfo.setJobNumber(userInfo.getJobNumber());
userInfo.setAppId(dingConfigBean.getAppKey()); userInfo.setAppId(dingConfigBean.getAppKey());
@@ -200,8 +222,11 @@ public class PlatformDingConfig {
return new ArrayList<>(); return new ArrayList<>();
} }
JSONObject object = new JSONObject();
object.put("dept_id", deptId);
return doRequest(service, "https://oapi.dingtalk.com/topapi/v2/department/listsub", return doRequest(service, "https://oapi.dingtalk.com/topapi/v2/department/listsub",
JSONUtil.createObj().putOpt("dept_id", deptId), new TypeReference<DingResponse<List<DingDeptVo>>>() { object, new TypeReference<DingResponse<List<DingDeptVo>>>() {
}).getResult(); }).getResult();
} }
@@ -218,9 +243,13 @@ public class PlatformDingConfig {
return DingUserListVo.fail(); return DingUserListVo.fail();
} }
JSONObject object = new JSONObject();
object.put("dept_id", deptId);
object.put("cursor", cursor);
object.put("size", 100);
return doRequest(service, "https://oapi.dingtalk.com/topapi/v2/user/list", return doRequest(service, "https://oapi.dingtalk.com/topapi/v2/user/list",
JSONUtil.createObj().putOpt("dept_id", deptId).putOpt("cursor", cursor).putOpt("size", 100), object, new TypeReference<DingResponse<DingUserListVo>>() {
new TypeReference<DingResponse<DingUserListVo>>() {
}).getResult(); }).getResult();
} }
@@ -263,10 +292,12 @@ public class PlatformDingConfig {
* 发送工作通知 * 发送工作通知
* *
* @param service * @param service
* @param content * @param title
* @param markdown
* @param actionUrl * @param actionUrl
* @param userIds * @param userIds
* @return * @return
* @see <a href='https://open.dingtalk.com/document/orgapp/asynchronous-sending-of-enterprise-session-messages' </a>
*/ */
public DingResponse<String> messageNotification(String service, String title, String markdown, String actionUrl, List<String> userIds) { public DingResponse<String> messageNotification(String service, String title, String markdown, String actionUrl, List<String> userIds) {
@@ -276,20 +307,29 @@ public class PlatformDingConfig {
DingConfigBean configBean = getConfigBean(service); DingConfigBean configBean = getConfigBean(service);
JSONObject actionCard = new JSONObject();
actionCard.set("title", title);
actionCard.set("markdown", markdown);
actionCard.set("single_title", "点击查看");
actionCard.set("single_url", actionUrl);
JSONObject msg = new JSONObject(); JSONObject msg = new JSONObject();
msg.set("msgtype", "action_card"); if (StrUtil.isEmpty(actionUrl)) {
msg.set("action_card", actionCard); JSONObject markdownObj = new JSONObject();
markdownObj.put("title", title);
markdownObj.put("text", markdown);
msg.put("msgtype", "markdown");
msg.put("markdown", markdownObj);
} else {
JSONObject actionCard = new JSONObject();
actionCard.put("title", title);
actionCard.put("markdown", markdown);
actionCard.put("single_title", "点击查看");
actionCard.put("single_url", actionUrl);
msg.put("msgtype", "action_card");
msg.put("action_card", actionCard);
}
JSONObject body = new JSONObject(); JSONObject body = new JSONObject();
body.putOpt("agent_id", configBean.getAgentId()); body.put("agent_id", configBean.getAgentId());
body.putOpt("userid_list", CollUtil.join(userIds, ",")); body.put("userid_list", CollUtil.join(userIds, ","));
body.putOpt("msg", msg); body.put("msg", msg);
return doRequest(service, "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2", body, return doRequest(service, "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2", body,
new TypeReference<DingResponse<String>>() { new TypeReference<DingResponse<String>>() {
}); });

View File

@@ -1,14 +1,17 @@
package com.tiesheng.platform.config.ding.bean; package com.tiesheng.platform.config.ding.bean;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONPath;
import java.util.Objects; import java.util.Objects;
public class DingResponse<T> { public class DingResponse<T> {
private String errcode; private String errcode;
private String errmsg; private String errmsg;
private String ticket;
private Long expiresIn; private Long expiresIn;
private T result; private T result;
private String rawBody;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// 逻辑方法 // 逻辑方法
@@ -18,6 +21,31 @@ public class DingResponse<T> {
return Objects.equals(errcode, "0"); return Objects.equals(errcode, "0");
} }
/**
* 构造一个异常对象
*
* @return
*/
public static <T> DingResponse<T> ofError() {
DingResponse<T> response = new DingResponse<>();
response.setErrcode("-1");
response.setErrmsg("网络访问异常。");
return response;
}
/**
* 从原始数据中获取值
*
* @param path
* @return
*/
public <E> E getRawValue(String path, Class<E> tClass) {
JSONPath jsonPath = JSONPath.compile(path);
return jsonPath.eval(JSON.parseObject(getRawBody()), tClass);
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// setter\getter // setter\getter
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@@ -46,14 +74,6 @@ public class DingResponse<T> {
this.result = result; this.result = result;
} }
public String getTicket() {
return ticket;
}
public void setTicket(String ticket) {
this.ticket = ticket;
}
public Long getExpiresIn() { public Long getExpiresIn() {
return expiresIn; return expiresIn;
} }
@@ -61,4 +81,12 @@ public class DingResponse<T> {
public void setExpiresIn(Long expiresIn) { public void setExpiresIn(Long expiresIn) {
this.expiresIn = expiresIn; this.expiresIn = expiresIn;
} }
public String getRawBody() {
return rawBody;
}
public void setRawBody(String rawBody) {
this.rawBody = rawBody;
}
} }

View File

@@ -2,12 +2,11 @@ package com.tiesheng.platform.config.wxmini;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.http.HttpRequest; import com.alibaba.fastjson.JSON;
import cn.hutool.http.HttpResponse; import com.alibaba.fastjson.JSONObject;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tiesheng.platform.config.wxmp.bean.WxConfigBean; import com.tiesheng.platform.config.wxmp.bean.WxConfigBean;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.service.http.OkHttpUtil;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -51,13 +50,14 @@ public class PlatformWxminiConfig {
*/ */
public String jscode2session(String service, String code) { public String jscode2session(String service, String code) {
WxConfigBean configBean = getConfigBean(service); WxConfigBean configBean = getConfigBean(service);
try (HttpResponse response = HttpRequest.get("https://api.weixin.qq.com/sns/jscode2session"
String body = OkHttpUtil.get("https://api.weixin.qq.com/sns/jscode2session"
+ "?appid=" + configBean.getAppId() + "?appid=" + configBean.getAppId()
+ "&secret=" + configBean.getAppSecret() + "&secret=" + configBean.getAppSecret()
+ "&js_code=" + code + "&grant_type=authorization_code").execute()) { + "&js_code=" + code + "&grant_type=authorization_code");
JSONObject object = JSONUtil.parseObj(response.body());
return object.getStr("openid"); JSONObject object = JSON.parseObject(body);
} return object.getString("openid");
} }
/** /**
@@ -67,12 +67,13 @@ public class PlatformWxminiConfig {
*/ */
public String getAccessToken(String service) { public String getAccessToken(String service) {
WxConfigBean configBean = getConfigBean(service); WxConfigBean configBean = getConfigBean(service);
try (HttpResponse response = HttpRequest.get("https://api.weixin.qq.com/cgi-bin/token"
String body = OkHttpUtil.get("https://api.weixin.qq.com/cgi-bin/token"
+ "?grant_type=client_credential&appid=" + configBean.getAppId() + "?grant_type=client_credential&appid=" + configBean.getAppId()
+ "&secret=" + configBean.getAppSecret()).execute()) { + "&secret=" + configBean.getAppSecret());
JSONObject object = JSONUtil.parseObj(response.body());
return object.getStr("access_token"); JSONObject object = JSON.parseObject(body);
} return object.getString("access_token");
} }

View File

@@ -4,16 +4,16 @@ package com.tiesheng.platform.config.wxmp;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.LogFactory; import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tiesheng.platform.config.wxmp.bean.WxConfigBean; import com.tiesheng.platform.config.wxmp.bean.WxConfigBean;
import com.tiesheng.platform.config.wxmp.bean.WxJsapiSignature; import com.tiesheng.platform.config.wxmp.bean.WxJsapiSignature;
import com.tiesheng.platform.config.wxmp.bean.WxOAuth2AccessToken; import com.tiesheng.platform.config.wxmp.bean.WxOAuth2AccessToken;
import com.tiesheng.platform.config.wxmp.bean.WxUserInfo; import com.tiesheng.platform.config.wxmp.bean.WxUserInfo;
import com.tiesheng.util.TimedCacheHelper;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.service.TsCacheService;
import com.tiesheng.util.service.http.OkHttpUtil;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -63,17 +63,17 @@ public class PlatformWxmpConfig {
*/ */
private String getAccessToken(String service) { private String getAccessToken(String service) {
WxConfigBean configBean = getConfigBean(service); WxConfigBean configBean = getConfigBean(service);
String accessToken = TimedCacheHelper.getTimedCache().get(CACHE_ACCESS_TOKEN + configBean.getAppId(), false); String accessToken = TsCacheService.of().get(CACHE_ACCESS_TOKEN + configBean.getAppId(), -1);
if (StrUtil.isEmpty(accessToken)) { if (StrUtil.isEmpty(accessToken)) {
Map<String, Object> query = new HashMap<>(10); Map<String, Object> query = new HashMap<>(10);
query.put("grant_type", "client_credential"); query.put("grant_type", "client_credential");
query.put("appid", configBean.getAppId()); query.put("appid", configBean.getAppId());
query.put("secret", configBean.getAppSecret()); query.put("secret", configBean.getAppSecret());
String response = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", query); String response = OkHttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", query);
LogFactory.get().info("getAccessToken: " + response); JSONObject respJson = JSON.parseObject(response);
JSONObject respJson = JSONUtil.parseObj(response); accessToken = respJson.getString("access_token");
accessToken = respJson.getStr("access_token"); TsCacheService.of().put(CACHE_ACCESS_TOKEN + configBean.getAppId(), accessToken,
TimedCacheHelper.getTimedCache().put(CACHE_ACCESS_TOKEN + configBean.getAppId(), accessToken, respJson.getLong("expires_in")); respJson.getLong("expires_in"));
} }
return accessToken; return accessToken;
} }
@@ -86,16 +86,17 @@ public class PlatformWxmpConfig {
*/ */
private String getJsapiTicket(String service) { private String getJsapiTicket(String service) {
WxConfigBean configBean = getConfigBean(service); WxConfigBean configBean = getConfigBean(service);
String jsapiTicket = TimedCacheHelper.getTimedCache().get(CACHE_JSAPI_TICKET + configBean.getAppId(), false); String jsapiTicket = TsCacheService.of().get(CACHE_JSAPI_TICKET + configBean.getAppId(), -1);
if (StrUtil.isEmpty(jsapiTicket)) { if (StrUtil.isEmpty(jsapiTicket)) {
Map<String, Object> query = new HashMap<>(10); Map<String, Object> query = new HashMap<>(10);
query.put("access_token", getAccessToken(service)); query.put("access_token", getAccessToken(service));
query.put("type", "jsapi"); query.put("type", "jsapi");
String response = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket", query); String response = OkHttpUtil.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket", query);
LogFactory.get().info("getJsapiTicket: " + response); LogFactory.get().info("getJsapiTicket: " + response);
JSONObject respJson = JSONUtil.parseObj(response); JSONObject respJson = JSON.parseObject(response);
jsapiTicket = respJson.getStr("ticket"); jsapiTicket = respJson.getString("ticket");
TimedCacheHelper.getTimedCache().put(CACHE_JSAPI_TICKET + configBean.getAppId(), jsapiTicket, respJson.getLong("expires_in")); TsCacheService.of().put(CACHE_JSAPI_TICKET + configBean.getAppId(), jsapiTicket,
respJson.getLong("expires_in"));
} }
return jsapiTicket; return jsapiTicket;
} }
@@ -155,7 +156,7 @@ public class PlatformWxmpConfig {
public void mediaPicGet(String service, String mediaId, String filePath) { public void mediaPicGet(String service, String mediaId, String filePath) {
String fileUrl = String.format("https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s", String fileUrl = String.format("https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s",
getAccessToken(service), mediaId); getAccessToken(service), mediaId);
HttpUtil.downloadFile(fileUrl, filePath); OkHttpUtil.downloadFile(fileUrl, filePath);
} }

View File

@@ -1,8 +1,8 @@
package com.tiesheng.platform.config.wxmp.bean; package com.tiesheng.platform.config.wxmp.bean;
import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSONObject;
import cn.hutool.json.JSONUtil; import com.tiesheng.util.service.http.OkHttpUtil;
/** /**
* @author hao * @author hao
@@ -13,13 +13,13 @@ public class WxOAuth2AccessToken {
private String openid; private String openid;
public static WxOAuth2AccessToken create(String appId, String secret, String code) { public static WxOAuth2AccessToken create(String appId, String secret, String code) {
String response = HttpUtil.get("https://api.weixin.qq.com/sns/oauth2/access_token" + String response = OkHttpUtil.get("https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=" + appId + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code"); "?appid=" + appId + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code");
JSONObject respJson = JSONUtil.parseObj(response); JSONObject respJson = JSON.parseObject(response);
WxOAuth2AccessToken oAuth2AccessToken = new WxOAuth2AccessToken(); WxOAuth2AccessToken oAuth2AccessToken = new WxOAuth2AccessToken();
oAuth2AccessToken.setOpenid(respJson.getStr("openid")); oAuth2AccessToken.setOpenid(respJson.getString("openid"));
oAuth2AccessToken.setAccessToken(respJson.getStr("access_token")); oAuth2AccessToken.setAccessToken(respJson.getString("access_token"));
return oAuth2AccessToken; return oAuth2AccessToken;
} }

View File

@@ -1,7 +1,7 @@
package com.tiesheng.platform.config.wxmp.bean; package com.tiesheng.platform.config.wxmp.bean;
import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON;
import cn.hutool.json.JSONUtil; import com.tiesheng.util.service.http.OkHttpUtil;
/** /**
* @author hao * @author hao
@@ -30,10 +30,10 @@ public class WxUserInfo {
* @return * @return
*/ */
public static WxUserInfo create(WxOAuth2AccessToken oAuth2AccessToken) { public static WxUserInfo create(WxOAuth2AccessToken oAuth2AccessToken) {
String s = HttpUtil.get("https://api.weixin.qq.com/sns/userinfo" String s = OkHttpUtil.get("https://api.weixin.qq.com/sns/userinfo"
+ "?access_token=" + oAuth2AccessToken.getAccessToken() + "?access_token=" + oAuth2AccessToken.getAccessToken()
+ "&openid=" + oAuth2AccessToken.getOpenid() + "&lang=zh_CN"); + "&openid=" + oAuth2AccessToken.getOpenid() + "&lang=zh_CN");
return JSONUtil.toBean(s, WxUserInfo.class); return JSON.parseObject(s, WxUserInfo.class);
} }

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-poi</artifactId> <artifactId>springboot-poi</artifactId>

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-role</artifactId> <artifactId>springboot-role</artifactId>
@@ -22,6 +22,13 @@
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-util</artifactId> <artifactId>springboot-util</artifactId>
</dependency> </dependency>
<!-- aspect -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -0,0 +1,104 @@
package com.tiesheng.role.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiesheng.role.pojos.dao.CoreRoleAuthority;
import com.tiesheng.role.pojos.dao.CoreRoleServer;
import com.tiesheng.role.pojos.dto.OwnerMenuDTO;
import com.tiesheng.role.pojos.dto.OwnerPointDTO;
import com.tiesheng.role.pojos.vo.ServiceMenuVO;
import com.tiesheng.role.service.CoreRoleService;
import com.tiesheng.util.pojos.ApiResp;
import com.tiesheng.util.pojos.TokenBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/comm/role")
public class CommRoleController {
@Resource
CoreRoleService coreRoleService;
/**
* 获取自己拥有的服务
*
* @return
*/
@GetMapping("/owner/server")
public ApiResp<List<CoreRoleServer>> ownerServer(TokenBean tokenBean) {
List<CoreRoleAuthority> allOwnerMenus = coreRoleService.getOwnerAuthorityLeafList(tokenBean.getId(), tokenBean.getRoleId());
List<String> list = allOwnerMenus.stream().map(CoreRoleAuthority::getService).collect(Collectors.toList());
List<CoreRoleServer> roleServerList = new ArrayList<>();
if (CollUtil.isNotEmpty(list)) {
roleServerList = coreRoleService.getServerMapper().selectList(new QueryWrapper<CoreRoleServer>()
.in("id", list)
.eq(CoreRoleServer.IS_DELETED, 0)
.eq("is_open", 1)
);
}
return ApiResp.respOK(roleServerList);
}
/**
* 获取自己拥有的菜单
*
* @return
*/
@GetMapping("/owner/menu")
public ApiResp<List<ServiceMenuVO>> ownerMenu(@Valid OwnerMenuDTO dto, TokenBean tokenBean) {
List<ServiceMenuVO> ownerMenus = coreRoleService.getOwnerMenus(tokenBean.getId(), tokenBean.getRoleId(), dto);
return ApiResp.respOK(ownerMenus);
}
/**
* 获取自己拥有的功能点
*
* @return
*/
@GetMapping("/owner/point")
public ApiResp<List<CoreRoleAuthority>> ownerPoint(@Valid OwnerPointDTO dto, TokenBean tokenBean) {
List<CoreRoleAuthority> allOwnerMenus = coreRoleService.getOwnerAuthorityLeafList(tokenBean.getId(), tokenBean.getRoleId());
String parentId;
if (StrUtil.isNotEmpty(dto.getParentNo())) {
CoreRoleAuthority selected = coreRoleService.getAuthorityMapper().selectOne(new QueryWrapper<CoreRoleAuthority>()
.eq("no", dto.getParentNo())
.eq("is_deleted", 0)
.last("limit 1")
);
parentId = selected == null ? "" : selected.getId();
} else {
parentId = "";
}
List<CoreRoleAuthority> collect = allOwnerMenus.stream()
.filter(it -> Objects.equals(it.getType(), "point"))
.filter(it -> it.getService().equals(dto.getService()))
.filter(it -> it.getPlatform().equals(dto.getPlatform()))
.filter(it -> {
if (StrUtil.isNotEmpty(parentId)) {
return parentId.equals(it.getParent());
}
return true;
}).collect(Collectors.toList());
return ApiResp.respOK(collect);
}
}

View File

@@ -1,317 +0,0 @@
package com.tiesheng.role.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tiesheng.role.pojos.dao.*;
import com.tiesheng.role.pojos.dto.GroupRxUpdateDTO;
import com.tiesheng.role.pojos.dto.MenuListDTO;
import com.tiesheng.role.pojos.dto.OwnerMenuDTO;
import com.tiesheng.role.pojos.dto.OwnerPointDTO;
import com.tiesheng.role.pojos.vo.GroupTypeDTO;
import com.tiesheng.role.pojos.vo.RoleUserPageVO;
import com.tiesheng.role.pojos.vo.ServiceMenuVO;
import com.tiesheng.role.service.CoreRoleService;
import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.pojos.ApiResp;
import com.tiesheng.util.pojos.IdDTO;
import com.tiesheng.util.pojos.PageDTO;
import com.tiesheng.util.pojos.TokenBean;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/role")
public class RoleController {
@Resource
CoreRoleService coreRoleService;
/**
* 角色列表
*
* @return
*/
@GetMapping("/group/list")
public ApiResp<List<CoreRoleGroup>> groupList(@Valid GroupTypeDTO dto) {
return ApiResp.respOK(coreRoleService.list(
new QueryWrapper<CoreRoleGroup>()
.eq("is_deleted", 0)
.eq("type", dto.getType())
.orderByAsc("sort")
));
}
/**
* 角色编辑
*
* @param roleGroup
* @return
*/
@PostMapping("/group/update")
public ApiResp<String> groupUpdate(@RequestBody CoreRoleGroup roleGroup) {
if (StrUtil.isNotEmpty(roleGroup.getId())) {
roleGroup.setType(null);
roleGroup.setIsSystem(null);
}
coreRoleService.saveOrUpdate(roleGroup);
return ApiResp.respOK("");
}
/**
* 角色-删除
*
* @return
*/
@PostMapping("/group/deleted")
public ApiResp<String> groupDeleted(@RequestBody @Valid IdDTO dto) {
CoreRoleGroup byId = coreRoleService.getById(dto.getId());
if (byId == null || byId.getIsDeleted() != 0) {
throw new ApiException("角色不存在或已删除");
}
if (byId.getIsSystem() == 1) {
throw new ApiException(StrUtil.format("该{}无法删除",
Objects.equals(byId.getType(), "role") ? "角色" : "职位"));
}
CoreRoleGroup coreServiceMenu = new CoreRoleGroup();
coreServiceMenu.setId(dto.getId());
coreServiceMenu.setIsDeleted(1);
coreRoleService.updateById(coreServiceMenu);
return ApiResp.respOK("");
}
/**
* 获取角色的权限
*
* @return
*/
@GetMapping("/group/rx/list")
public ApiResp<List<CoreRoleGroupRx>> groupRxList(@Valid IdDTO dto) {
List<CoreRoleGroupRx> list = coreRoleService.getGroupRxMapper().selectList(new QueryWrapper<CoreRoleGroupRx>()
.eq(CoreRoleGroupRx.IS_DELETED, 0)
.eq("group_id", dto.getId())
);
return ApiResp.respOK(list);
}
/**
* 橘色的权限编辑
*
* @return
*/
@PostMapping("/group/rx/update")
public ApiResp<String> groupRxUpdate(@RequestBody @Valid GroupRxUpdateDTO dto) {
coreRoleService.updateGroupRx(dto);
return ApiResp.respOK("");
}
/**
* 授权列表
*
* @return
*/
@GetMapping("/user/page")
public ApiResp<List<RoleUserPageVO>> userPage(PageDTO dto) {
QueryWrapper<CoreRoleUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("cru.is_deleted", 0);
dto.likeColumns(queryWrapper, "cru.ext1", "cru.ext2", "cru.ext3");
queryWrapper.orderByAsc("cru.user_id");
Page<RoleUserPageVO> page = dto.pageObj();
coreRoleService.getUserMapper().page(page, queryWrapper);
return ApiResp.respOK(page.getRecords(), page.getTotal());
}
/**
* 授权调整
*
* @return
*/
@PostMapping("/user/update")
public ApiResp<String> userUpdate(@RequestBody CoreRoleUser roleUser) {
if (StrUtil.isNotEmpty(roleUser.getId())) {
coreRoleService.getUserMapper().updateById(roleUser);
} else {
coreRoleService.getUserMapper().insert(roleUser);
}
return ApiResp.respOK("");
}
/**
* 授权-删除
*
* @return
*/
@PostMapping("/user/deleted")
public ApiResp<String> userDeleted(@RequestBody @Valid IdDTO dto) {
CoreRoleUser coreRoleUser = new CoreRoleUser();
coreRoleUser.setId(dto.getId());
coreRoleUser.setIsDeleted(1);
coreRoleService.getUserMapper().updateById(coreRoleUser);
return ApiResp.respOK("");
}
/**
* 获取服务列表
*
* @return
*/
@GetMapping("/server/list")
public ApiResp<List<CoreRoleServer>> list() {
return ApiResp.respOK(coreRoleService.getServerMapper().selectList(new QueryWrapper<CoreRoleServer>()
.eq(CoreRoleServer.IS_DELETED, 0)
.eq("is_open", 1)
));
}
/**
* 修改服务
*
* @param coreService
* @return
*/
@PostMapping("/server/update")
public ApiResp<String> update(@RequestBody CoreRoleServer coreService) {
if (StrUtil.isNotEmpty(coreService.getId())) {
coreRoleService.getServerMapper().updateById(coreService);
} else {
coreRoleService.getServerMapper().insert(coreService);
}
return ApiResp.respOK("");
}
/**
* 权限-列出
*
* @return
*/
@GetMapping("/authority/list")
public ApiResp<List<ServiceMenuVO>> menuList(@Valid MenuListDTO dto) {
List<CoreRoleAuthority> list = coreRoleService.getAuthorityMapper().selectList(new QueryWrapper<CoreRoleAuthority>()
.eq(CoreRoleAuthority.IS_DELETED, 0)
.eq("service", dto.getService())
.eq(StrUtil.isNotEmpty(dto.getPlatform()), "platform", dto.getPlatform())
.orderByAsc("sort")
);
List<ServiceMenuVO> collect = coreRoleService.menuChildrenWrap(list, null);
return ApiResp.respOK(collect);
}
/**
* 权限-编辑
*
* @return
*/
@PostMapping("/authority/update")
public ApiResp<String> menuUpdate(@RequestBody CoreRoleAuthority serviceMenu) {
serviceMenu.setParent(StrUtil.emptyToDefault(serviceMenu.getParent(), null));
if (StrUtil.isEmpty(serviceMenu.getId())) {
coreRoleService.getAuthorityMapper().insert(serviceMenu);
} else {
serviceMenu.setNo(null);
coreRoleService.getAuthorityMapper().updateById(serviceMenu);
}
return ApiResp.respOK("");
}
/**
* 获取自己拥有的服务
*
* @return
*/
@GetMapping("/owner/server")
public ApiResp<List<CoreRoleServer>> ownerServer(TokenBean tokenBean) {
List<CoreRoleAuthority> allOwnerMenus = coreRoleService.getOwnerAuthorityLeafList(tokenBean.getId(), tokenBean.getRoleId());
List<String> list = allOwnerMenus.stream().map(CoreRoleAuthority::getService).collect(Collectors.toList());
List<CoreRoleServer> roleServerList = new ArrayList<>();
if (CollUtil.isNotEmpty(list)) {
roleServerList = coreRoleService.getServerMapper().selectList(new QueryWrapper<CoreRoleServer>()
.in("id", list)
.eq(CoreRoleServer.IS_DELETED, 0)
.eq("is_open", 1)
);
}
return ApiResp.respOK(roleServerList);
}
/**
* 获取自己拥有的菜单
*
* @return
*/
@GetMapping("/owner/menu")
public ApiResp<List<ServiceMenuVO>> ownerMenu(TokenBean tokenBean, @Valid OwnerMenuDTO dto) {
List<ServiceMenuVO> ownerMenus = coreRoleService.getOwnerMenus(tokenBean.getId(), tokenBean.getRoleId(), dto);
return ApiResp.respOK(ownerMenus);
}
/**
* 获取自己拥有的功能点
*
* @return
*/
@GetMapping("/owner/point")
public ApiResp<List<CoreRoleAuthority>> ownerPoint(TokenBean tokenBean, @Valid OwnerPointDTO dto) {
List<CoreRoleAuthority> allOwnerMenus = coreRoleService.getOwnerAuthorityLeafList(tokenBean.getId(), tokenBean.getRoleId());
String parentId;
if (StrUtil.isNotEmpty(dto.getParentNo())) {
CoreRoleAuthority selected = coreRoleService.getAuthorityMapper().selectOne(new QueryWrapper<CoreRoleAuthority>()
.eq("no", dto.getParentNo())
.eq("is_deleted", 0)
.last("limit 1")
);
parentId = selected == null ? "" : selected.getId();
} else {
parentId = "";
}
List<CoreRoleAuthority> collect = allOwnerMenus.stream()
.filter(it -> Objects.equals(it.getType(), "point"))
.filter(it -> it.getService().equals(dto.getService()))
.filter(it -> it.getPlatform().equals(dto.getPlatform()))
.filter(it -> {
if (StrUtil.isNotEmpty(parentId)) {
return parentId.equals(it.getParent());
}
return true;
}).collect(Collectors.toList());
return ApiResp.respOK(collect);
}
}

View File

@@ -0,0 +1,123 @@
package com.tiesheng.role.controller.role;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tiesheng.annotation.role.RoleAuthority;
import com.tiesheng.role.pojos.dao.CoreRoleGroup;
import com.tiesheng.role.pojos.dao.CoreRoleGroupRx;
import com.tiesheng.role.pojos.dto.GroupRxUpdateDTO;
import com.tiesheng.role.pojos.vo.GroupTypeDTO;
import com.tiesheng.role.service.CoreRoleService;
import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.pojos.ApiResp;
import com.tiesheng.util.pojos.IdDTO;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
@RestController
@RequestMapping("/role/group")
@RoleAuthority(value = "group", group = "role")
public class RoleGroupController {
@Resource
CoreRoleService coreRoleService;
/**
* 角色列表
*
* @return
*/
@GetMapping("/page")
public ApiResp<List<CoreRoleGroup>> groupPage(@Valid GroupTypeDTO dto) {
QueryWrapper<CoreRoleGroup> queryWrapper = new QueryWrapper<CoreRoleGroup>()
.eq("is_deleted", 0)
.eq(StrUtil.isNotEmpty(dto.getType()), "type", dto.getType())
.orderByAsc("sort");
dto.likeColumns(queryWrapper, "name");
Page<CoreRoleGroup> page = dto.pageObj();
coreRoleService.page(page, queryWrapper);
return ApiResp.respOK(page.getRecords(), page.getTotal());
}
/**
* 角色编辑
*
* @param roleGroup
* @return
*/
@PostMapping("/update")
public ApiResp<String> groupUpdate(@RequestBody CoreRoleGroup roleGroup) {
if (StrUtil.isNotEmpty(roleGroup.getId())) {
roleGroup.setType(null);
roleGroup.setIsSystem(null);
}
coreRoleService.saveOrUpdate(roleGroup);
return ApiResp.respOK("");
}
/**
* 角色-删除
*
* @return
*/
@PostMapping("/deleted")
public ApiResp<String> groupDeleted(@RequestBody @Valid IdDTO dto) {
CoreRoleGroup byId = coreRoleService.getById(dto.getId());
if (byId == null || byId.getIsDeleted() != 0) {
throw new ApiException("角色不存在或已删除");
}
if (byId.getIsSystem() == 1) {
throw new ApiException(StrUtil.format("该{}无法删除",
Objects.equals(byId.getType(), "role") ? "角色" : "职位"));
}
CoreRoleGroup coreServiceMenu = new CoreRoleGroup();
coreServiceMenu.setId(dto.getId());
coreServiceMenu.setIsDeleted(1);
coreRoleService.updateById(coreServiceMenu);
return ApiResp.respOK("");
}
/**
* 获取角色的权限
*
* @return
*/
@GetMapping("/rx/list")
public ApiResp<List<CoreRoleGroupRx>> groupRxList(@Valid IdDTO dto) {
List<CoreRoleGroupRx> list = coreRoleService.getGroupRxMapper().selectList(new QueryWrapper<CoreRoleGroupRx>()
.eq(CoreRoleGroupRx.IS_DELETED, 0)
.eq("group_id", dto.getId())
.eq("type", "bind")
);
return ApiResp.respOK(list);
}
/**
* 角色的权限编辑
*
* @return
*/
@PostMapping("/rx/update")
public ApiResp<String> groupRxUpdate(@RequestBody @Valid GroupRxUpdateDTO dto) {
coreRoleService.updateGroupRx(dto);
return ApiResp.respOK("");
}
}

View File

@@ -0,0 +1,95 @@
package com.tiesheng.role.controller.role;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiesheng.annotation.role.RoleAuthority;
import com.tiesheng.role.pojos.dao.CoreRoleAuthority;
import com.tiesheng.role.pojos.dao.CoreRoleServer;
import com.tiesheng.role.pojos.dto.MenuListDTO;
import com.tiesheng.role.pojos.vo.ServiceMenuVO;
import com.tiesheng.role.service.CoreRoleService;
import com.tiesheng.util.pojos.ApiResp;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/role")
@RoleAuthority(value = "server", group = "role")
public class RoleServerController {
@Resource
CoreRoleService coreRoleService;
/**
* 获取服务列表
*
* @return
*/
@GetMapping("/server/list")
public ApiResp<List<CoreRoleServer>> list() {
return ApiResp.respOK(coreRoleService.getServerMapper().selectList(new QueryWrapper<CoreRoleServer>()
.eq(CoreRoleServer.IS_DELETED, 0)
.eq("is_open", 1)
));
}
/**
* 修改服务
*
* @param coreService
* @return
*/
@PostMapping("/server/update")
public ApiResp<String> update(@RequestBody CoreRoleServer coreService) {
if (StrUtil.isNotEmpty(coreService.getId())) {
coreRoleService.getServerMapper().updateById(coreService);
} else {
coreRoleService.getServerMapper().insert(coreService);
}
return ApiResp.respOK("");
}
/**
* 权限-列出
*
* @return
*/
@GetMapping("/authority/list")
public ApiResp<List<ServiceMenuVO>> menuList(@Valid MenuListDTO dto) {
List<CoreRoleAuthority> list = coreRoleService.getAuthorityMapper().selectList(new QueryWrapper<CoreRoleAuthority>()
.eq(CoreRoleAuthority.IS_DELETED, 0)
.eq("service", dto.getService())
.eq(StrUtil.isNotEmpty(dto.getPlatform()), "platform", dto.getPlatform())
.orderByAsc("sort")
);
List<ServiceMenuVO> collect = coreRoleService.menuChildrenWrap(list, null);
return ApiResp.respOK(collect);
}
/**
* 权限-编辑
*
* @return
*/
@PostMapping("/authority/update")
public ApiResp<String> menuUpdate(@RequestBody CoreRoleAuthority serviceMenu) {
serviceMenu.setParent(StrUtil.emptyToDefault(serviceMenu.getParent(), null));
if (StrUtil.isEmpty(serviceMenu.getId())) {
coreRoleService.getAuthorityMapper().insert(serviceMenu);
} else {
serviceMenu.setNo(null);
coreRoleService.getAuthorityMapper().updateById(serviceMenu);
}
return ApiResp.respOK("");
}
}

View File

@@ -0,0 +1,69 @@
package com.tiesheng.role.controller.role;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tiesheng.annotation.role.RoleAuthority;
import com.tiesheng.role.pojos.dao.CoreRoleUser;
import com.tiesheng.role.pojos.vo.RoleUserPageVO;
import com.tiesheng.role.service.CoreRoleService;
import com.tiesheng.util.pojos.ApiResp;
import com.tiesheng.util.pojos.IdDTO;
import com.tiesheng.util.pojos.PageDTO;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/role/user")
@RoleAuthority(value = "user", group = "role")
public class RoleUserController {
@Resource
CoreRoleService coreRoleService;
/**
* 授权列表
*
* @return
*/
@GetMapping("/page")
public ApiResp<List<RoleUserPageVO>> userPage(PageDTO dto) {
QueryWrapper<CoreRoleUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("cru.is_deleted", 0);
dto.likeColumns(queryWrapper, "cru.ext1", "cru.ext2", "cru.ext3");
queryWrapper.orderByAsc("cru.user_id");
Page<RoleUserPageVO> page = dto.pageObj();
coreRoleService.getUserMapper().page(page, queryWrapper);
return ApiResp.respOK(page.getRecords(), page.getTotal());
}
/**
* 授权调整
*
* @return
*/
@PostMapping("/update")
public ApiResp<String> userUpdate(@RequestBody CoreRoleUser roleUser) {
coreRoleService.roleUserUpdate(roleUser);
return ApiResp.respOK("");
}
/**
* 授权-删除
*
* @return
*/
@PostMapping("/deleted")
public ApiResp<String> userDeleted(@RequestBody @Valid IdDTO dto) {
coreRoleService.roleUserDeleted(dto.getId());
return ApiResp.respOK("");
}
}

View File

@@ -3,5 +3,13 @@ package com.tiesheng.role.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tiesheng.role.pojos.dao.CoreRoleAuthority; import com.tiesheng.role.pojos.dao.CoreRoleAuthority;
import java.util.List;
public interface CoreRoleAuthorityMapper extends BaseMapper<CoreRoleAuthority> { public interface CoreRoleAuthorityMapper extends BaseMapper<CoreRoleAuthority> {
/**
* 批量插入数据
*
* @param coreRoleAuthorities
*/
void batchInsert(List<CoreRoleAuthority> coreRoleAuthorities);
} }

View File

@@ -14,4 +14,4 @@ public interface CoreRoleGroupRxMapper extends BaseMapper<CoreRoleGroupRx> {
* @return * @return
*/ */
int batchInsert(@Param("list") List<CoreRoleGroupRx> coreRoleGroupRxs); int batchInsert(@Param("list") List<CoreRoleGroupRx> coreRoleGroupRxs);
} }

View File

@@ -29,7 +29,7 @@ public interface CoreRoleUserMapper extends BaseMapper<CoreRoleUser> {
* @param userId * @param userId
* @return * @return
*/ */
List<CoreRoleAuthority> getOwnerAuthorityLeafList(@Param("userId") String userId, List<CoreRoleAuthority> getOwnerAuthorityLeafList(@Param("userId") String userId, @Param("roleId") String roleId,
@Param("roleId") String roleId); @Param("type") String type);
} }

View File

@@ -78,6 +78,24 @@ public class CoreRoleAuthority extends DaoBase {
@TableField(value = "platform") @TableField(value = "platform")
private String platform; private String platform;
/**
* 版本号
*/
@TableField(value = "version")
private String version;
/**
* 权限来源auto-自动生成input-添加
*/
@TableField(value = "`source`")
private String source;
/**
* 依赖权限
*/
@TableField(value = "deps")
private String deps;
/** /**
* 扩展1 * 扩展1
*/ */
@@ -294,6 +312,60 @@ public class CoreRoleAuthority extends DaoBase {
this.platform = platform; this.platform = platform;
} }
/**
* 获取版本号
*
* @return version - 版本号
*/
public String getVersion() {
return version;
}
/**
* 设置版本号
*
* @param version 版本号
*/
public void setVersion(String version) {
this.version = version;
}
/**
* 获取权限来源auto-自动生成input-添加
*
* @return source - 权限来源auto-自动生成input-添加
*/
public String getSource() {
return source;
}
/**
* 设置权限来源auto-自动生成input-添加
*
* @param source 权限来源auto-自动生成input-添加
*/
public void setSource(String source) {
this.source = source;
}
/**
* 获取依赖权限
*
* @return deps - 依赖权限
*/
public String getDeps() {
return deps;
}
/**
* 设置依赖权限
*
* @param deps 依赖权限
*/
public void setDeps(String deps) {
this.deps = deps;
}
/** /**
* 获取扩展1 * 获取扩展1
* *

View File

@@ -24,6 +24,12 @@ public class CoreRoleGroupRx extends DaoBase {
@TableField(value = "menu_id") @TableField(value = "menu_id")
private String menuId; private String menuId;
/**
* 关联类型dep-依赖bind-绑定
*/
@TableField(value = "`type`")
private String type;
/** /**
* 获取角色id * 获取角色id
* *
@@ -59,4 +65,22 @@ public class CoreRoleGroupRx extends DaoBase {
public void setMenuId(String menuId) { public void setMenuId(String menuId) {
this.menuId = menuId; this.menuId = menuId;
} }
/**
* 获取关联类型dep-依赖bind-绑定
*
* @return type - 关联类型dep-依赖bind-绑定
*/
public String getType() {
return type;
}
/**
* 设置关联类型dep-依赖bind-绑定
*
* @param type 关联类型dep-依赖bind-绑定
*/
public void setType(String type) {
this.type = type;
}
} }

View File

@@ -1,10 +1,9 @@
package com.tiesheng.role.pojos.vo; package com.tiesheng.role.pojos.vo;
import javax.validation.constraints.NotEmpty; import com.tiesheng.util.pojos.PageDTO;
public class GroupTypeDTO { public class GroupTypeDTO extends PageDTO {
@NotEmpty(message = "请选择一个类型")
private String type; private String type;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@@ -2,12 +2,23 @@ package com.tiesheng.role.pojos.vo;
import com.tiesheng.role.pojos.dao.CoreRoleAuthority; import com.tiesheng.role.pojos.dao.CoreRoleAuthority;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class ServiceMenuVO extends CoreRoleAuthority { public class ServiceMenuVO extends CoreRoleAuthority {
private List<ServiceMenuVO> children; private List<ServiceMenuVO> children;
/**
* 排序
*/
public ServiceMenuVO sortChildren() {
children = children.stream().sorted(Comparator.comparingInt(ServiceMenuVO::getSort))
.collect(Collectors.toList());
return this;
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// setter\getter // setter\getter
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@@ -2,42 +2,43 @@ package com.tiesheng.role.service;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiesheng.role.mapper.*; import com.tiesheng.role.mapper.*;
import com.tiesheng.role.pojos.dao.CoreRoleAuthority; import com.tiesheng.role.pojos.dao.CoreRoleAuthority;
import com.tiesheng.role.pojos.dao.CoreRoleGroup; import com.tiesheng.role.pojos.dao.CoreRoleGroup;
import com.tiesheng.role.pojos.dao.CoreRoleGroupRx; import com.tiesheng.role.pojos.dao.CoreRoleGroupRx;
import com.tiesheng.role.pojos.dao.CoreRoleUser;
import com.tiesheng.role.pojos.dto.GroupRxUpdateDTO; import com.tiesheng.role.pojos.dto.GroupRxUpdateDTO;
import com.tiesheng.role.pojos.dto.OwnerMenuDTO; import com.tiesheng.role.pojos.dto.OwnerMenuDTO;
import com.tiesheng.role.pojos.vo.ServiceMenuVO; import com.tiesheng.role.pojos.vo.ServiceMenuVO;
import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.service.TsCacheService;
import com.tiesheng.util.service.TsServiceBase; import com.tiesheng.util.service.TsServiceBase;
import com.tiesheng.util.service.role.TsAuthorityHandler;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
public class CoreRoleService extends TsServiceBase<CoreRoleGroupMapper, CoreRoleGroup> { public class CoreRoleService extends TsServiceBase<CoreRoleGroupMapper, CoreRoleGroup> {
@Resource @Resource
CoreRoleUserMapper coreRoleUserMapper; CoreRoleUserMapper coreRoleUserMapper;
@Resource @Resource
CoreRoleGroupRxMapper coreRoleGroupRxMapper; CoreRoleGroupRxMapper coreRoleGroupRxMapper;
@Resource @Resource
CoreRoleAuthorityMapper coreRoleAuthorityMapper; CoreRoleAuthorityMapper coreRoleAuthorityMapper;
@Resource @Resource
CoreRoleServerMapper coreRoleServerMapper; CoreRoleServerMapper coreRoleServerMapper;
public CoreRoleServerMapper getServerMapper() { public CoreRoleServerMapper getServerMapper() {
return coreRoleServerMapper; return coreRoleServerMapper;
} }
@@ -63,7 +64,18 @@ public class CoreRoleService extends TsServiceBase<CoreRoleGroupMapper, CoreRole
* @return * @return
*/ */
public List<ServiceMenuVO> menuChildrenWrap(List<CoreRoleAuthority> coreServiceMenuList, String parent) { public List<ServiceMenuVO> menuChildrenWrap(List<CoreRoleAuthority> coreServiceMenuList, String parent) {
List<CoreRoleAuthority> distinctList = coreServiceMenuList.stream().distinct().collect(Collectors.toList());
HashMap<String, String> keyMaps = MapUtil.newHashMap();
List<CoreRoleAuthority> distinctList = coreServiceMenuList.stream()
.filter(it -> {
if (keyMaps.containsKey(it.getId())) {
return false;
}
keyMaps.put(it.getId(), it.getId());
return true;
})
.collect(Collectors.toList());
List<ServiceMenuVO> list = new ArrayList<>(); List<ServiceMenuVO> list = new ArrayList<>();
@@ -91,16 +103,88 @@ public class CoreRoleService extends TsServiceBase<CoreRoleGroupMapper, CoreRole
coreRoleGroupRxMapper.delete(new QueryWrapper<CoreRoleGroupRx>() coreRoleGroupRxMapper.delete(new QueryWrapper<CoreRoleGroupRx>()
.eq("group_id", dto.getId()) .eq("group_id", dto.getId())
); );
if (CollUtil.isEmpty(dto.getMenuIds())) {
return;
}
CoreRoleGroup roleGroup = getById(dto.getId());
Validator.validateNotEmpty(roleGroup, "角色不存在");
List<CoreRoleAuthority> authorities = coreRoleAuthorityMapper.selectList(new QueryWrapper<CoreRoleAuthority>()
.eq(CoreRoleAuthority.IS_DELETED, 0)
.in("id", dto.getMenuIds())
);
List<CoreRoleGroupRx> list = new ArrayList<>(); List<CoreRoleGroupRx> list = new ArrayList<>();
for (String menuId : dto.getMenuIds()) { for (CoreRoleAuthority authority : authorities) {
CoreRoleGroupRx coreRoleGroupRx = new CoreRoleGroupRx(); CoreRoleGroupRx coreRoleGroupRx = new CoreRoleGroupRx();
coreRoleGroupRx.setGroupId(dto.getId()); coreRoleGroupRx.setGroupId(roleGroup.getId());
coreRoleGroupRx.setMenuId(menuId); coreRoleGroupRx.setMenuId(authority.getId());
coreRoleGroupRx.setType("bind");
list.add(coreRoleGroupRx); list.add(coreRoleGroupRx);
List<String> deps = JSON.parseArray(authority.getDeps(), String.class);
for (String dep : deps) {
CoreRoleGroupRx depRx = new CoreRoleGroupRx();
depRx.setGroupId(roleGroup.getId());
depRx.setMenuId(StrUtil.format("{}_{}", authority.getService(), dep));
depRx.setType("dep");
list.add(depRx);
}
} }
if (CollUtil.isNotEmpty(list)) { if (CollUtil.isNotEmpty(list)) {
coreRoleGroupRxMapper.batchInsert(list); coreRoleGroupRxMapper.batchInsert(list);
onRoleChange(dto.getId(), "");
}
}
/**
* 编辑、更新用户的授权
*
* @param roleUser
*/
public void roleUserUpdate(CoreRoleUser roleUser) {
if (StrUtil.isNotEmpty(roleUser.getId())) {
coreRoleUserMapper.updateById(roleUser);
} else {
coreRoleUserMapper.insert(roleUser);
}
onRoleChange("", roleUser.getUserId());
}
/**
* 删除用户授权
*
* @param recordId
*/
public void roleUserDeleted(String recordId) {
CoreRoleUser coreRoleUser = coreRoleUserMapper.selectById(recordId);
if (coreRoleUser == null || coreRoleUser.getIsDeleted() == 1) {
throw new ApiException("该授权已被删除");
}
coreRoleUser.setIsDeleted(1);
coreRoleUserMapper.updateById(coreRoleUser);
onRoleChange("", coreRoleUser.getUserId());
}
/**
* 当授权发生变化时
*/
public void onRoleChange(String roleId, String userId) {
if (StrUtil.isEmpty(roleId)) {
TsCacheService.of().keys(StrUtil.replace(TsAuthorityHandler.CACHE_AUTHORITY,
":{}", "")).forEach(key -> TsCacheService.of().remove(key));
} else {
TsCacheService.of().keys(StrUtil.format(TsAuthorityHandler.CACHE_AUTHORITY,
roleId, userId)).forEach(key -> TsCacheService.of().remove(key));
} }
} }
@@ -111,7 +195,7 @@ public class CoreRoleService extends TsServiceBase<CoreRoleGroupMapper, CoreRole
* @return * @return
*/ */
public List<CoreRoleAuthority> getOwnerAuthorityLeafList(String userId, String roleId) { public List<CoreRoleAuthority> getOwnerAuthorityLeafList(String userId, String roleId) {
return coreRoleUserMapper.getOwnerAuthorityLeafList(userId, roleId); return coreRoleUserMapper.getOwnerAuthorityLeafList(userId, roleId, "bind");
} }
@@ -152,7 +236,11 @@ public class CoreRoleService extends TsServiceBase<CoreRoleGroupMapper, CoreRole
return menuChildrenWrap(ownerAuthorityList.stream() return menuChildrenWrap(ownerAuthorityList.stream()
.filter(it -> Objects.equals(it.getType(), "group") || Objects.equals(it.getType(), "menu")) .filter(it -> Objects.equals(it.getType(), "group") || Objects.equals(it.getType(), "menu"))
.collect(Collectors.toList()), null); .filter(it -> Objects.equals(it.getService(), dto.getService()) && Objects.equals(it.getPlatform(), dto.getPlatform()))
.collect(Collectors.toList()), null)
.stream().sorted(Comparator.comparingInt(ServiceMenuVO::getSort))
.map(ServiceMenuVO::sortChildren)
.collect(Collectors.toList());
} }
} }

View File

@@ -0,0 +1,120 @@
package com.tiesheng.role.service;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiesheng.annotation.role.RoleAuthority;
import com.tiesheng.role.mapper.CoreRoleAuthorityMapper;
import com.tiesheng.role.mapper.CoreRoleUserMapper;
import com.tiesheng.role.pojos.dao.CoreRoleAuthority;
import com.tiesheng.util.config.GlobalConfig;
import com.tiesheng.util.pojos.TokenBean;
import com.tiesheng.util.service.role.TsAuthorityHandler;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class RoleAuthorityHandler implements TsAuthorityHandler {
@Resource
CoreRoleAuthorityMapper coreRoleAuthorityMapper;
@Resource
GlobalConfig globalConfig;
@Resource
CoreRoleUserMapper coreRoleUserMapper;
@Override
public void addRoleAuthority(String version, RoleAuthority menu, List<RoleAuthority> points) {
if (menu.group().length == 0) {
return;
}
String menuPlatform = StrUtil.emptyToDefault(menu.platform(), "web");
List<CoreRoleAuthority> list = new ArrayList<>();
// 分组
CoreRoleAuthority groupAuthority = new CoreRoleAuthority();
groupAuthority.setLevel(0);
for (String group : menu.group()) {
String parentId = groupAuthority.getId();
int level = groupAuthority.getLevel() + 1;
String no = group;
if (StrUtil.isNotEmpty(groupAuthority.getNo())) {
no = StrUtil.join("_", groupAuthority.getNo(), group);
}
groupAuthority = new CoreRoleAuthority();
groupAuthority.setNo(no);
groupAuthority.setName(group);
groupAuthority.setService(globalConfig.getService());
groupAuthority.setType("group");
groupAuthority.setLevel(level);
groupAuthority.setPlatform(menuPlatform);
groupAuthority.setParent(parentId);
groupAuthority.setVersion(version);
groupAuthority.setSource("auto");
groupAuthority.setId(StrUtil.join("_", groupAuthority.getService(), groupAuthority.getNo()));
list.add(groupAuthority);
}
// 菜单
CoreRoleAuthority menuAuthority = new CoreRoleAuthority();
menuAuthority.setNo(StrUtil.join("_", menu.group(), menu.value()));
menuAuthority.setName(menu.value());
menuAuthority.setService(globalConfig.getService());
menuAuthority.setType("menu");
menuAuthority.setLevel(groupAuthority.getLevel() + 1);
menuAuthority.setParent(groupAuthority.getId());
menuAuthority.setPlatform(menuPlatform);
menuAuthority.setVersion(version);
menuAuthority.setSource("auto");
menuAuthority.setDeps(JSON.toJSONString(menu.deps()));
menuAuthority.setId(StrUtil.join("_", menuAuthority.getService(), menuAuthority.getNo()));
list.add(menuAuthority);
// 功能点
for (RoleAuthority authority : points) {
CoreRoleAuthority point = new CoreRoleAuthority();
point.setNo(StrUtil.join("_", menuAuthority.getNo(), authority.value()));
point.setName(authority.value());
point.setService(globalConfig.getService());
point.setType("point");
point.setLevel(menuAuthority.getLevel() + 1);
point.setParent(menuAuthority.getId());
point.setVersion(version);
point.setSource("auto");
point.setDeps(JSON.toJSONString(authority.deps()));
point.setPlatform(StrUtil.emptyToDefault(authority.platform(), menuPlatform));
point.setId(StrUtil.join("_", point.getService(), point.getNo()));
list.add(point);
}
coreRoleAuthorityMapper.batchInsert(list);
}
@Override
public List<String> getAuthorities(TokenBean tokenBean) {
List<CoreRoleAuthority> list = coreRoleUserMapper.getOwnerAuthorityLeafList(tokenBean.getId(),
tokenBean.getRoleId(), "");
return list.stream().map(CoreRoleAuthority::getNo).distinct().collect(Collectors.toList());
}
@Override
public void onCreateAfter(String version) {
coreRoleAuthorityMapper.delete(new QueryWrapper<CoreRoleAuthority>()
.ne("version", version)
.eq("service", globalConfig.getService())
.eq("source", "auto")
);
}
}

View File

@@ -1,3 +1,6 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
create table core_role_authority create table core_role_authority
( (
id varchar(50) not null id varchar(50) not null
@@ -93,3 +96,16 @@ create table core_role_user
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
comment '角色-用户'; comment '角色-用户';
alter table core_role_authority
add version varchar(50) null comment '版本号' after platform;
alter table core_role_authority
add source varchar(10) null comment '权限来源auto-自动生成input-添加' after version;
alter table core_role_authority
add deps text null comment '依赖权限' after source;
alter table core_role_group_rx
add type varchar(10) null comment '关联类型dep-依赖bind-绑定';
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -19,13 +19,43 @@
<result column="type" jdbcType="VARCHAR" property="type" /> <result column="type" jdbcType="VARCHAR" property="type" />
<result column="link" jdbcType="LONGVARCHAR" property="link" /> <result column="link" jdbcType="LONGVARCHAR" property="link" />
<result column="platform" jdbcType="VARCHAR" property="platform" /> <result column="platform" jdbcType="VARCHAR" property="platform" />
<result column="version" jdbcType="VARCHAR" property="version" />
<result column="source" jdbcType="VARCHAR" property="source" />
<result column="deps" jdbcType="LONGVARCHAR" property="deps" />
<result column="ext1" jdbcType="VARCHAR" property="ext1" /> <result column="ext1" jdbcType="VARCHAR" property="ext1" />
<result column="ext2" jdbcType="VARCHAR" property="ext2" /> <result column="ext2" jdbcType="VARCHAR" property="ext2" />
<result column="ext3" jdbcType="VARCHAR" property="ext3" /> <result column="ext3" jdbcType="VARCHAR" property="ext3" />
</resultMap> </resultMap>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
<!--@mbg.generated--> <!--@mbg.generated-->
id, create_time, update_time, is_deleted, service, `no`, `name`, sort, `level`, parent, id, create_time, update_time, is_deleted, service, `no`, `name`, sort, `level`, parent,
remark, is_open, `type`, link, platform, ext1, ext2, ext3 remark, is_open, `type`, link, platform, version, `source`, deps, ext1, ext2, ext3
</sql> </sql>
</mapper>
<insert id="batchInsert">
insert into core_role_authority(id, create_time, update_time, is_deleted, service, no, name, level, parent,
type, platform, is_open, version, source, deps)
values
<foreach collection="list" item="item" separator=",">
(#{item.id}, now(), now(), 0,
#{item.service},
#{item.no},
#{item.name},
#{item.level},
#{item.parent},
#{item.type},
#{item.platform}, 1, #{item.version}, #{item.source}, #{item.deps})
</foreach>
on duplicate key update update_time=now(),
is_deleted=0,
level=values(level),
parent=values(parent),
type=values(type),
platform=values(platform),
source=values(source),
deps=values(deps),
version=values(version)
</insert>
</mapper>

View File

@@ -10,19 +10,20 @@
<result column="is_deleted" jdbcType="INTEGER" property="isDeleted" /> <result column="is_deleted" jdbcType="INTEGER" property="isDeleted" />
<result column="group_id" jdbcType="VARCHAR" property="groupId" /> <result column="group_id" jdbcType="VARCHAR" property="groupId" />
<result column="menu_id" jdbcType="VARCHAR" property="menuId" /> <result column="menu_id" jdbcType="VARCHAR" property="menuId" />
<result column="type" jdbcType="VARCHAR" property="type" />
</resultMap> </resultMap>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
<!--@mbg.generated--> <!--@mbg.generated-->
id, create_time, update_time, is_deleted, group_id, menu_id id, create_time, update_time, is_deleted, group_id, menu_id, `type`
</sql> </sql>
<insert id="batchInsert"> <insert id="batchInsert">
insert into core_role_group_rx(id, create_time, update_time, is_deleted, group_id, menu_id) insert into core_role_group_rx(id, create_time, update_time, is_deleted, group_id, menu_id,type)
values values
<foreach collection="list" separator="," item="item"> <foreach collection="list" item="item" separator=",">
(uuid(), now(), now(), 0, (uuid(), now(), now(), 0,
#{item.groupId}, #{item.groupId},
#{item.menuId}) #{item.menuId}, #{item.type})
</foreach> </foreach>
</insert> </insert>
</mapper> </mapper>

View File

@@ -50,10 +50,13 @@
select crgr.menu_id select crgr.menu_id
from core_role_user cru from core_role_user cru
left join core_role_group_rx crgr on crgr.group_id = cru.type_id left join core_role_group_rx crgr on crgr.group_id = cru.type_id
where type = 'job' where cru.type = 'job'
and user_id = #{userId} and cru.user_id = #{userId}
and cru.is_deleted = 0 and cru.is_deleted = 0
and (cru.expire_time is null or cru.expire_time > now()) and (cru.expire_time is null or cru.expire_time > now())
<if test="type != null and type != ''">
and crgr.type=#{type}
</if>
<if test="roleId != null and roleId != ''"> <if test="roleId != null and roleId != ''">
union union
@@ -61,6 +64,9 @@
select menu_id select menu_id
from core_role_group_rx from core_role_group_rx
where group_id = #{roleId} where group_id = #{roleId}
<if test="type != null and type != ''">
and type=#{type}
</if>
</if> </if>
) )
order by sort order by sort

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.tiesheng.springboot-parent</groupId> <groupId>com.tiesheng.springboot-parent</groupId>
<artifactId>springboot-parent</artifactId> <artifactId>springboot-parent</artifactId>
<version>2.0.0.rc7</version> <version>2.0.1</version>
</parent> </parent>
<artifactId>springboot-util</artifactId> <artifactId>springboot-util</artifactId>
@@ -32,8 +32,50 @@
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId> <artifactId>hutool-core</artifactId>
<version>5.8.16</version> <version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-cache</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-log</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-db</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency> </dependency>
<!-- MySql驱动 --> <!-- MySql驱动 -->
@@ -68,6 +110,17 @@
<version>1.68</version> <version>1.68</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -4,10 +4,8 @@ package com.tiesheng.util;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject; import com.alibaba.fastjson.JSON;
import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONObject;
import java.util.Map;
public class CharacterUtils { public class CharacterUtils {
@@ -24,19 +22,24 @@ public class CharacterUtils {
/** /**
* 移除特殊字符 * 移除特殊字符
* *
* @param o 类 * @param o
* @param searchStr 规则 * @param searchStr 规则
*/ */
public static void removeSymbol(Object o, String searchStr) { public static void removeSymbol(Object o, String searchStr) {
JSONObject object = JSONUtil.parseObj(o, false); JSONObject object = JSON.parseObject(JSON.toJSONString(o));
for (Map.Entry<String, Object> entry : object) { for (String key : object.keySet()) {
if (ObjectUtil.isEmpty(entry.getValue()) || StrUtil.equals("null", String.valueOf(entry.getValue()))) { if (StrUtil.isEmpty(key)) {
entry.setValue("");
continue; continue;
} }
if (StrUtil.isNotEmpty(entry.getKey())) {
entry.setValue(StrUtil.trim(StrUtil.replace(entry.getValue().toString(), searchStr, ""))); Object value = object.get(key);
if (ObjectUtil.isEmpty(value) || StrUtil.equals("null", String.valueOf(value))) {
object.put(key, "");
continue;
} }
String replace = StrUtil.replace(String.valueOf(value), searchStr, "");
object.put(key, StrUtil.trim(replace));
} }
BeanUtil.copyProperties(object, o); BeanUtil.copyProperties(object, o);
} }

View File

@@ -1,50 +0,0 @@
package com.tiesheng.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.tiesheng.util.config.DesensitizeValueFilter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class CommonUtil {
/**
* FastJson配置
*
* @return
*/
public static FastJsonConfig fastJsonConfig() {
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.WriteEnumUsingName,
SerializerFeature.DisableCircularReferenceDetect
);
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
config.setSerializeFilters(new DesensitizeValueFilter());
return config;
}
/**
* 格式化数据
*
* @param value
* @return
* @throws IOException
*/
public static String writeJsonString(Object value) throws IOException {
FastJsonConfig fastJsonConfig = fastJsonConfig();
ByteArrayOutputStream outnew = new ByteArrayOutputStream();
JSON.writeJSONStringWithFastJsonConfig(outnew, fastJsonConfig.getCharset(),
value, fastJsonConfig.getSerializeConfig(),
fastJsonConfig.getSerializeFilters(),
fastJsonConfig.getDateFormat(), JSON.DEFAULT_GENERATE_FEATURE,
fastJsonConfig.getSerializerFeatures());
return outnew.toString();
}
}

View File

@@ -1,25 +0,0 @@
package com.tiesheng.util;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
public class TimedCacheHelper {
private static volatile TimedCache<String, String> timedCache;
/**
* 获取一个默认2分钟过期的缓存
*/
public static TimedCache<String, String> getTimedCache() {
if (timedCache == null) {
synchronized (TimedCacheHelper.class) {
if (timedCache == null) {
timedCache = CacheUtil.newTimedCache(2 * 60 * 1000);
timedCache.schedulePrune(1000);
}
}
}
return timedCache;
}
}

View File

@@ -1,37 +0,0 @@
package com.tiesheng.util.config;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.tiesheng.annotation.desensitize.Desensitize;
import java.lang.reflect.Field;
/**
* 脱敏过滤类
*
* @author hao
*/
public class DesensitizeValueFilter implements ValueFilter {
@Override
public Object process(Object object, String name, Object value) {
if (ObjectUtil.isEmpty(value) || !(value instanceof String)) {
return value;
}
Field field = ReflectUtil.getField(object.getClass(), name);
if (ObjectUtil.isEmpty(field)) {
return value;
}
Desensitize desensitize = field.getAnnotation(Desensitize.class);
if (String.class != field.getType() || ObjectUtil.isEmpty(desensitize)) {
return value;
}
String originVal = String.valueOf(value);
return StrUtil.hide(originVal, desensitize.prefix(),
StrUtil.length(originVal) - desensitize.suffix());
}
}

View File

@@ -8,8 +8,8 @@ import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4; import cn.hutool.crypto.symmetric.SM4;
import com.tiesheng.util.ServletKit; import com.tiesheng.util.ServletKit;
import com.tiesheng.util.TimedCacheHelper;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.service.TsCacheService;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -104,12 +104,12 @@ public class EncryptConfig {
String salt = decrypt(encrypted).substring(0, saltSize); String salt = decrypt(encrypted).substring(0, saltSize);
String inputEncrypted = passwdCreate(inputPasswd, salt); String inputEncrypted = passwdCreate(inputPasswd, salt);
if (!StrUtil.equals(inputEncrypted, encrypted)) { if (!StrUtil.equals(inputEncrypted, encrypted)) {
String clientIp = ServletKit.getClientIP(); String clientIp = "passwdVerify_" + ServletKit.getClientIP();
int num = NumberUtil.parseInt(TimedCacheHelper.getTimedCache().get(clientIp, false)); int num = NumberUtil.parseInt(TsCacheService.of().get(clientIp, -1));
if (num > 5) { if (num > 5) {
throw new ApiException("登录失败已达6次请10分钟后再试"); throw new ApiException("登录失败已达6次请10分钟后再试");
} }
TimedCacheHelper.getTimedCache().put(clientIp, String.valueOf(num + 1), 10 * 60 * 1000); TsCacheService.of().put(clientIp, String.valueOf(num + 1), 10 * 60);
throw new ApiException("账号或密码错误"); throw new ApiException("账号或密码错误");
} }
} }

View File

@@ -4,17 +4,16 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.comparator.VersionComparator; import cn.hutool.core.comparator.VersionComparator;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpGlobalConfig;
import cn.hutool.log.LogFactory; import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -29,6 +28,18 @@ import java.util.List;
@ConfigurationProperties(prefix = "tiesheng.global") @ConfigurationProperties(prefix = "tiesheng.global")
public class GlobalConfig { public class GlobalConfig {
static {
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
// FASTJSON 设置全局序列化配置
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteMapNullValue.getMask();
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullStringAsEmpty.getMask();
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteEnumUsingName.getMask();
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteDateUseDateFormat.getMask();
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
}
private String host; private String host;
private String service; private String service;
private String version; private String version;
@@ -40,14 +51,6 @@ public class GlobalConfig {
private HashMap<String, String> ext; private HashMap<String, String> ext;
@PostConstruct
public void init() {
// 默认10s的超时时间
HttpGlobalConfig.setTimeout(10 * 1000);
// 最多重定向3次
HttpGlobalConfig.setMaxRedirectCount(3);
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// 逻辑方法 // 逻辑方法
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@@ -1,9 +1,9 @@
package com.tiesheng.util.config; package com.tiesheng.util.config;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.log.LogFactory; import cn.hutool.log.LogFactory;
import com.tiesheng.util.ip2region.Searcher; import com.tiesheng.util.ip2region.Searcher;
import com.tiesheng.util.service.http.OkHttpUtil;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -27,7 +27,7 @@ public class Ip2regionConfig {
public void downloadDbFile() { public void downloadDbFile() {
if (!FileUtil.exist(dbPath)) { if (!FileUtil.exist(dbPath)) {
LogFactory.get().info("download ip2region file start"); LogFactory.get().info("download ip2region file start");
HttpUtil.downloadFile(dbUrl, dbPath); OkHttpUtil.downloadFile(dbUrl, dbPath);
LogFactory.get().info("download ip2region file finish"); LogFactory.get().info("download ip2region file finish");
} }
} }

View File

@@ -4,10 +4,10 @@ package com.tiesheng.util.config;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTValidator;
import com.tiesheng.util.exception.ApiException; import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.jwt.JWT;
import com.tiesheng.util.jwt.JWTValidator;
import com.tiesheng.util.pojos.TokenBean; import com.tiesheng.util.pojos.TokenBean;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -23,6 +23,12 @@ import java.util.Map;
@ConfigurationProperties("tiesheng.token") @ConfigurationProperties("tiesheng.token")
public class TsTokenConfig { public class TsTokenConfig {
/**
* 用户登录的KEY
*/
public static String CACHE_REQUEST_LOGIN_KEY = "CACHE:LOGIN:{}";
/** /**
* token常量 * token常量
*/ */
@@ -75,11 +81,11 @@ public class TsTokenConfig {
try { try {
JWT decode = JWT.of(token); JWT decode = JWT.of(token);
JWTValidator.of(decode).validateDate(); JWTValidator.of(decode).validateDate();
tokenBean = JSONUtil.toBean(decode.getPayloads(), TokenBean.class); tokenBean = JSON.toJavaObject(decode.getPayloads(), TokenBean.class);
} catch (Exception ignored) { } catch (Exception ignored) {
} }
if (tokenBean == null && thrExp) { if ((tokenBean == null || StrUtil.isEmpty(tokenBean.getId())) && thrExp) {
throw new ApiException(StrUtil.isEmpty(token) ? 110 : 112, throw new ApiException(StrUtil.isEmpty(token) ? 110 : 112,
StrUtil.isEmpty(token) ? "请先登录" : "登录过期,请重新登陆"); StrUtil.isEmpty(token) ? "请先登录" : "登录过期,请重新登陆");
} }

View File

@@ -0,0 +1,95 @@
package com.tiesheng.util.jwt;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.Map;
/**
* Claims 认证简单的JSONObject包装
*
* @author looly
* @since 5.7.0
*/
public class Claims implements Serializable {
private static final long serialVersionUID = 1L;
private JSONObject claimJSON;
/**
* 增加Claims属性如果属性值为{@code null},则移除这个属性
*
* @param name 属性名
* @param value 属性值
*/
protected void setClaim(String name, Object value) {
init();
Assert.notNull(name, "Name must be not null!");
if (value == null) {
claimJSON.remove(name);
return;
}
claimJSON.put(name, value);
}
/**
* 加入多个Claims属性
*
* @param headerClaims 多个Claims属性
*/
protected void putAll(Map<String, ?> headerClaims) {
if (MapUtil.isNotEmpty(headerClaims)) {
for (Map.Entry<String, ?> entry : headerClaims.entrySet()) {
setClaim(entry.getKey(), entry.getValue());
}
}
}
/**
* 获取指定名称属性
*
* @param name 名称
* @return 属性
*/
public Object getClaim(String name) {
init();
return this.claimJSON.get(name);
}
/**
* 获取Claims的JSON字符串形式
*
* @return JSON字符串
*/
public JSONObject getClaimsJson() {
init();
return this.claimJSON;
}
/**
* 解析JWT JSON
*
* @param tokenPart JWT JSON
* @param charset 编码
*/
public void parse(String tokenPart, Charset charset) {
this.claimJSON = JSON.parseObject(Base64.decodeStr(tokenPart, charset));
}
@Override
public String toString() {
init();
return this.claimJSON.toString();
}
private void init() {
if (null == this.claimJSON) {
this.claimJSON = new JSONObject();
}
}
}

View File

@@ -0,0 +1,433 @@
package com.tiesheng.util.jwt;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.tiesheng.util.jwt.signers.AlgorithmUtil;
import com.tiesheng.util.jwt.signers.JWTSigner;
import com.tiesheng.util.jwt.signers.JWTSignerUtil;
import com.tiesheng.util.jwt.signers.NoneJWTSigner;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.KeyPair;
import java.util.List;
import java.util.Map;
/**
* JSON Web Token (JWT)基于JSON的开放标准(RFC 7519)用于在网络应用环境间传递声明。<br>
* <p>
* 结构header.payload.signature
* <ul>
* <li>header主要声明了JWT的签名算法</li>
* <li>payload主要承载了各种声明并传递明文数据</li>
* <li>signature拥有该部分的JWT被称为JWS也就是签了名的JWS</li>
* </ul>
*
* <p>
* 详细介绍见https://www.jianshu.com/p/576dbf44b2ae
* </p>
*
* @author looly
* @since 5.7.0
*/
public class JWT implements RegisteredPayload<JWT> {
private final JWTHeader header;
private final JWTPayload payload;
private Charset charset;
private JWTSigner signer;
private List<String> tokens;
/**
* 创建空的JWT对象
*
* @return JWT
*/
public static JWT create() {
return new JWT();
}
/**
* 创建并解析JWT对象
*
* @param token JWT Token字符串格式为xxxx.yyyy.zzzz
* @return JWT
*/
public static JWT of(String token) {
return new JWT(token);
}
/**
* 构造
*/
public JWT() {
this.header = new JWTHeader();
this.payload = new JWTPayload();
this.charset = CharsetUtil.CHARSET_UTF_8;
}
/**
* 构造
*
* @param token JWT Token字符串格式为xxxx.yyyy.zzzz
*/
public JWT(String token) {
this();
parse(token);
}
/**
* 解析JWT内容
*
* @param token JWT Token字符串格式为xxxx.yyyy.zzzz
* @return this
*/
public JWT parse(String token) {
Assert.notBlank(token, "Token String must be not blank!");
final List<String> tokens = splitToken(token);
this.tokens = tokens;
this.header.parse(tokens.get(0), this.charset);
this.payload.parse(tokens.get(1), this.charset);
return this;
}
/**
* 设置编码
*
* @param charset 编码
* @return this
*/
public JWT setCharset(Charset charset) {
this.charset = charset;
return this;
}
/**
* 设置密钥如果头部指定了算法直接使用否则默认算法是HS256(HmacSHA256)
*
* @param key 密钥
* @return this
*/
public JWT setKey(byte[] key) {
// 检查头信息中是否有算法信息
final String claim = (String) this.header.getClaim(JWTHeader.ALGORITHM);
if (StrUtil.isNotBlank(claim)) {
return setSigner(JWTSignerUtil.createSigner(claim, key));
}
return setSigner(JWTSignerUtil.hs256(key));
}
/**
* 设置签名算法
*
* @param algorithmId 签名算法ID如HS256
* @param key 密钥
* @return this
*/
public JWT setSigner(String algorithmId, byte[] key) {
return setSigner(JWTSignerUtil.createSigner(algorithmId, key));
}
/**
* 设置签名算法
*
* @param algorithmId 签名算法ID如HS256
* @param key 密钥
* @return this
* @since 5.7.2
*/
public JWT setSigner(String algorithmId, Key key) {
return setSigner(JWTSignerUtil.createSigner(algorithmId, key));
}
/**
* 设置非对称签名算法
*
* @param algorithmId 签名算法ID如HS256
* @param keyPair 密钥对
* @return this
* @since 5.7.2
*/
public JWT setSigner(String algorithmId, KeyPair keyPair) {
return setSigner(JWTSignerUtil.createSigner(algorithmId, keyPair));
}
/**
* 设置签名算法
*
* @param signer 签名算法
* @return this
*/
public JWT setSigner(JWTSigner signer) {
this.signer = signer;
return this;
}
/**
* 获取JWT算法签名器
*
* @return JWT算法签名器
*/
public JWTSigner getSigner() {
return this.signer;
}
/**
* 获取所有头信息
*
* @return 头信息
*/
public JSONObject getHeaders() {
return this.header.getClaimsJson();
}
/**
* 获取头
*
* @return 头信息
* @since 5.7.2
*/
public JWTHeader getHeader() {
return this.header;
}
/**
* 获取头信息
*
* @param name 头信息名称
* @return 头信息
*/
public Object getHeader(String name) {
return this.header.getClaim(name);
}
/**
* 获取算法ID(alg)头信息
*
* @return 算法头信息
* @see JWTHeader#ALGORITHM
*/
public String getAlgorithm() {
return (String) this.header.getClaim(JWTHeader.ALGORITHM);
}
/**
* 设置JWT头信息
*
* @param name 头名
* @param value 头
* @return this
*/
public JWT setHeader(String name, Object value) {
this.header.setClaim(name, value);
return this;
}
/**
* 增加JWT头信息
*
* @param headers 头信息
* @return this
*/
public JWT addHeaders(Map<String, ?> headers) {
this.header.addHeaders(headers);
return this;
}
/**
* 获取所有载荷信息
*
* @return 载荷信息
*/
public JSONObject getPayloads() {
return this.payload.getClaimsJson();
}
/**
* 获取载荷对象
*
* @return 载荷信息
* @since 5.7.2
*/
public JWTPayload getPayload() {
return this.payload;
}
/**
* 获取载荷信息
*
* @param name 载荷信息名称
* @return 载荷信息
*/
public Object getPayload(String name) {
return getPayload().getClaim(name);
}
/**
* 设置JWT载荷信息
*
* @param name 载荷名
* @param value 头
* @return this
*/
@Override
public JWT setPayload(String name, Object value) {
this.payload.setClaim(name, value);
return this;
}
/**
* 增加JWT载荷信息
*
* @param payloads 载荷信息
* @return this
*/
public JWT addPayloads(Map<String, ?> payloads) {
this.payload.addPayloads(payloads);
return this;
}
/**
* 签名生成JWT字符串
*
* @return JWT字符串
*/
public String sign() {
return sign(true);
}
/**
* 签名生成JWT字符串
*
* @param addTypeIfNot 如果'typ'头不存在,是否赋值默认值
* @return JWT字符串
* @since 5.8.24
*/
public String sign(boolean addTypeIfNot) {
return sign(this.signer, addTypeIfNot);
}
/**
* 签名生成JWT字符串
*
* @param signer JWT签名器
* @return JWT字符串
*/
public String sign(JWTSigner signer) {
return sign(signer, true);
}
/**
* 签名生成JWT字符串
*
* @param signer JWT签名器
* @param addTypeIfNot 如果'typ'头不存在,是否赋值默认值
* @return JWT字符串
* @since 5.8.24
*/
public String sign(JWTSigner signer, boolean addTypeIfNot) {
Assert.notNull(signer, () -> new JWTException("No Signer provided!"));
// 检查tye信息
if (addTypeIfNot) {
final String type = (String) this.header.getClaim(JWTHeader.TYPE);
if (StrUtil.isBlank(type)) {
this.header.setClaim(JWTHeader.TYPE, "JWT");
}
}
// 检查头信息中是否有算法信息
final String algorithm = (String) this.header.getClaim(JWTHeader.ALGORITHM);
if (StrUtil.isBlank(algorithm)) {
this.header.setClaim(JWTHeader.ALGORITHM,
AlgorithmUtil.getId(signer.getAlgorithm()));
}
final String headerBase64 = Base64.encodeUrlSafe(this.header.toString(), charset);
final String payloadBase64 = Base64.encodeUrlSafe(this.payload.toString(), charset);
final String sign = signer.sign(headerBase64, payloadBase64);
return StrUtil.format("{}.{}.{}", headerBase64, payloadBase64, sign);
}
/**
* 验证JWT Token是否有效
*
* @return 是否有效
*/
public boolean verify() {
return verify(this.signer);
}
/**
* 验证JWT是否有效验证包括
*
* <ul>
* <li>Token是否正确</li>
* <li>{@link JWTPayload#NOT_BEFORE}:生效时间不能晚于当前时间</li>
* <li>{@link JWTPayload#EXPIRES_AT}:失效时间不能早于当前时间</li>
* <li>{@link JWTPayload#ISSUED_AT} 签发时间不能晚于当前时间</li>
* </ul>
*
* @param leeway 容忍空间,单位:秒。当不能晚于当前时间时,向后容忍;不能早于向前容忍。
* @return 是否有效
* @see JWTValidator
* @since 5.7.4
*/
public boolean validate(long leeway) {
if (false == verify()) {
return false;
}
// 校验时间字段
try {
JWTValidator.of(this).validateDate(DateUtil.date(), leeway);
} catch (ValidateException e) {
return false;
}
return true;
}
/**
* 验证JWT Token是否有效
*
* @param signer 签名器(签名算法)
* @return 是否有效
*/
public boolean verify(JWTSigner signer) {
if (null == signer) {
// 如果无签名器提供默认认为是无签名JWT信息
signer = NoneJWTSigner.NONE;
}
final List<String> tokens = this.tokens;
if (CollUtil.isEmpty(tokens)) {
throw new JWTException("No token to verify!");
}
return signer.verify(tokens.get(0), tokens.get(1), tokens.get(2));
}
/**
* 将JWT字符串拆分为3部分无加密算法则最后一部分是""
*
* @param token JWT Token
* @return 三部分内容
*/
private static List<String> splitToken(String token) {
final List<String> tokens = StrUtil.split(token, CharUtil.DOT);
if (3 != tokens.size()) {
throw new JWTException("The token was expected 3 parts, but got {}.", tokens.size());
}
return tokens;
}
}

View File

@@ -0,0 +1,38 @@
package com.tiesheng.util.jwt;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
/**
* JWT异常
*
* @author looly
* @since 5.7.0
*/
public class JWTException extends RuntimeException {
private static final long serialVersionUID = 1L;
public JWTException(Throwable e) {
super(ExceptionUtil.getMessage(e), e);
}
public JWTException(String message) {
super(message);
}
public JWTException(String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params));
}
public JWTException(String message, Throwable cause) {
super(message, cause);
}
public JWTException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) {
super(message, throwable, enableSuppression, writableStackTrace);
}
public JWTException(Throwable throwable, String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params), throwable);
}
}

View File

@@ -0,0 +1,58 @@
package com.tiesheng.util.jwt;
import java.util.Map;
/**
* JWT头部信息
*
* @author looly
* @since 5.7.0
*/
public class JWTHeader extends Claims {
private static final long serialVersionUID = 1L;
//Header names
/**
* 加密算法通常为HMAC SHA256HS256
*/
public static String ALGORITHM = "alg";
/**
* 声明类型一般为jwt
*/
public static String TYPE = "typ";
/**
* 内容类型content type
*/
public static String CONTENT_TYPE = "cty";
/**
* jwk的ID编号
*/
public static String KEY_ID = "kid";
/**
* 构造,初始化默认(typ=JWT)
*/
public JWTHeader() {}
/**
* 增加“kid”头信息
*
* @param keyId kid
* @return this
*/
public JWTHeader setKeyId(String keyId) {
setClaim(KEY_ID, keyId);
return this;
}
/**
* 增加自定义JWT认证头
*
* @param headerClaims 头信息
* @return this
*/
public JWTHeader addHeaders(Map<String, ?> headerClaims) {
putAll(headerClaims);
return this;
}
}

View File

@@ -0,0 +1,39 @@
package com.tiesheng.util.jwt;
import java.util.Map;
/**
* JWT载荷信息<br>
* 载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
*
* <ul>
* <li>标准中注册的声明</li>
* <li>公共的声明</li>
* <li>私有的声明</li>
* </ul>
* <p>
* 详细介绍见https://www.jianshu.com/p/576dbf44b2ae
*
* @author looly
* @since 5.7.0
*/
public class JWTPayload extends Claims implements RegisteredPayload<JWTPayload>{
private static final long serialVersionUID = 1L;
/**
* 增加自定义JWT认证载荷信息
*
* @param payloadClaims 载荷信息
* @return this
*/
public JWTPayload addPayloads(Map<String, ?> payloadClaims) {
putAll(payloadClaims);
return this;
}
@Override
public JWTPayload setPayload(String name, Object value) {
setClaim(name, value);
return this;
}
}

View File

@@ -0,0 +1,257 @@
package com.tiesheng.util.jwt;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.util.StrUtil;
import com.tiesheng.util.jwt.signers.JWTSigner;
import com.tiesheng.util.jwt.signers.NoneJWTSigner;
import java.util.Date;
/**
* JWT数据校验器用于校验包括
* <ul>
* <li>算法是否一致</li>
* <li>算法签名是否正确</li>
* <li>字段值是否有效(例如时间未过期等)</li>
* </ul>
*
* @author looly
* @since 5.7.2
*/
public class JWTValidator {
private final JWT jwt;
/**
* 创建JWT验证器
*
* @param token JWT Token
* @return JWTValidator
*/
public static JWTValidator of(String token) {
return new JWTValidator(JWT.of(token));
}
/**
* 创建JWT验证器
*
* @param jwt JWT对象
* @return JWTValidator
*/
public static JWTValidator of(JWT jwt) {
return new JWTValidator(jwt);
}
/**
* 构造
*
* @param jwt JWT对象
*/
public JWTValidator(JWT jwt) {
this.jwt = jwt;
}
/**
* 验证算法使用JWT对象自带的{@link JWTSigner}
*
* @return this
* @throws ValidateException 验证失败的异常
*/
public JWTValidator validateAlgorithm() throws ValidateException {
return validateAlgorithm(null);
}
/**
* 验证算法,使用自定义的{@link JWTSigner}
*
* @param signer 用于验证算法的签名器
* @return this
* @throws ValidateException 验证失败的异常
*/
public JWTValidator validateAlgorithm(JWTSigner signer) throws ValidateException {
validateAlgorithm(this.jwt, signer);
return this;
}
/**
* 检查JWT的以下三两个时间
*
* <ul>
* <li>{@link JWTPayload#NOT_BEFORE}:被检查时间必须晚于生效时间</li>
* <li>{@link JWTPayload#EXPIRES_AT}:被检查时间必须早于失效时间</li>
* <li>{@link JWTPayload#ISSUED_AT}:签发时间必须早于失效时间</li>
* </ul>
* <p>
* 如果某个时间没有设置,则不检查(表示无限制)
*
* @return this
* @throws ValidateException 验证失败的异常
* @since 5.7.3
*/
public JWTValidator validateDate() throws ValidateException {
return validateDate(DateUtil.beginOfSecond(DateUtil.date()));
}
/**
* 检查JWT的以下三两个时间
*
* <ul>
* <li>{@link JWTPayload#NOT_BEFORE}:生效时间不能晚于当前时间</li>
* <li>{@link JWTPayload#EXPIRES_AT}:失效时间不能早于当前时间</li>
* <li>{@link JWTPayload#ISSUED_AT} 签发时间不能晚于当前时间</li>
* </ul>
* <p>
* 如果某个时间没有设置,则不检查(表示无限制)
*
* @param dateToCheck 被检查的时间,一般为当前时间
* @return this
* @throws ValidateException 验证失败的异常
*/
public JWTValidator validateDate(Date dateToCheck) throws ValidateException {
validateDate(this.jwt.getPayload(), dateToCheck, 0L);
return this;
}
/**
* 检查JWT的以下三两个时间
*
* <ul>
* <li>{@link JWTPayload#NOT_BEFORE}:生效时间不能晚于当前时间</li>
* <li>{@link JWTPayload#EXPIRES_AT}:失效时间不能早于当前时间</li>
* <li>{@link JWTPayload#ISSUED_AT} 签发时间不能晚于当前时间</li>
* </ul>
* <p>
* 如果某个时间没有设置,则不检查(表示无限制)
*
* @param dateToCheck 被检查的时间,一般为当前时间
* @param leeway 容忍空间,单位:秒。当不能晚于当前时间时,向后容忍;不能早于向前容忍。
* @return this
* @throws ValidateException 验证失败的异常
*/
public JWTValidator validateDate(Date dateToCheck, long leeway) throws ValidateException {
validateDate(this.jwt.getPayload(), dateToCheck, leeway);
return this;
}
/**
* 验证算法
*
* @param jwt {@link JWT}对象
* @param signer 用于验证的签名器
* @throws ValidateException 验证异常
*/
private static void validateAlgorithm(JWT jwt, JWTSigner signer) throws ValidateException {
final String algorithmId = jwt.getAlgorithm();
if (null == signer) {
signer = jwt.getSigner();
}
if (StrUtil.isEmpty(algorithmId)) {
// 可能无签名
if (null == signer || signer instanceof NoneJWTSigner) {
return;
}
throw new ValidateException("No algorithm defined in header!");
}
if (null == signer) {
throw new IllegalArgumentException("No Signer for validate algorithm!");
}
final String algorithmIdInSigner = signer.getAlgorithmId();
if (false == StrUtil.equals(algorithmId, algorithmIdInSigner)) {
throw new ValidateException("Algorithm [{}] defined in header doesn't match to [{}]!"
, algorithmId, algorithmIdInSigner);
}
// 通过算法验证签名是否正确
if (false == jwt.verify(signer)) {
throw new ValidateException("Signature verification failed!");
}
}
/**
* 检查JWT的以下三两个时间
*
* <ul>
* <li>{@link JWTPayload#NOT_BEFORE}:生效时间不能晚于当前时间</li>
* <li>{@link JWTPayload#EXPIRES_AT}:失效时间不能早于当前时间</li>
* <li>{@link JWTPayload#ISSUED_AT} 签发时间不能晚于当前时间</li>
* </ul>
* <p>
* 如果某个时间没有设置,则不检查(表示无限制)
*
* @param payload {@link JWTPayload}
* @param now 当前时间
* @param leeway 容忍空间,单位:秒。当不能晚于当前时间时,向后容忍;不能早于向前容忍。
* @throws ValidateException 验证异常
*/
private static void validateDate(JWTPayload payload, Date now, long leeway) throws ValidateException {
if (null == now) {
// 默认当前时间
now = DateUtil.date();
// truncate millis
now.setTime(now.getTime() / 1000 * 1000);
}
// 检查生效时间(生效时间不能晚于当前时间)
final Date notBefore = payload.getClaimsJson().getDate(JWTPayload.NOT_BEFORE);
validateNotAfter(JWTPayload.NOT_BEFORE, notBefore, now, leeway);
// 检查失效时间(失效时间不能早于当前时间)
final Date expiresAt = payload.getClaimsJson().getDate(JWTPayload.EXPIRES_AT);
validateNotBefore(JWTPayload.EXPIRES_AT, expiresAt, now, leeway);
// 检查签发时间(签发时间不能晚于当前时间)
final Date issueAt = payload.getClaimsJson().getDate(JWTPayload.ISSUED_AT);
validateNotAfter(JWTPayload.ISSUED_AT, issueAt, now, leeway);
}
/**
* 验证指定字段的时间不能晚于当前时间<br>
* 被检查的日期不存在则跳过
*
* @param fieldName 字段名
* @param dateToCheck 被检查的字段日期
* @param now 当前时间
* @param leeway 容忍空间,单位:秒。向后容忍
* @throws ValidateException 验证异常
*/
private static void validateNotAfter(String fieldName, Date dateToCheck, Date now, long leeway) throws ValidateException {
if (null == dateToCheck) {
return;
}
if(leeway > 0){
now = DateUtil.date(now.getTime() + leeway * 1000);
}
if (dateToCheck.after(now)) {
throw new ValidateException("'{}':[{}] is after now:[{}]",
fieldName, DateUtil.date(dateToCheck), DateUtil.date(now));
}
}
/**
* 验证指定字段的时间不能早于当前时间<br>
* 被检查的日期不存在则跳过
*
* @param fieldName 字段名
* @param dateToCheck 被检查的字段日期
* @param now 当前时间
* @param leeway 容忍空间,单位:秒。。向前容忍
* @throws ValidateException 验证异常
*/
@SuppressWarnings("SameParameterValue")
private static void validateNotBefore(String fieldName, Date dateToCheck, Date now, long leeway) throws ValidateException {
if (null == dateToCheck) {
return;
}
if(leeway > 0){
now = DateUtil.date(now.getTime() - leeway * 1000);
}
if (dateToCheck.before(now)) {
throw new ValidateException("'{}':[{}] is before now:[{}]",
fieldName, DateUtil.date(dateToCheck), DateUtil.date(now));
}
}
}

View File

@@ -0,0 +1,122 @@
package com.tiesheng.util.jwt;
import java.util.Date;
/**
* 注册的标准载荷Payload声明
*
* @param <T> 实现此接口的类的类型
* @author looly
* @since 5.7.2
*/
public interface RegisteredPayload<T extends RegisteredPayload<T>> {
/**
* jwt签发者
*/
String ISSUER = "iss";
/**
* jwt所面向的用户
*/
String SUBJECT = "sub";
/**
* 接收jwt的一方
*/
String AUDIENCE = "aud";
/**
* jwt的过期时间这个过期时间必须要大于签发时间
*/
String EXPIRES_AT = "exp";
/**
* 生效时间定义在什么时间之前该jwt都是不可用的.
*/
String NOT_BEFORE = "nbf";
/**
* jwt的签发时间
*/
String ISSUED_AT = "iat";
/**
* jwt的唯一身份标识主要用来作为一次性token,从而回避重放攻击。
*/
String JWT_ID = "jti";
/**
* 设置 jwt签发者("iss")的Payload值
*
* @param issuer jwt签发者
* @return this
*/
default T setIssuer(String issuer) {
return setPayload(ISSUER, issuer);
}
/**
* 设置jwt所面向的用户("sub")的Payload值
*
* @param subject jwt所面向的用户
* @return this
*/
default T setSubject(String subject) {
return setPayload(SUBJECT, subject);
}
/**
* 设置接收jwt的一方("aud")的Payload值
*
* @param audience 接收jwt的一方
* @return this
*/
default T setAudience(String... audience) {
return setPayload(AUDIENCE, audience);
}
/**
* 设置jwt的过期时间("exp")的Payload值这个过期时间必须要大于签发时间
*
* @param expiresAt jwt的过期时间
* @return this
* @see #setIssuedAt(Date)
*/
default T setExpiresAt(Date expiresAt) {
return setPayload(EXPIRES_AT, expiresAt);
}
/**
* 设置不可用时间点界限("nbf")的Payload值
*
* @param notBefore 不可用时间点界限在这个时间点之前jwt不可用
* @return this
*/
default T setNotBefore(Date notBefore) {
return setPayload(NOT_BEFORE, notBefore);
}
/**
* 设置jwt的签发时间("iat")
*
* @param issuedAt 签发时间
* @return this
*/
default T setIssuedAt(Date issuedAt) {
return setPayload(ISSUED_AT, issuedAt);
}
/**
* 设置jwt的唯一身份标识("jti")
*
* @param jwtId 唯一身份标识
* @return this
*/
default T setJWTId(String jwtId) {
return setPayload(JWT_ID, jwtId);
}
/**
* 设置Payload值
*
* @param name payload名
* @param value payload值
* @return this
*/
T setPayload(String name, Object value);
}

View File

@@ -0,0 +1,89 @@
package com.tiesheng.util.jwt.signers;
import cn.hutool.core.map.BiMap;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.digest.HmacAlgorithm;
import java.util.HashMap;
/**
* 算法工具类算法和JWT算法ID对应表
*
* @author looly
* @since 5.7.0
*/
public class AlgorithmUtil {
private static final BiMap<String, String> map;
static {
map = new BiMap<>(new HashMap<>());
map.put("HS256", HmacAlgorithm.HmacSHA256.getValue());
map.put("HS384", HmacAlgorithm.HmacSHA384.getValue());
map.put("HS512", HmacAlgorithm.HmacSHA512.getValue());
map.put("HMD5", HmacAlgorithm.HmacMD5.getValue());
map.put("HSHA1", HmacAlgorithm.HmacSHA1.getValue());
map.put("SM4CMAC", HmacAlgorithm.SM4CMAC.getValue());
map.put("RS256", SignAlgorithm.SHA256withRSA.getValue());
map.put("RS384", SignAlgorithm.SHA384withRSA.getValue());
map.put("RS512", SignAlgorithm.SHA512withRSA.getValue());
map.put("ES256", SignAlgorithm.SHA256withECDSA.getValue());
map.put("ES384", SignAlgorithm.SHA384withECDSA.getValue());
map.put("ES512", SignAlgorithm.SHA512withECDSA.getValue());
map.put("PS256", SignAlgorithm.SHA256withRSA_PSS.getValue());
map.put("PS384", SignAlgorithm.SHA384withRSA_PSS.getValue());
map.put("PS512", SignAlgorithm.SHA512withRSA_PSS.getValue());
map.put("RMD2", SignAlgorithm.MD2withRSA.getValue());
map.put("RMD5", SignAlgorithm.MD5withRSA.getValue());
map.put("RSHA1", SignAlgorithm.SHA1withRSA.getValue());
map.put("DNONE", SignAlgorithm.NONEwithDSA.getValue());
map.put("DSHA1", SignAlgorithm.SHA1withDSA.getValue());
map.put("ENONE", SignAlgorithm.NONEwithECDSA.getValue());
map.put("ESHA1", SignAlgorithm.SHA1withECDSA.getValue());
}
/**
* 获取算法用户传入算法ID返回算法名传入算法名返回本身
* @param idOrAlgorithm 算法ID或算法名
* @return 算法名
*/
public static String getAlgorithm(String idOrAlgorithm){
return ObjectUtil.defaultIfNull(getAlgorithmById(idOrAlgorithm), idOrAlgorithm);
}
/**
* 获取算法ID用户传入算法名返回ID传入算法ID返回本身
* @param idOrAlgorithm 算法ID或算法名
* @return 算法ID
*/
public static String getId(String idOrAlgorithm){
return ObjectUtil.defaultIfNull(getIdByAlgorithm(idOrAlgorithm), idOrAlgorithm);
}
/**
* 根据JWT算法ID获取算法
*
* @param id JWT算法ID
* @return 算法
*/
private static String getAlgorithmById(String id) {
return map.get(id.toUpperCase());
}
/**
* 根据算法获取JWT算法ID
*
* @param algorithm 算法
* @return JWT算法ID
*/
private static String getIdByAlgorithm(String algorithm) {
return map.getKey(algorithm);
}
}

View File

@@ -0,0 +1,97 @@
package com.tiesheng.util.jwt.signers;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.Sign;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* 非对称加密JWT签名封装
*
* @author looly
* @since 5.7.0
*/
public class AsymmetricJWTSigner implements JWTSigner {
private Charset charset = CharsetUtil.CHARSET_UTF_8;
private final Sign sign;
/**
* 构造
*
* @param algorithm 算法字符串表示
* @param key 公钥{@link PublicKey}或私钥{@link PrivateKey},公钥用于验证签名,私钥用于产生签名
*/
public AsymmetricJWTSigner(String algorithm, Key key) {
final PublicKey publicKey = key instanceof PublicKey ? (PublicKey) key : null;
final PrivateKey privateKey = key instanceof PrivateKey ? (PrivateKey) key : null;
this.sign = new Sign(algorithm, privateKey, publicKey);
}
/**
* 构造
*
* @param algorithm 算法字符串表示
* @param keyPair 密钥对
*/
public AsymmetricJWTSigner(String algorithm, KeyPair keyPair) {
this.sign = new Sign(algorithm, keyPair);
}
/**
* 设置编码
*
* @param charset 编码
* @return 编码
*/
public AsymmetricJWTSigner setCharset(Charset charset) {
this.charset = charset;
return this;
}
@Override
public String sign(String headerBase64, String payloadBase64) {
final String dataStr = StrUtil.format("{}.{}", headerBase64, payloadBase64);
return Base64.encodeUrlSafe(sign(StrUtil.bytes(dataStr, charset)));
}
/**
* 签名字符串数据
*
* @param data 数据
* @return 签名
*/
protected byte[] sign(byte[] data) {
return sign.sign(data);
}
@Override
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
return verify(
StrUtil.bytes(StrUtil.format("{}.{}", headerBase64, payloadBase64), charset),
Base64.decode(signBase64));
}
/**
* 验签数据
*
* @param data 数据
* @param signed 签名
* @return 是否通过
*/
protected boolean verify(byte[] data, byte[] signed) {
return sign.verify(data, signed);
}
@Override
public String getAlgorithm() {
return this.sign.getSignature().getAlgorithm();
}
}

View File

@@ -0,0 +1,182 @@
package com.tiesheng.util.jwt.signers;
import com.tiesheng.util.jwt.JWTException;
import java.security.Key;
import java.security.KeyPair;
/**
* 椭圆曲线Elliptic Curve的JWT签名器。<br>
* 按照https://datatracker.ietf.org/doc/html/rfc7518#section-3.4,<br>
* Elliptic Curve Digital Signature Algorithm (ECDSA)算法签名需要转换DER格式为pair (R, S)
*
* @author looly
* @since 5.8.21
*/
public class EllipticCurveJWTSigner extends AsymmetricJWTSigner {
/**
* 构造
*
* @param algorithm 算法
* @param key 密钥
*/
public EllipticCurveJWTSigner(String algorithm, Key key) {
super(algorithm, key);
}
/**
* 构造
*
* @param algorithm 算法
* @param keyPair 密钥对
*/
public EllipticCurveJWTSigner(String algorithm, KeyPair keyPair) {
super(algorithm, keyPair);
}
@Override
protected byte[] sign(final byte[] data) {
// https://datatracker.ietf.org/doc/html/rfc7518#section-3.4
return derToConcat(super.sign(data), getSignatureByteArrayLength(getAlgorithm()));
}
@Override
protected boolean verify(final byte[] data, final byte[] signed) {
// https://datatracker.ietf.org/doc/html/rfc7518#section-3.4
return super.verify(data, concatToDER(signed));
}
/**
* 获取签名长度
* @param alg 算法
* @return 长度
* @throws JWTException JWT异常
*/
private static int getSignatureByteArrayLength(final String alg) throws JWTException {
switch (alg) {
case "ES256":
case "SHA256withECDSA":
return 64;
case "ES384":
case "SHA384withECDSA":
return 96;
case "ES512":
case "SHA512withECDSA":
return 132;
default:
throw new JWTException("Unsupported Algorithm: {}", alg);
}
}
private static byte[] derToConcat(final byte[] derSignature, int outputLength) throws JWTException {
if (derSignature.length < 8 || derSignature[0] != 48) {
throw new JWTException("Invalid ECDSA signature format");
}
final int offset;
if (derSignature[1] > 0) {
offset = 2;
} else if (derSignature[1] == (byte) 0x81) {
offset = 3;
} else {
throw new JWTException("Invalid ECDSA signature format");
}
final byte rLength = derSignature[offset + 1];
int i = rLength;
while ((i > 0) && (derSignature[(offset + 2 + rLength) - i] == 0)) {
i--;
}
final byte sLength = derSignature[offset + 2 + rLength + 1];
int j = sLength;
while ((j > 0) && (derSignature[(offset + 2 + rLength + 2 + sLength) - j] == 0)) {
j--;
}
int rawLen = Math.max(i, j);
rawLen = Math.max(rawLen, outputLength / 2);
if ((derSignature[offset - 1] & 0xff) != derSignature.length - offset
|| (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength
|| derSignature[offset] != 2
|| derSignature[offset + 2 + rLength] != 2) {
throw new JWTException("Invalid ECDSA signature format");
}
final byte[] concatSignature = new byte[2 * rawLen];
System.arraycopy(derSignature, (offset + 2 + rLength) - i, concatSignature, rawLen - i, i);
System.arraycopy(derSignature, (offset + 2 + rLength + 2 + sLength) - j, concatSignature, 2 * rawLen - j, j);
return concatSignature;
}
private static byte[] concatToDER(byte[] jwsSignature) throws ArrayIndexOutOfBoundsException {
int rawLen = jwsSignature.length / 2;
int i = rawLen;
while ((i > 0) && (jwsSignature[rawLen - i] == 0)) {
i--;
}
int j = i;
if (jwsSignature[rawLen - i] < 0) {
j += 1;
}
int k = rawLen;
while ((k > 0) && (jwsSignature[2 * rawLen - k] == 0)) {
k--;
}
int l = k;
if (jwsSignature[2 * rawLen - k] < 0) {
l += 1;
}
int len = 2 + j + 2 + l;
if (len > 255) {
throw new JWTException("Invalid ECDSA signature format");
}
int offset;
final byte[] derSignature;
if (len < 128) {
derSignature = new byte[2 + 2 + j + 2 + l];
offset = 1;
} else {
derSignature = new byte[3 + 2 + j + 2 + l];
derSignature[1] = (byte) 0x81;
offset = 2;
}
derSignature[0] = 48;
derSignature[offset++] = (byte) len;
derSignature[offset++] = 2;
derSignature[offset++] = (byte) j;
System.arraycopy(jwsSignature, rawLen - i, derSignature, (offset + j) - i, i);
offset += j;
derSignature[offset++] = 2;
derSignature[offset++] = (byte) l;
System.arraycopy(jwsSignature, 2 * rawLen - k, derSignature, (offset + l) - k, k);
return derSignature;
}
}

View File

@@ -0,0 +1,69 @@
package com.tiesheng.util.jwt.signers;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.HMac;
import java.nio.charset.Charset;
import java.security.Key;
/**
* HMac算法签名实现
*
* @author looly
* @since 5.7.0
*/
public class HMacJWTSigner implements JWTSigner {
private Charset charset = CharsetUtil.CHARSET_UTF_8;
private final HMac hMac;
/**
* 构造
*
* @param algorithm HMAC签名算法
* @param key 密钥
*/
public HMacJWTSigner(String algorithm, byte[] key) {
this.hMac = new HMac(algorithm, key);
}
/**
* 构造
*
* @param algorithm HMAC签名算法
* @param key 密钥
*/
public HMacJWTSigner(String algorithm, Key key) {
this.hMac = new HMac(algorithm, key);
}
/**
* 设置编码
*
* @param charset 编码
* @return 编码
*/
public HMacJWTSigner setCharset(Charset charset) {
this.charset = charset;
return this;
}
@Override
public String sign(String headerBase64, String payloadBase64) {
return hMac.digestBase64(StrUtil.format("{}.{}", headerBase64, payloadBase64), charset, true);
}
@Override
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
final String sign = sign(headerBase64, payloadBase64);
return hMac.verify(
StrUtil.bytes(sign, charset),
StrUtil.bytes(signBase64, charset));
}
@Override
public String getAlgorithm() {
return this.hMac.getAlgorithm();
}
}

View File

@@ -0,0 +1,45 @@
package com.tiesheng.util.jwt.signers;
/**
* JWT签名接口封装通过实现此接口完成不同算法的签名功能
*
* @author looly
*/
public interface JWTSigner {
/**
* 签名
*
* @param headerBase64 JWT头的JSON字符串的Base64表示
* @param payloadBase64 JWT载荷的JSON字符串Base64表示
* @return 签名结果Base64即JWT的第三部分
*/
String sign(String headerBase64, String payloadBase64);
/**
* 验签
*
* @param headerBase64 JWT头的JSON字符串Base64表示
* @param payloadBase64 JWT载荷的JSON字符串Base64表示
* @param signBase64 被验证的签名Base64表示
* @return 签名是否一致
*/
boolean verify(String headerBase64, String payloadBase64, String signBase64);
/**
* 获取算法
*
* @return 算法
*/
String getAlgorithm();
/**
* 获取算法ID即算法的简写形式如HS256
*
* @return 算法ID
* @since 5.7.2
*/
default String getAlgorithmId() {
return AlgorithmUtil.getId(getAlgorithm());
}
}

View File

@@ -0,0 +1,288 @@
package com.tiesheng.util.jwt.signers;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReUtil;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* JWT签名器工具类
*
* @author looly
* @since 5.7.0
*/
public class JWTSignerUtil {
/**
* 无签名
*
* @return 无签名的签名器
*/
public static JWTSigner none() {
return NoneJWTSigner.NONE;
}
//------------------------------------------------------------------------- HSxxx
/**
* HS256(HmacSHA256)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner hs256(byte[] key) {
return createSigner("HS256", key);
}
/**
* HS384(HmacSHA384)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner hs384(byte[] key) {
return createSigner("HS384", key);
}
/**
* HS512(HmacSHA512)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner hs512(byte[] key) {
return createSigner("HS512", key);
}
//------------------------------------------------------------------------- RSxxx
/**
* RS256(SHA256withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rs256(Key key) {
return createSigner("RS256", key);
}
/**
* RS384(SHA384withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rs384(Key key) {
return createSigner("RS384", key);
}
/**
* RS512(SHA512withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rs512(Key key) {
return createSigner("RS512", key);
}
//------------------------------------------------------------------------- ESxxx
/**
* ES256(SHA256withECDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner es256(Key key) {
return createSigner("ES256", key);
}
/**
* ES384(SHA383withECDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner es384(Key key) {
return createSigner("ES384", key);
}
/**
* ES512(SHA512withECDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner es512(Key key) {
return createSigner("ES512", key);
}
/**
* HMD5(HmacMD5)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner hmd5(Key key) {
return createSigner("HMD5",key);
}
/**
* HSHA1(HmacSHA1)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner hsha1(Key key) {
return createSigner("HSHA1",key);
}
/**
* SM4CMAC(SM4CMAC)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner sm4cmac(Key key) {
return createSigner("SM4CMAC",key);
}
/**
* RMD2(MD2withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rmd2(Key key) {
return createSigner("RMD2",key);
}
/**
* RMD5(MD5withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rmd5(Key key) {
return createSigner("RMD5",key);
}
/**
* RSHA1(SHA1withRSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner rsha1(Key key) {
return createSigner("RSHA1",key);
}
/**
* DNONE(NONEwithDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner dnone(Key key) {
return createSigner("DNONE",key);
}
/**
* DSHA1(SHA1withDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner dsha1(Key key) {
return createSigner("DSHA1",key);
}
/**
* ENONE(NONEwithECDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner enone(Key key) {
return createSigner("ENONE",key);
}
/**
* ESHA1(SHA1withECDSA)签名器
*
* @param key 密钥
* @return 签名器
*/
public static JWTSigner esha1(Key key) {
return createSigner("ESHA1",key);
}
/**
* 创建签名器
*
* @param algorithmId 算法ID见{@link AlgorithmUtil}
* @param key 密钥
* @return 签名器
*/
public static JWTSigner createSigner(String algorithmId, byte[] key) {
Assert.notNull(key, "Signer key must be not null!");
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
return none();
}
return new HMacJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
}
/**
* 创建签名器
*
* @param algorithmId 算法ID见{@link AlgorithmUtil}
* @param keyPair 密钥对
* @return 签名器
*/
public static JWTSigner createSigner(String algorithmId, KeyPair keyPair) {
Assert.notNull(keyPair, "Signer key pair must be not null!");
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
return none();
}
// issue3205@Github
if(ReUtil.isMatch("es\\d{3}", algorithmId.toLowerCase())){
return new EllipticCurveJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), keyPair);
}
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), keyPair);
}
/**
* 创建签名器
*
* @param algorithmId 算法ID见{@link AlgorithmUtil}
* @param key 密钥
* @return 签名器
*/
public static JWTSigner createSigner(String algorithmId, Key key) {
Assert.notNull(key, "Signer key must be not null!");
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
return NoneJWTSigner.NONE;
}
if (key instanceof PrivateKey || key instanceof PublicKey) {
// issue3205@Github
if(ReUtil.isMatch("ES\\d{3}", algorithmId)){
return new EllipticCurveJWTSigner(algorithmId, key);
}
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
}
return new HMacJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
}
}

View File

@@ -0,0 +1,31 @@
package com.tiesheng.util.jwt.signers;
import cn.hutool.core.util.StrUtil;
/**
* 无需签名的JWT签名器
*
* @author looly
* @since 5.7.0
*/
public class NoneJWTSigner implements JWTSigner {
public static final String ID_NONE = "none";
public static NoneJWTSigner NONE = new NoneJWTSigner();
@Override
public String sign(String headerBase64, String payloadBase64) {
return StrUtil.EMPTY;
}
@Override
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
return StrUtil.isEmpty(signBase64);
}
@Override
public String getAlgorithm() {
return ID_NONE;
}
}

View File

@@ -129,6 +129,19 @@ public class ApiResp<T> {
} }
/**
* 130错误
*
* @param msg
* @return
*/
public static ApiResp<String> resp130(String msg) {
ApiResp<String> result = new ApiResp<>();
result.code = 130;
result.message = msg;
return result;
}
/** /**
* 是否成功 * 是否成功
* *

View File

@@ -6,8 +6,8 @@ import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
import com.tiesheng.util.config.GlobalConfig; import com.tiesheng.util.config.GlobalConfig;
import com.tiesheng.util.service.http.OkHttpUtil;
public class FileUploadPath { public class FileUploadPath {
@@ -71,7 +71,7 @@ public class FileUploadPath {
String newFileName = UPLOAD_FOLDER + StrUtil.subAfter(httpUrl, UPLOAD_FOLDER, true); String newFileName = UPLOAD_FOLDER + StrUtil.subAfter(httpUrl, UPLOAD_FOLDER, true);
FileUploadPath uploadPath = file(newFileName); FileUploadPath uploadPath = file(newFileName);
if (!FileUtil.exist(uploadPath.getAbsolutePath())) { if (!FileUtil.exist(uploadPath.getAbsolutePath())) {
HttpUtil.downloadFile(httpUrl, uploadPath.getAbsolutePath()); OkHttpUtil.downloadFile(httpUrl, uploadPath.getAbsolutePath());
} }
return uploadPath.getHttpPath(); return uploadPath.getHttpPath();

View File

@@ -3,8 +3,8 @@ package com.tiesheng.util.pojos;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.jwt.JWT;
import com.tiesheng.util.config.TsTokenConfig; import com.tiesheng.util.config.TsTokenConfig;
import com.tiesheng.util.jwt.JWT;
/** /**
* @author hao * @author hao
@@ -12,19 +12,15 @@ import com.tiesheng.util.config.TsTokenConfig;
public class TokenBean { public class TokenBean {
private String id; private String id;
private String environmentType;
private String service;
private String roleId; private String roleId;
private String extra; private String extra;
public TokenBean() { public TokenBean() {
} }
public TokenBean(String id, String environmentType, String service) { public TokenBean(String id, String roleId) {
this.id = id; this.id = id;
this.environmentType = environmentType; this.roleId = roleId;
this.service = service;
this.roleId = "";
this.extra = ""; this.extra = "";
} }
@@ -37,8 +33,6 @@ public class TokenBean {
return JWT.create() return JWT.create()
.setExpiresAt(DateUtil.offsetHour(DateUtil.date(), tsTokenConfig.getExpireHours())) .setExpiresAt(DateUtil.offsetHour(DateUtil.date(), tsTokenConfig.getExpireHours()))
.setPayload("id", getId()) .setPayload("id", getId())
.setPayload("environmentType", StrUtil.emptyToDefault(getEnvironmentType(), ""))
.setPayload("service", StrUtil.emptyToDefault(getService(), ""))
.setPayload("roleId", StrUtil.emptyToDefault(getRoleId(), "")) .setPayload("roleId", StrUtil.emptyToDefault(getRoleId(), ""))
.setPayload("extra", StrUtil.emptyToDefault(getExtra(), "")) .setPayload("extra", StrUtil.emptyToDefault(getExtra(), ""))
.setKey(tsTokenConfig.getEncryptKey().getBytes()) .setKey(tsTokenConfig.getEncryptKey().getBytes())
@@ -57,22 +51,6 @@ public class TokenBean {
this.id = id; this.id = id;
} }
public String getEnvironmentType() {
return environmentType;
}
public void setEnvironmentType(String environmentType) {
this.environmentType = environmentType;
}
public String getService() {
return service;
}
public void setService(String service) {
this.service = service;
}
public String getExtra() { public String getExtra() {
return extra; return extra;
} }

View File

@@ -0,0 +1,95 @@
package com.tiesheng.util.service;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.tiesheng.util.ServletKit;
import com.tiesheng.util.exception.ApiException;
import com.tiesheng.util.service.cache.TsCacheHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
public class TsCacheService {
@Autowired
TsCacheHandler tsCacheHandler;
/**
* 获取一个缓存
*
* @return
*/
public static TsCacheService of() {
return SpringUtil.getBean(TsCacheService.class);
}
public void put(String key, String value) {
tsCacheHandler.put(key, value);
}
public void put(String key, String value, long seconds) {
tsCacheHandler.put(key, value, seconds);
}
public void putObj(String key, Object value, long seconds) {
tsCacheHandler.put(key, JSON.toJSONString(value), seconds);
}
public String get(String key) {
return tsCacheHandler.get(key);
}
public String get(String key, long seconds) {
return tsCacheHandler.get(key, seconds);
}
/**
* 获取一个缓存对象
*
* @param key
* @param tClass
* @param seconds =0时获取数据后会删除缓存<0时不改变原有缓存时间
* @param <T>
* @return
*/
public <T> T getObj(String key, Class<T> tClass, long seconds) {
return JSON.parseObject(get(key, seconds), tClass);
}
public void remove(String key) {
tsCacheHandler.remove(key);
}
/**
* 获取key
*
* @param prefix
* @return
*/
public Set<String> keys(String prefix) {
return tsCacheHandler.keys(prefix);
}
///////////////////////////////////////////////////////////////////////////
// 图形验证码
///////////////////////////////////////////////////////////////////////////
/**
* 验证 图片验证码
*/
public void verifyCaptcha(String value) {
String captchaKey = ServletUtil.getHeader(ServletKit.getRequest(), "captcha", "utf-8");
String cache = get(captchaKey);
if (StrUtil.isEmpty(cache) || !StrUtil.equals(cache, value)) {
throw new ApiException("验证码不正确");
}
remove(captchaKey);
}
}

View File

@@ -0,0 +1,21 @@
package com.tiesheng.util.service.cache;
import java.util.Set;
public interface TsCacheHandler {
String name();
void put(String key, String value);
void put(String key, String value, long timeout);
String get(String key);
String get(String key, long timeout);
void remove(String key);
Set<String> keys(String prefix);
}

Some files were not shown because too many files have changed in this diff Show More