diff --git a/README.md b/README.md index fa318262..3d1d61d8 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,40 @@ # zlt-microservices-platform

- License - Downloads - Downloads - Downloads - Downloads - Downloads + Downloads + Downloads + Downloads + Downloads + Downloads + + star + + + github star +

## 如果您觉得有帮助,请点右上角 "Star" 支持一下谢谢 +  +## 1. 总体架构图 +![mark](https://gitee.com/zlt2000/images/raw/master/springcloud%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E5%9B%BE.jpg) -[TOC] - -## 1. 项目介绍 +  +## 2. 功能介绍 +![mark](https://gitee.com/zlt2000/images/raw/master/ZLT-MP%E5%BE%AE%E6%9C%8D%E5%8A%A1%E5%B9%B3%E5%8F%B0%E5%8A%9F%E8%83%BD%E5%9B%BE.jpg) +  +## 3. 项目介绍 * **技术交流群** - - + - - - + +
交流一群(已满)
交流二群
交流三群
首页用户搜索
交流三群
@@ -38,9 +46,7 @@ * 账号密码:admin/admin * APM监控账号密码:admin/admin * Grafana账号:zlt/zlt123 - * txlcn事务管理器密码:admin * 任务管理账号密码:admin/123456 - * Sentinel:sentinel/sentinel * **演示环境有全方位的监控示例:日志系统 + APM系统 + GPE系统** * Gitee地址:https://gitee.com/zlt2000/microservices-platform * Github地址:https://github.com/zlt2000/microservices-platform @@ -54,19 +60,7 @@ >重构于开源项目OCP&cp:https://gitee.com/owenwangwen/open-capacity-platform   - -## 2. 项目总体架构图 -![mark](http://qiniu.zlt2000.cn/blog/20191021/IyNU3skYNIMf.jpg?imageslim) - -  - -## 3. 功能介绍 -![mark](http://qiniu.zlt2000.cn/blog/20200207/rpBztRCvwvQD.jpg?imageslim) - -  - ## 4. 模块说明 - ```lua central-platform -- 父项目,公共依赖 │ ├─zlt-business -- 业务模块一级工程 @@ -83,14 +77,16 @@ central-platform -- 父项目,公共依赖 │ │ ├─zlt-db-spring-boot-starter -- 封装数据库通用操作逻辑 │ │ ├─zlt-log-spring-boot-starter -- 封装log通用操作逻辑 │ │ ├─zlt-redis-spring-boot-starter -- 封装Redis通用操作逻辑 -│ │ ├─zlt-ribbon-spring-boot-starter -- 封装Ribbon和Feign的通用操作逻辑 +│ │ ├─zlt-loadbalancer-spring-boot-starter -- 封装Loadbalancer和Feign的通用操作逻辑 │ │ ├─zlt-sentinel-spring-boot-starter -- 封装Sentinel的通用操作逻辑 │ │ ├─zlt-swagger2-spring-boot-starter -- 封装Swagger通用操作逻辑 +│ │ ├─zlt-elasticsearch-spring-boot-starter -- 封装Elasticsearch通用操作逻辑 +│ │ ├─zlt-oss-spring-boot-starter -- 封装对象存储通用操作逻辑 +│ │ ├─zlt-zookeeper-spring-boot-starter -- 封装Zookeeper通用操作逻辑 │ ├─zlt-config -- 配置中心 │ ├─zlt-doc -- 项目文档 │ ├─zlt-gateway -- api网关一级工程 │ │ ├─sc-gateway -- spring-cloud-gateway[9900] -│ │ ├─zuul-gateway -- netflix-zuul[9900] │ ├─zlt-job -- 分布式任务调度一级工程 │ │ ├─job-admin -- 任务管理器[8081] │ │ ├─job-core -- 任务调度核心代码 @@ -114,56 +110,53 @@ central-platform -- 父项目,公共依赖 - - + +
阿里云腾讯云阿里云腾讯云
## 5. 交流反馈 * 有问题先看看 [F&Q](https://www.kancloud.cn/zlt2000/microservices-platform/981382) 中有没有相关的回答 - * 欢迎提交`ISSUS`,请写清楚问题的具体原因,重现步骤和环境(上下文) - * 项目/微服务交流请进群: * 一群:[250883130(已满)](https://shang.qq.com/wpa/qunwpa?idkey=17544199255998bda0d938fb72b08d076c40c52c9904520b76eb5eb0585da71e) - * 二群:[1041797659](https://shang.qq.com/wpa/qunwpa?idkey=41988facbc02f678942a7ee7ae03122f2ef0a10c948b3d07319f070bfb0d3a98) - -* 个人博客:https://blog.csdn.net/zlt2000 - + * 二群:[1041797659(已满)](https://shang.qq.com/wpa/qunwpa?idkey=41988facbc02f678942a7ee7ae03122f2ef0a10c948b3d07319f070bfb0d3a98) + * 三群:[512637767](https://qm.qq.com/cgi-bin/qm/qr?k=HntAHTirZwCEjF8PQpjDYkw37Zx5rJg8&jump_from=webapi) +* 个人博客:[https://zlt2000.gitee.io](https://zlt2000.gitee.io) * 个人邮箱:zltdiablo@163.com - * 个人公众号:[陶陶技术笔记](http://qiniu.zlt2000.cn/blog/20190902/M56cWjw7uNsc.png?imageslim) +* GitChat:[https://gitbook.cn/gitchat/author/5b2362320398d50d7b7ab29e](https://gitbook.cn/gitchat/author/5b2362320398d50d7b7ab29e)   ## 6. 截图(点击可大图预览) - - + + - - - + + + - - + + - - + + - - + + - - + + - - + +
首页用户搜索首页用户搜索
server_metricsapplication_metrics
日志系统日志链路
持续集成2sonar结果server_metricsapplication_metrics
skywalking首页.pngskywalking应用拓扑图skywalking首页.pngskywalking应用拓扑图
elk任务中心elk任务中心
日志中心02慢查询sql日志中心02慢查询sql
nacos-discovery应用吞吐量监控nacos-discovery应用吞吐量监控
\ No newline at end of file diff --git a/pom.xml b/pom.xml index e727893d..e2effa3b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,470 +1,512 @@ - - - 4.0.0 - com.zlt - central-platform - 3.5.0 - pom - - - 1.8 - 1.8 - UTF-8 - 8 - 2.1.1.RELEASE - 2.1.12.RELEASE - Greenwich.SR5 - 4.4 - 1.2.62 - 2.9.2 - 1.8.7 - 1.0.6 - 2.0.1 - 0.9.1 - 1.1.21 - 1.14 - 3.1.0 - 2.3.8.RELEASE - 1.1.0.RELEASE - 3.9.1 - 1.6.2 - 5.1.4 - 3.3.1 - 3.8.1 - 7.2.28 - 4.1.3 - 4.1.1 - 2.2.2 - 1.7 - 2.6 - 5.0.2.RELEASE - 1.26.7 - 1.21 - 2.11.4 - 5.2.2.RELEASE - 1.0.2 - 1.9.4 - 1.1.6.RELEASE - 2.6 - 4.0.1 - 3.2.5.RELEASE - 6.8.8 - 1.2.2 - zlt-job/**/*, zlt-register/**/*, zlt-web/**/* - openjdk:8-jre-alpine - /tmp - hub.zlt.com:8080/microservices-platform - -Djava.security.egd=file:/dev/./urandom - -Xms128m -Xmx128m - - - - - org.apache.commons - commons-lang3 - - - org.apache.commons - commons-collections4 - ${commons-collections4.version} - - - commons-beanutils - commons-beanutils - ${commons-beanutils.version} - - - com.alibaba - fastjson - ${fastjson.version} - - - org.projectlombok - lombok - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - - com.zlt - zlt-common-spring-boot-starter - ${project.version} - - - com.zlt - zlt-db-spring-boot-starter - ${project.version} - - - com.zlt - zlt-log-spring-boot-starter - ${project.version} - - - com.zlt - zlt-redis-spring-boot-starter - ${project.version} - - - com.zlt - zlt-swagger2-spring-boot-starter - ${project.version} - - - com.zlt - zlt-ribbon-spring-boot-starter - ${project.version} - - - com.zlt - zlt-auth-client-spring-boot-starter - ${project.version} - - - com.zlt - zlt-sentinel-spring-boot-starter - ${project.version} - - - com.zlt - zlt-config - ${project.version} - - - com.zlt - zlt-common-core - ${project.version} - - - com.zlt - zlt-elasticsearch-spring-boot-starter - ${project.version} - - - io.jsonwebtoken - jjwt - ${jjwt.version} - - - - com.alibaba - druid-spring-boot-starter - ${druid-starter} - - - - com.baomidou - mybatis-plus-boot-starter - ${mybatis-plus-boot-starter.version} - - - com.baomidou - mybatis-plus-extension - ${mybatis-plus-boot-starter.version} - - - - org.springframework.security.oauth - spring-security-oauth2 - ${security-oauth2.version} - - - org.springframework.security - spring-security-jwt - ${security-jwt.version} - - - - org.redisson - redisson - ${redisson.version} - - - - io.springfox - springfox-swagger2 - ${swagger.version} - - - io.springfox - springfox-bean-validators - ${swagger.version} - - - io.springfox - springfox-swagger-ui - ${swagger.version} - - - com.github.xiaoymin - swagger-bootstrap-ui - ${swagger.ui.version} - - - com.zyplayer - swagger-mg-ui - ${swagger.mg.ui.version} - - - com.github.zlt2000 - swagger-butler-core - ${swagger.butler.version} - - - - com.github.whvcse - easy-captcha - ${easyCaptcha.version} - - - - cn.hutool - hutool-all - ${hutool.version} - - - com.aliyun.oss - aliyun-sdk-oss - ${aliyun-sdk-oss} - - - - com.qiniu - qiniu-java-sdk - ${qiniu-java-sdk} - - - org.apache.poi - poi - ${poi.version} - - - org.apache.poi - poi-ooxml - ${poi.version} - - - - cn.afterturn - easypoi-base - ${easypoi.version} - - - com.baomidou - mybatis-plus - ${mybatis-plus-boot-starter.version} - - - de.codecentric - spring-boot-admin-starter-server - ${spring-boot-admin.version} - - - org.apache.velocity - velocity - ${velocity.version} - - - org.apache.commons - commons-configuration2 - ${commons-configuration2.version} - - - com.codingapi.txlcn - txlcn-tm - ${txlcn.version} - - - com.codingapi.txlcn - txlcn-tc - ${txlcn.version} - - - com.codingapi.txlcn - txlcn-txmsg-netty - ${txlcn.version} - - - io.shardingsphere - sharding-jdbc-spring-boot-starter - ${sharding-sphere.version} - - - com.github.tobato - fastdfs-client - ${fastdfs-client.version} - - - com.zlt - search-client - ${project.version} - - - eu.bitwalker - UserAgentUtils - ${userAgent.version} - - - com.alibaba - transmittable-thread-local - ${transmittable.version} - - - io.lettuce - lettuce-core - ${lettuce.version} - - - com.nepxion - banner - ${banner.version} - - - org.springframework.social - spring-social-security - ${spring-social-security.version} - - - commons-io - commons-io - ${commons-io.version} - - - javax.servlet - javax.servlet-api - ${servlet-api.version} - - - org.springframework.data - spring-data-elasticsearch - ${spring-data-elasticsearch.version} - - - org.elasticsearch.client - elasticsearch-rest-high-level-client - ${elasticsearch.version} - - - org.elasticsearch.client - elasticsearch-rest-client - ${elasticsearch.version} - - - org.elasticsearch - elasticsearch - ${elasticsearch.version} - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot-dependencies.version} - pom - import - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud-dependencies.version} - pom - import - - - com.alibaba.cloud - spring-cloud-alibaba-dependencies - ${spring-cloud-alibaba-dependencies.version} - pom - import - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring-boot-dependencies.version} - - - com.spotify - docker-maven-plugin - ${docker-maven-plugin.version} - - - - - - - - aliyun-repos - https://maven.aliyun.com/repository/public - - false - - - - - - releases - releases Repository - http://192.168.28.138:8081/repository/maven-releases/ - - - - snapshots - http://192.168.28.138:8081/repository/maven-snapshots/ - true - - - - - - zlt-commons - - zlt-uaa - - zlt-gateway - - zlt-business - - zlt-web - - zlt-job - - zlt-monitor - - zlt-transaction - - zlt-config - - zlt-demo - - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - - LeTao Zhu - zltdiablo@163.com - https://github.com/zlt2000 - https://blog.csdn.net/zlt2000 - - + + + 4.0.0 + com.zlt + central-platform + 5.4.0 + pom + + + 1.8 + 1.8 + UTF-8 + 8 + 2021.1 + 2.5.14 + 2020.0.5 + 4.4 + 2.0.1 + 0.9.1 + 1.2.6 + 1.14 + 3.1.0 + 2.3.8.RELEASE + 1.1.0.RELEASE + 3.16.1 + 1.6.2 + 5.7.20 + 3.5.1 + 3.8.1 + 7.2.28 + 4.1.3 + 4.1.1 + 2.5.6 + 1.7 + 2.8 + 5.0.2.RELEASE + 1.27.2 + 1.21 + 2.12.1 + 1.0.2 + 1.9.4 + 1.1.6.RELEASE + 2.7 + 4.0.1 + 4.2.3 + 7.13.4 + 2.0.5 + 6.2.0.Final + 2.7.8 + 5.1.0 + 2.12.4 + 1.10 + 2.2.9.RELEASE + 1.12.40 + 2.2.5.RELEASE + 1.2.2 + zlt-job/**/*, zlt-register/**/*, zlt-web/**/* + openjdk:8-jre-alpine + /tmp + hub.zlt.com:8080/microservices-platform + -Djava.security.egd=file:/dev/./urandom + -Xms128m -Xmx128m + + + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-collections4 + ${commons-collections4.version} + + + commons-beanutils + commons-beanutils + ${commons-beanutils.version} + + + org.projectlombok + lombok + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + + + + com.zlt + zlt-common-spring-boot-starter + ${project.version} + + + com.zlt + zlt-db-spring-boot-starter + ${project.version} + + + com.zlt + zlt-log-spring-boot-starter + ${project.version} + + + com.zlt + zlt-redis-spring-boot-starter + ${project.version} + + + com.zlt + zlt-swagger2-spring-boot-starter + ${project.version} + + + com.zlt + zlt-loadbalancer-spring-boot-starter + ${project.version} + + + com.zlt + zlt-auth-client-spring-boot-starter + ${project.version} + + + com.zlt + zlt-sentinel-spring-boot-starter + ${project.version} + + + com.zlt + zlt-config + ${project.version} + + + com.zlt + zlt-common-core + ${project.version} + + + com.zlt + zlt-elasticsearch-spring-boot-starter + ${project.version} + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + com.alibaba + druid-spring-boot-starter + ${druid-starter} + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus-boot-starter.version} + + + com.baomidou + mybatis-plus-extension + ${mybatis-plus-boot-starter.version} + + + com.baomidou + mybatis-plus + ${mybatis-plus-boot-starter.version} + + + + org.springframework.security.oauth + spring-security-oauth2 + ${security-oauth2.version} + + + org.springframework.security + spring-security-jwt + ${security-jwt.version} + + + + org.redisson + redisson-spring-boot-starter + ${redisson-starter.version} + + + + com.github.zlt2000 + swagger-butler-core + ${swagger.butler.version} + + + + com.github.whvcse + easy-captcha + ${easyCaptcha.version} + + + + cn.hutool + hutool-all + ${hutool.version} + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun-sdk-oss} + + + + com.qiniu + qiniu-java-sdk + ${qiniu-java-sdk} + + + org.apache.poi + poi + ${poi.version} + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + cn.afterturn + easypoi-base + ${easypoi.version} + + + de.codecentric + spring-boot-admin-starter-server + ${spring-boot-admin.version} + + + org.apache.velocity + velocity + ${velocity.version} + + + org.apache.commons + commons-configuration2 + ${commons-configuration2.version} + + + com.codingapi.txlcn + txlcn-tm + ${txlcn.version} + + + com.codingapi.txlcn + txlcn-tc + ${txlcn.version} + + + com.codingapi.txlcn + txlcn-txmsg-netty + ${txlcn.version} + + + io.shardingsphere + sharding-jdbc-spring-boot-starter + ${sharding-sphere.version} + + + com.github.tobato + fastdfs-client + ${fastdfs-client.version} + + + com.zlt + search-client + ${project.version} + + + eu.bitwalker + UserAgentUtils + ${userAgent.version} + + + com.alibaba + transmittable-thread-local + ${transmittable.version} + + + com.nepxion + banner + ${banner.version} + + + org.springframework.social + spring-social-security + ${spring-social-security.version} + + + commons-io + commons-io + ${commons-io.version} + + + javax.servlet + javax.servlet-api + ${servlet-api.version} + + + org.springframework.data + spring-data-elasticsearch + ${spring-data-elasticsearch.version} + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + ${elasticsearch.version} + + + org.elasticsearch + elasticsearch-x-content + ${elasticsearch.version} + + + org.elasticsearch.client + elasticsearch-rest-client + ${elasticsearch.version} + + + org.elasticsearch + elasticsearch + ${elasticsearch.version} + + + com.github.xiaoymin + knife4j-spring-boot-starter + ${knife4j.version} + + + com.github.xiaoymin + knife4j-micro-spring-boot-starter + ${knife4j.version} + + + org.hibernate + hibernate-validator + ${hibernate-validator.version} + + + org.apache.dubbo + dubbo + ${dubbo.version} + + + com.zlt + zlt-oss-spring-boot-starter + ${project.version} + + + com.zlt + zlt-zookeeper-spring-boot-starter + ${project.version} + + + org.apache.curator + curator-recipes + ${curator.version} + + + org.apache.curator + curator-framework + ${curator.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind.version} + + + org.springframework.cloud + spring-cloud-starter-oauth2 + ${spring-cloud-starter-oauth2.version} + + + com.amazonaws + aws-java-sdk-s3 + ${aws-java-sdk-s3.version} + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + ${zuul.version} + + + commons-configuration + commons-configuration + ${commons-configuration.version} + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot-dependencies.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + pom + import + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba-dependencies.version} + pom + import + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot-dependencies.version} + + + com.spotify + docker-maven-plugin + ${docker-maven-plugin.version} + + + + + + + + aliyun-repos + https://maven.aliyun.com/repository/public + + false + + + + + + releases + releases Repository + http://192.168.28.138:8081/repository/maven-releases/ + + + + snapshots + http://192.168.28.138:8081/repository/maven-snapshots/ + true + + + + + + zlt-commons + + zlt-uaa + + zlt-gateway + + zlt-business + + zlt-web + + zlt-job + + zlt-monitor + + zlt-transaction + + zlt-config + + zlt-demo + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + LeTao Zhu + zltdiablo@163.com + https://github.com/zlt2000 + https://blog.csdn.net/zlt2000 + + \ No newline at end of file diff --git a/zlt-business/code-generator/pom.xml b/zlt-business/code-generator/pom.xml index afe82e55..276283ad 100644 --- a/zlt-business/code-generator/pom.xml +++ b/zlt-business/code-generator/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-business - 3.5.0 + 5.4.0 code-generator @@ -59,6 +59,10 @@ io.micrometer micrometer-registry-prometheus + + commons-configuration + commons-configuration + diff --git a/zlt-business/file-center/pom.xml b/zlt-business/file-center/pom.xml index 60b50794..38a2e06f 100644 --- a/zlt-business/file-center/pom.xml +++ b/zlt-business/file-center/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-business - 3.5.0 + 5.4.0 file-center 文件中心 @@ -24,12 +24,16 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter com.zlt zlt-sentinel-spring-boot-starter + + com.zlt + zlt-oss-spring-boot-starter + com.alibaba.cloud @@ -66,20 +70,20 @@ micrometer-registry-prometheus - - - com.aliyun.oss - aliyun-sdk-oss - - - - com.qiniu - qiniu-java-sdk - - + + + + com.amazonaws + aws-java-sdk-s3 + + + + org.hibernate + hibernate-validator diff --git a/zlt-business/file-center/src/main/java/com/central/FileCenterApp.java b/zlt-business/file-center/src/main/java/com/central/FileCenterApp.java index 487e94e5..a0a9a6cf 100644 --- a/zlt-business/file-center/src/main/java/com/central/FileCenterApp.java +++ b/zlt-business/file-center/src/main/java/com/central/FileCenterApp.java @@ -1,10 +1,8 @@ package com.central; -import com.central.common.ribbon.annotation.EnableFeignInterceptor; -import com.central.file.properties.FileServerProperties; +import com.central.common.lb.annotation.EnableFeignInterceptor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -13,7 +11,6 @@ * @author 作者 owen E-mail: 624191343@qq.com */ @EnableDiscoveryClient -@EnableConfigurationProperties(FileServerProperties.class) @EnableFeignClients @EnableFeignInterceptor @SpringBootApplication diff --git a/zlt-business/file-center/src/main/java/com/central/file/config/AliyunOSSAutoConfigure.java b/zlt-business/file-center/src/main/java/com/central/file/config/AliyunOSSAutoConfigure.java deleted file mode 100644 index 9531e08a..00000000 --- a/zlt-business/file-center/src/main/java/com/central/file/config/AliyunOSSAutoConfigure.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.central.file.config; - -import com.aliyun.oss.common.auth.DefaultCredentialProvider; -import com.central.file.model.FileInfo; -import com.central.file.properties.FileServerProperties; -import com.central.file.service.impl.AbstractIFileService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.aliyun.oss.OSSClient; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -/** - * 阿里云配置 - * - * @author 作者 owen E-mail: 624191343@qq.com - */ -@Configuration -@ConditionalOnProperty(name = "zlt.file-server.type", havingValue = "aliyun") -public class AliyunOSSAutoConfigure { - @Autowired - private FileServerProperties fileProperties; - - /** - * 阿里云文件存储client - * 只有配置了aliyun.oss.access-key才可以使用 - */ - @Bean - public OSSClient ossClient() { - OSSClient ossClient = new OSSClient(fileProperties.getOss().getEndpoint() - , new DefaultCredentialProvider(fileProperties.getOss().getAccessKey(), fileProperties.getOss().getAccessKeySecret()) - , null); - return ossClient; - } - - @Service - public class AliyunOssServiceImpl extends AbstractIFileService { - @Autowired - private OSSClient ossClient; - - @Override - protected String fileType() { - return fileProperties.getType(); - } - - @Override - protected void uploadFile(MultipartFile file, FileInfo fileInfo) throws Exception { - ossClient.putObject(fileProperties.getOss().getBucketName(), fileInfo.getName(), file.getInputStream()); - fileInfo.setUrl(fileProperties.getOss().getDomain() + "/" + fileInfo.getName()); - } - - @Override - protected boolean deleteFile(FileInfo fileInfo) { - ossClient.deleteObject(fileProperties.getOss().getBucketName(), fileInfo.getName()); - return true; - } - } -} diff --git a/zlt-business/file-center/src/main/java/com/central/file/config/FastdfsAutoConfigure.java b/zlt-business/file-center/src/main/java/com/central/file/config/FastdfsAutoConfigure.java deleted file mode 100644 index e845d6f5..00000000 --- a/zlt-business/file-center/src/main/java/com/central/file/config/FastdfsAutoConfigure.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.central.file.config; - -import cn.hutool.core.util.StrUtil; -import com.central.file.model.FileInfo; -import com.central.file.properties.FileServerProperties; -import com.central.file.service.impl.AbstractIFileService; -import com.github.tobato.fastdfs.domain.fdfs.StorePath; -import com.github.tobato.fastdfs.service.FastFileStorageClient; -import org.apache.commons.io.FilenameUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -/** - * FastDFS配置 - * - * @author zlt - */ -@Configuration -@ConditionalOnProperty(name = "zlt.file-server.type", havingValue = "fastdfs") -public class FastdfsAutoConfigure { - @Autowired - private FileServerProperties fileProperties; - - @Service - public class FastdfsServiceImpl extends AbstractIFileService { - @Autowired - private FastFileStorageClient storageClient; - - @Override - protected String fileType() { - return fileProperties.getType(); - } - - @Override - protected void uploadFile(MultipartFile file, FileInfo fileInfo) throws Exception { - StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(), FilenameUtils.getExtension(file.getOriginalFilename()), null); - fileInfo.setUrl("http://" + fileProperties.getFdfs().getWebUrl() + "/" + storePath.getFullPath()); - fileInfo.setPath(storePath.getFullPath()); - } - - @Override - protected boolean deleteFile(FileInfo fileInfo) { - if (fileInfo != null && StrUtil.isNotEmpty(fileInfo.getPath())) { - StorePath storePath = StorePath.parseFromUrl(fileInfo.getPath()); - storageClient.deleteFile(storePath.getGroup(), storePath.getPath()); - } - return true; - } - } -} diff --git a/zlt-business/file-center/src/main/java/com/central/file/config/QiniuOSSAutoConfigure.java b/zlt-business/file-center/src/main/java/com/central/file/config/QiniuOSSAutoConfigure.java deleted file mode 100644 index 849f928a..00000000 --- a/zlt-business/file-center/src/main/java/com/central/file/config/QiniuOSSAutoConfigure.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.central.file.config; - -import com.central.file.model.FileInfo; -import com.central.file.properties.FileServerProperties; -import com.central.file.service.impl.AbstractIFileService; -import com.qiniu.common.QiniuException; -import com.qiniu.http.Response; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.qiniu.common.Zone; -import com.qiniu.storage.BucketManager; -import com.qiniu.storage.UploadManager; -import com.qiniu.util.Auth; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -/** - * 七牛云配置 - * - * @author 作者 owen E-mail: 624191343@qq.com - */ -@Configuration -@ConditionalOnProperty(name = "zlt.file-server.type", havingValue = "qiniu") -public class QiniuOSSAutoConfigure { - @Autowired - private FileServerProperties fileProperties; - - /** - * 华东机房 - */ - @Bean - public com.qiniu.storage.Configuration qiniuConfig() { - return new com.qiniu.storage.Configuration(Zone.zone2()); - } - - /** - * 构建一个七牛上传工具实例 - */ - @Bean - public UploadManager uploadManager() { - return new UploadManager(qiniuConfig()); - } - - /** - * 认证信息实例 - * - * @return - */ - @Bean - public Auth auth() { - return Auth.create(fileProperties.getOss().getAccessKey(), fileProperties.getOss().getAccessKeySecret()); - } - - /** - * 构建七牛空间管理实例 - */ - @Bean - public BucketManager bucketManager() { - return new BucketManager(auth(), qiniuConfig()); - } - - @Service - public class QiniuOssServiceImpl extends AbstractIFileService { - @Autowired - private UploadManager uploadManager; - @Autowired - private BucketManager bucketManager; - @Autowired - private Auth auth; - - @Override - protected String fileType() { - return fileProperties.getType(); - } - - @Override - protected void uploadFile(MultipartFile file, FileInfo fileInfo) throws Exception { - // 调用put方法上传 - uploadManager.put(file.getBytes(), fileInfo.getName(), auth.uploadToken(fileProperties.getOss().getBucketName())); - fileInfo.setUrl(fileProperties.getOss().getEndpoint() + "/" + fileInfo.getName()); - fileInfo.setPath(fileProperties.getOss().getEndpoint() + "/" + fileInfo.getName()); - } - - @Override - protected boolean deleteFile(FileInfo fileInfo) { - try { - Response response = bucketManager.delete(fileProperties.getOss().getBucketName(), fileInfo.getPath()); - int retry = 0; - while (response.needRetry() && retry++ < 3) { - response = bucketManager.delete(fileProperties.getOss().getBucketName(), fileInfo.getPath()); - } - } catch (QiniuException e) { - return false; - } - return true; - } - } -} diff --git a/zlt-business/file-center/src/main/java/com/central/file/model/FileInfo.java b/zlt-business/file-center/src/main/java/com/central/file/model/FileInfo.java index a6622a7b..2e7e300b 100644 --- a/zlt-business/file-center/src/main/java/com/central/file/model/FileInfo.java +++ b/zlt-business/file-center/src/main/java/com/central/file/model/FileInfo.java @@ -56,9 +56,4 @@ public class FileInfo extends Model { private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; - - @Override - protected Serializable pkVal() { - return this.id; - } } diff --git a/zlt-business/file-center/src/main/java/com/central/file/properties/FileServerProperties.java b/zlt-business/file-center/src/main/java/com/central/file/properties/FileServerProperties.java deleted file mode 100644 index dca0c508..00000000 --- a/zlt-business/file-center/src/main/java/com/central/file/properties/FileServerProperties.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.central.file.properties; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.context.config.annotation.RefreshScope; - -/** - * @author zlt - */ -@Setter -@Getter -@ConfigurationProperties(prefix = "zlt.file-server") -@RefreshScope -public class FileServerProperties { - /** - * 为以下3个值,指定不同的自动化配置 - * qiniu:七牛oss - * aliyun:阿里云oss - * fastdfs:本地部署的fastDFS - */ - private String type; - - /** - * oss配置 - */ - OssProperties oss = new OssProperties(); - - /** - * fastDFS配置 - */ - FdfsProperties fdfs = new FdfsProperties(); -} diff --git a/zlt-business/file-center/src/main/java/com/central/file/properties/OssProperties.java b/zlt-business/file-center/src/main/java/com/central/file/properties/OssProperties.java deleted file mode 100644 index f3300b3b..00000000 --- a/zlt-business/file-center/src/main/java/com/central/file/properties/OssProperties.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.central.file.properties; - -import lombok.Getter; -import lombok.Setter; - -/** - * @author zlt - */ -@Setter -@Getter -public class OssProperties { - /** - * 密钥key - */ - private String accessKey; - /** - * 密钥密码 - */ - private String accessKeySecret; - /** - * 端点 - */ - private String endpoint; - /** - * bucket名称 - */ - private String bucketName; - /** - * 说明 - */ - private String domain; -} diff --git a/zlt-business/file-center/src/main/java/com/central/file/service/IFileService.java b/zlt-business/file-center/src/main/java/com/central/file/service/IFileService.java index 6f97a861..2e44595f 100644 --- a/zlt-business/file-center/src/main/java/com/central/file/service/IFileService.java +++ b/zlt-business/file-center/src/main/java/com/central/file/service/IFileService.java @@ -1,5 +1,6 @@ package com.central.file.service; +import java.io.OutputStream; import java.util.Map; import com.baomidou.mybatisplus.extension.service.IService; @@ -19,4 +20,6 @@ public interface IFileService extends IService { PageResult findList(Map params); void delete(String id); + + void out(String id, OutputStream os); } diff --git a/zlt-business/file-center/src/main/java/com/central/file/service/impl/AbstractIFileService.java b/zlt-business/file-center/src/main/java/com/central/file/service/impl/AbstractIFileService.java index caf622c8..93e38e1e 100644 --- a/zlt-business/file-center/src/main/java/com/central/file/service/impl/AbstractIFileService.java +++ b/zlt-business/file-center/src/main/java/com/central/file/service/impl/AbstractIFileService.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.central.oss.model.ObjectInfo; import org.apache.commons.collections4.MapUtils; import org.springframework.web.multipart.MultipartFile; @@ -27,16 +28,14 @@ public abstract class AbstractIFileService extends ServiceImpl findList(Map params) { diff --git a/zlt-business/file-center/src/main/java/com/central/file/service/impl/FastdfsService.java b/zlt-business/file-center/src/main/java/com/central/file/service/impl/FastdfsService.java new file mode 100644 index 00000000..fe54278e --- /dev/null +++ b/zlt-business/file-center/src/main/java/com/central/file/service/impl/FastdfsService.java @@ -0,0 +1,44 @@ +package com.central.file.service.impl; + +import com.central.oss.model.ObjectInfo; +import com.central.oss.properties.FileServerProperties; +import com.central.oss.template.FdfsTemplate; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.OutputStream; + +/** + * @author zlt + * @date 2021/2/13 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Service +@ConditionalOnProperty(prefix = com.central.oss.properties.FileServerProperties.PREFIX, name = "type", havingValue = FileServerProperties.TYPE_FDFS) +public class FastdfsService extends AbstractIFileService { + @Resource + private FdfsTemplate fdfsTemplate; + + @Override + protected String fileType() { + return FileServerProperties.TYPE_FDFS; + } + + @Override + protected ObjectInfo uploadFile(MultipartFile file) { + return fdfsTemplate.upload(file); + } + + @Override + protected void deleteFile(String objectPath) { + fdfsTemplate.delete(objectPath); + } + + @Override + public void out(String id, OutputStream os) { + } +} diff --git a/zlt-business/file-center/src/main/java/com/central/file/service/impl/S3Service.java b/zlt-business/file-center/src/main/java/com/central/file/service/impl/S3Service.java new file mode 100644 index 00000000..f87679d6 --- /dev/null +++ b/zlt-business/file-center/src/main/java/com/central/file/service/impl/S3Service.java @@ -0,0 +1,74 @@ +package com.central.file.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.central.common.constant.CommonConstant; +import com.central.file.model.FileInfo; +import com.central.oss.model.ObjectInfo; +import com.central.oss.properties.FileServerProperties; +import com.central.oss.template.S3Template; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.OutputStream; + +/** + * @author zlt + * @date 2021/2/13 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Service +@ConditionalOnProperty(prefix = com.central.oss.properties.FileServerProperties.PREFIX, name = "type", havingValue = FileServerProperties.TYPE_S3) +public class S3Service extends AbstractIFileService { + @Resource + private S3Template s3Template; + + @Override + protected String fileType() { + return FileServerProperties.TYPE_S3; + } + + @Override + protected ObjectInfo uploadFile(MultipartFile file) { + return s3Template.upload(file); + } + + @Override + protected void deleteFile(String objectPath) { + S3Object s3Object = parsePath(objectPath); + s3Template.delete(s3Object.bucketName, s3Object.objectName); + } + + @Override + public void out(String id, OutputStream os) { + FileInfo fileInfo = baseMapper.selectById(id); + if (fileInfo != null) { + S3Object s3Object = parsePath(fileInfo.getPath()); + s3Template.out(s3Object.bucketName, s3Object.objectName, os); + } + } + + @Setter + @Getter + private class S3Object { + private String bucketName; + private String objectName; + } + + private S3Object parsePath(String path) { + S3Object s3Object = new S3Object(); + if (StrUtil.isNotEmpty(path)) { + int splitIndex = path.lastIndexOf(CommonConstant.PATH_SPLIT); + if (splitIndex != -1) { + s3Object.bucketName = path.substring(0, splitIndex); + s3Object.objectName = path.substring(splitIndex + 1); + } + } + return s3Object; + } +} diff --git a/zlt-business/file-center/src/main/java/com/central/file/utils/FileUtil.java b/zlt-business/file-center/src/main/java/com/central/file/utils/FileUtil.java index 6f534561..f1da64e1 100644 --- a/zlt-business/file-center/src/main/java/com/central/file/utils/FileUtil.java +++ b/zlt-business/file-center/src/main/java/com/central/file/utils/FileUtil.java @@ -22,10 +22,8 @@ private FileUtil() { throw new IllegalStateException("Utility class"); } - public static FileInfo getFileInfo(MultipartFile file) throws Exception { - //String md5 = fileMd5(file.getInputStream()); + public static FileInfo getFileInfo(MultipartFile file) { FileInfo fileInfo = new FileInfo(); - // 将文件的md5设置为文件表的id fileInfo.setId(IdUtil.fastSimpleUUID()); fileInfo.setName(file.getOriginalFilename()); fileInfo.setContentType(file.getContentType()); diff --git a/zlt-business/file-center/src/main/resources/application.yml b/zlt-business/file-center/src/main/resources/application.yml index e4c7e8c3..839effbe 100644 --- a/zlt-business/file-center/src/main/resources/application.yml +++ b/zlt-business/file-center/src/main/resources/application.yml @@ -15,16 +15,14 @@ mybatis-plus: zlt: file-server: - type: fastdfs - fdfs: - web-url: ${zlt.fdfs.web-url} - #oss配置 - #oss: - # access-key: tpi8mObnfzZi4ggBX8Bw7zydjoTQ0WeML3RkPKsX - # accessKeySecret: HZBXmSyUTy-haYp0KbBTtsil-GoKjVS2kDKT8Yow - # endpoint: http://pkqtmn0p1.bkt.clouddn.com - # bucketName: public-oss - # domain: + type: s3 + #fdfs: + # web-url: ${zlt.fdfs.web-url} + s3: + access-key: ${zlt.s3.access-key} + accessKeySecret: ${zlt.s3.accessKeySecret} + endpoint: ${zlt.s3.endpoint} + bucketName: ${zlt.s3.bucketName} swagger: enabled: true title: 文件中心 @@ -36,7 +34,7 @@ zlt: enable: true #fastDFS配置 -fdfs: - soTimeout: 1500 - connectTimeout: 600 - trackerList: ${zlt.fdfs.trackerList} \ No newline at end of file +#fdfs: +# soTimeout: 1500 +# connectTimeout: 600 +# trackerList: ${zlt.fdfs.trackerList} \ No newline at end of file diff --git a/zlt-business/pom.xml b/zlt-business/pom.xml index 25a8ab00..a5ec9e4f 100644 --- a/zlt-business/pom.xml +++ b/zlt-business/pom.xml @@ -4,7 +4,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-business 业务中心 diff --git a/zlt-business/search-center/pom.xml b/zlt-business/search-center/pom.xml index 242c4d2f..6a62964c 100644 --- a/zlt-business/search-center/pom.xml +++ b/zlt-business/search-center/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-business - 3.5.0 + 5.4.0 search-center 搜索中心 diff --git a/zlt-business/search-center/search-client/pom.xml b/zlt-business/search-center/search-client/pom.xml index 67779c85..30188b40 100644 --- a/zlt-business/search-center/search-client/pom.xml +++ b/zlt-business/search-center/search-client/pom.xml @@ -4,7 +4,7 @@ com.zlt search-center - 3.5.0 + 5.4.0 search-client 搜索中心客户端 @@ -13,7 +13,7 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter com.zlt diff --git a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/SearchService.java b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/SearchService.java index 1f8d3d30..b094f5c2 100644 --- a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/SearchService.java +++ b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/SearchService.java @@ -1,10 +1,10 @@ package com.central.search.client.feign; -import com.alibaba.fastjson.JSONObject; import com.central.common.constant.ServiceNameConstants; import com.central.common.model.PageResult; import com.central.search.client.feign.fallback.SearchServiceFallbackFactory; import com.central.search.model.SearchDto; +import com.fasterxml.jackson.databind.JsonNode; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; @@ -19,5 +19,5 @@ public interface SearchService { * @param searchDto 搜索Dto */ @PostMapping(value = "/search/{indexName}") - PageResult strQuery(@PathVariable("indexName") String indexName, @RequestBody SearchDto searchDto); + PageResult strQuery(@PathVariable("indexName") String indexName, @RequestBody SearchDto searchDto); } diff --git a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/fallback/AggregationServiceFallbackFactory.java b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/fallback/AggregationServiceFallbackFactory.java index 86c2534e..bc3b9ff7 100644 --- a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/fallback/AggregationServiceFallbackFactory.java +++ b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/fallback/AggregationServiceFallbackFactory.java @@ -2,7 +2,7 @@ import cn.hutool.core.map.MapUtil; import com.central.search.client.feign.AggregationService; -import feign.hystrix.FallbackFactory; +import org.springframework.cloud.openfeign.FallbackFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/fallback/SearchServiceFallbackFactory.java b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/fallback/SearchServiceFallbackFactory.java index 7623ef26..ad2cb5b3 100644 --- a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/fallback/SearchServiceFallbackFactory.java +++ b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/feign/fallback/SearchServiceFallbackFactory.java @@ -1,9 +1,9 @@ package com.central.search.client.feign.fallback; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; import com.central.search.client.feign.SearchService; -import feign.hystrix.FallbackFactory; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.cloud.openfeign.FallbackFactory; import lombok.extern.slf4j.Slf4j; /** @@ -17,7 +17,7 @@ public class SearchServiceFallbackFactory implements FallbackFactory { log.error("通过索引{}搜索异常:{}", indexName, throwable); - return PageResult.builder().build(); + return PageResult.builder().build(); }; } } diff --git a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/service/IQueryService.java b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/service/IQueryService.java index dab65cde..1a282cac 100644 --- a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/service/IQueryService.java +++ b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/service/IQueryService.java @@ -1,9 +1,9 @@ package com.central.search.client.service; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; import com.central.search.model.LogicDelDto; import com.central.search.model.SearchDto; +import com.fasterxml.jackson.databind.JsonNode; import java.util.Map; @@ -19,7 +19,7 @@ public interface IQueryService { * @param indexName 索引名 * @param searchDto 搜索Dto */ - PageResult strQuery(String indexName, SearchDto searchDto); + PageResult strQuery(String indexName, SearchDto searchDto); /** * 查询文档列表 @@ -27,7 +27,7 @@ public interface IQueryService { * @param searchDto 搜索Dto * @param logicDelDto 逻辑删除Dto */ - PageResult strQuery(String indexName, SearchDto searchDto, LogicDelDto logicDelDto); + PageResult strQuery(String indexName, SearchDto searchDto, LogicDelDto logicDelDto); /** * 访问统计聚合查询 diff --git a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/service/impl/QueryServiceImpl.java b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/service/impl/QueryServiceImpl.java index d4798919..221faafe 100644 --- a/zlt-business/search-center/search-client/src/main/java/com/central/search/client/service/impl/QueryServiceImpl.java +++ b/zlt-business/search-center/search-client/src/main/java/com/central/search/client/service/impl/QueryServiceImpl.java @@ -1,13 +1,13 @@ package com.central.search.client.service.impl; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; import com.central.search.client.feign.AggregationService; import com.central.search.client.feign.SearchService; import com.central.search.client.service.IQueryService; import com.central.search.model.LogicDelDto; import com.central.search.model.SearchDto; +import com.fasterxml.jackson.databind.JsonNode; import javax.annotation.Resource; import java.util.Map; @@ -26,12 +26,12 @@ public class QueryServiceImpl implements IQueryService { private AggregationService aggregationService; @Override - public PageResult strQuery(String indexName, SearchDto searchDto) { + public PageResult strQuery(String indexName, SearchDto searchDto) { return strQuery(indexName, searchDto, null); } @Override - public PageResult strQuery(String indexName, SearchDto searchDto, LogicDelDto logicDelDto) { + public PageResult strQuery(String indexName, SearchDto searchDto, LogicDelDto logicDelDto) { setLogicDelQueryStr(searchDto, logicDelDto); return searchService.strQuery(indexName, searchDto); } diff --git a/zlt-business/search-center/search-client/src/main/java/com/central/search/model/SearchDto.java b/zlt-business/search-center/search-client/src/main/java/com/central/search/model/SearchDto.java index 71f24ebe..e1a06530 100644 --- a/zlt-business/search-center/search-client/src/main/java/com/central/search/model/SearchDto.java +++ b/zlt-business/search-center/search-client/src/main/java/com/central/search/model/SearchDto.java @@ -2,8 +2,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; import java.io.Serializable; @@ -31,6 +29,10 @@ public class SearchDto implements Serializable { * 排序字段 */ private String sortCol; + /** + * 排序顺序 + */ + private String sortOrder = "DESC"; /** * 是否显示高亮 */ diff --git a/zlt-business/search-center/search-server/pom.xml b/zlt-business/search-center/search-server/pom.xml index f258d2ee..8a2330d7 100644 --- a/zlt-business/search-center/search-server/pom.xml +++ b/zlt-business/search-center/search-server/pom.xml @@ -4,7 +4,7 @@ com.zlt search-center - 3.5.0 + 5.4.0 search-server 搜索中心服务端 @@ -60,6 +60,11 @@ io.micrometer micrometer-registry-prometheus + + + org.hibernate + hibernate-validator + diff --git a/zlt-business/search-center/search-server/src/main/java/com/central/admin/service/impl/IndexServiceImpl.java b/zlt-business/search-center/search-server/src/main/java/com/central/admin/service/impl/IndexServiceImpl.java index 9c15eabe..f4ad351f 100644 --- a/zlt-business/search-center/search-server/src/main/java/com/central/admin/service/impl/IndexServiceImpl.java +++ b/zlt-business/search-center/search-server/src/main/java/com/central/admin/service/impl/IndexServiceImpl.java @@ -1,51 +1,51 @@ package com.central.admin.service.impl; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSONObject; -import com.carrotsearch.hppc.cursors.ObjectCursor; import com.central.admin.model.IndexDto; import com.central.admin.service.IIndexService; import com.central.common.model.PageResult; +import com.central.common.utils.JsonUtil; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.util.EntityUtils; -import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; -import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.indices.CreateIndexRequest; +import org.elasticsearch.client.indices.CreateIndexResponse; +import org.elasticsearch.client.indices.GetIndexRequest; +import org.elasticsearch.client.indices.GetIndexResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; -import org.elasticsearch.action.admin.indices.get.GetIndexRequest; -import org.elasticsearch.action.admin.indices.get.GetIndexResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; -import org.elasticsearch.cluster.metadata.AliasMetaData; -import org.elasticsearch.cluster.metadata.MappingMetaData; -import org.elasticsearch.common.collect.ImmutableOpenMap; +import org.elasticsearch.cluster.metadata.AliasMetadata; +import org.elasticsearch.cluster.metadata.MappingMetadata; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.stereotype.Service; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 索引 * * @author zlt * @date 2019/4/23 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Service public class IndexServiceImpl implements IIndexService { - @Autowired - private ElasticsearchRestTemplate elasticsearchRestTemplate; - private ObjectMapper mapper = new ObjectMapper(); + private final RestHighLevelClient client; + + public IndexServiceImpl(RestHighLevelClient client) { + this.client = client; + } + @Override public boolean create(IndexDto indexDto) throws IOException { CreateIndexRequest request = new CreateIndexRequest(indexDto.getIndexName()); @@ -53,11 +53,11 @@ public boolean create(IndexDto indexDto) throws IOException { .put("index.number_of_shards", indexDto.getNumberOfShards()) .put("index.number_of_replicas", indexDto.getNumberOfReplicas()) ); - if (StrUtil.isNotEmpty(indexDto.getType()) && StrUtil.isNotEmpty(indexDto.getMappingsSource())) { + if (StrUtil.isNotEmpty(indexDto.getMappingsSource())) { //mappings - request.mapping(indexDto.getType(), indexDto.getMappingsSource(), XContentType.JSON); + request.mapping(indexDto.getMappingsSource(), XContentType.JSON); } - CreateIndexResponse response = elasticsearchRestTemplate.getClient() + CreateIndexResponse response = client .indices() .create(request, RequestOptions.DEFAULT); return response.isAcknowledged(); @@ -66,13 +66,16 @@ public boolean create(IndexDto indexDto) throws IOException { @Override public boolean delete(String indexName) throws IOException { DeleteIndexRequest request = new DeleteIndexRequest(indexName); - AcknowledgedResponse response = elasticsearchRestTemplate.getClient().indices().delete(request, RequestOptions.DEFAULT); + AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT); return response.isAcknowledged(); } @Override public PageResult> list(String queryStr, String indices) throws IOException { - Response response = elasticsearchRestTemplate.getClient().getLowLevelClient() + if (StrUtil.isNotEmpty(queryStr)) { + indices = queryStr; + } + Response response = client.getLowLevelClient() .performRequest(new Request( "GET", "/_cat/indices?h=health,status,index,docsCount,docsDeleted,storeSize&s=cds:desc&format=json&index="+StrUtil.nullToEmpty(indices) @@ -81,52 +84,35 @@ public PageResult> list(String queryStr, String indices) thr List> listOfIndicesFromEs = null; if (response != null) { String rawBody = EntityUtils.toString(response.getEntity()); - TypeReference>> typeRef = new TypeReference>>() {}; + TypeReference>> typeRef = new TypeReference>>() {}; listOfIndicesFromEs = mapper.readValue(rawBody, typeRef); } return PageResult.>builder().data(listOfIndicesFromEs).code(0).build(); } - /** - * bytes 转换为 kb - */ - private Double getKB(Long bytes) { - if (bytes == null) { - return 0D; - } - return bytes / 1024D; - } - @Override public Map show(String indexName) throws IOException { - GetIndexRequest request = new GetIndexRequest(); - request.indices(indexName); - GetIndexResponse getIndexResponse = elasticsearchRestTemplate.getClient() + GetIndexRequest request = new GetIndexRequest(indexName); + GetIndexResponse getIndexResponse = client .indices().get(request, RequestOptions.DEFAULT); - ImmutableOpenMap mappOpenMap = getIndexResponse.getMappings().get(indexName); - List indexAliases = getIndexResponse.getAliases().get(indexName); + MappingMetadata mappingMetadata = getIndexResponse.getMappings().get(indexName); + Map mappOpenMap = mappingMetadata.getSourceAsMap(); + List indexAliases = getIndexResponse.getAliases().get(indexName); String settingsStr = getIndexResponse.getSettings().get(indexName).toString(); Object settingsObj = null; if (StrUtil.isNotEmpty(settingsStr)) { - settingsObj = JSONObject.parse(settingsStr); + settingsObj = JsonUtil.parse(settingsStr); } Map result = new HashMap<>(1); Map indexMap = new HashMap<>(3); - Map mappMap = new HashMap<>(mappOpenMap.size()); List aliasesList = new ArrayList<>(indexAliases.size()); indexMap.put("aliases", aliasesList); indexMap.put("settings", settingsObj); - indexMap.put("mappings", mappMap); + indexMap.put("mappings", mappOpenMap); result.put(indexName, indexMap); - //获取mappings数据 - for (ObjectCursor key : mappOpenMap.keys()) { - MappingMetaData data = mappOpenMap.get(key.value); - Map dataMap = data.getSourceAsMap(); - mappMap.put(key.value, dataMap); - } //获取aliases数据 - for (AliasMetaData aliases : indexAliases) { + for (AliasMetadata aliases : indexAliases) { aliasesList.add(aliases.getAlias()); } return result; diff --git a/zlt-business/search-center/search-server/src/main/java/com/central/search/controller/SearchController.java b/zlt-business/search-center/search-server/src/main/java/com/central/search/controller/SearchController.java index 43d6aa1d..6a7f30a5 100644 --- a/zlt-business/search-center/search-server/src/main/java/com/central/search/controller/SearchController.java +++ b/zlt-business/search-center/search-server/src/main/java/com/central/search/controller/SearchController.java @@ -1,10 +1,10 @@ package com.central.search.controller; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.*; import com.central.search.model.SearchDto; import com.central.search.service.ISearchService; +import com.fasterxml.jackson.databind.JsonNode; import org.springframework.web.bind.annotation.*; import io.swagger.annotations.Api; @@ -34,7 +34,7 @@ public SearchController(ISearchService searchService) { * @param searchDto 搜索Dto */ @PostMapping("/{indexName}") - public PageResult strQuery(@PathVariable String indexName, @RequestBody(required = false) SearchDto searchDto) throws IOException { + public PageResult strQuery(@PathVariable String indexName, @RequestBody(required = false) SearchDto searchDto) throws IOException { if (searchDto == null) { searchDto = new SearchDto(); } diff --git a/zlt-business/search-center/search-server/src/main/java/com/central/search/service/ISearchService.java b/zlt-business/search-center/search-server/src/main/java/com/central/search/service/ISearchService.java index 575d4033..3396bcfe 100644 --- a/zlt-business/search-center/search-server/src/main/java/com/central/search/service/ISearchService.java +++ b/zlt-business/search-center/search-server/src/main/java/com/central/search/service/ISearchService.java @@ -1,8 +1,8 @@ package com.central.search.service; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; import com.central.search.model.SearchDto; +import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; @@ -17,5 +17,5 @@ public interface ISearchService { * @param searchDto 搜索Dto * @return */ - PageResult strQuery(String indexName, SearchDto searchDto) throws IOException; + PageResult strQuery(String indexName, SearchDto searchDto) throws IOException; } diff --git a/zlt-business/search-center/search-server/src/main/java/com/central/search/service/impl/AggregationServiceImpl.java b/zlt-business/search-center/search-server/src/main/java/com/central/search/service/impl/AggregationServiceImpl.java index 9a98eac3..26125f55 100644 --- a/zlt-business/search-center/search-server/src/main/java/com/central/search/service/impl/AggregationServiceImpl.java +++ b/zlt-business/search-center/search-server/src/main/java/com/central/search/service/impl/AggregationServiceImpl.java @@ -14,16 +14,15 @@ import org.elasticsearch.search.aggregations.bucket.range.ParsedDateRange; import org.elasticsearch.search.aggregations.bucket.range.Range; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.metrics.cardinality.Cardinality; +import org.elasticsearch.search.aggregations.metrics.ParsedCardinality; import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.stereotype.Service; import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; @@ -35,13 +34,16 @@ * * @author zlt * @date 2019/5/7 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Service public class AggregationServiceImpl implements IAggregationService { - private final ElasticsearchRestTemplate elasticsearchRestTemplate; + private final RestHighLevelClient client; - public AggregationServiceImpl(ElasticsearchRestTemplate elasticsearchRestTemplate) { - this.elasticsearchRestTemplate = elasticsearchRestTemplate; + public AggregationServiceImpl(RestHighLevelClient client) { + this.client = client; } /** @@ -113,7 +115,7 @@ public AggregationServiceImpl(ElasticsearchRestTemplate elasticsearchRestTemplat */ @Override public Map requestStatAgg(String indexName, String routing) throws IOException { - DateTime currDt = DateTime.now(); + ZonedDateTime zonedDateTime = ZonedDateTime.now(); LocalDate localDate = LocalDate.now(); LocalDateTime curDateTime = LocalDateTime.now(); @@ -126,7 +128,7 @@ public Map requestStatAgg(String indexName, String routing) thro .dateRange("currDate") .field("timestamp") .addRange( - currDt.withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0).withMillisOfSecond(0), currDt.plusDays(1) + zonedDateTime.withHour(0).withMinute(0).withSecond(0).withNano(0), zonedDateTime.plusDays(1) ) .subAggregation( AggregationBuilders @@ -138,18 +140,18 @@ public Map requestStatAgg(String indexName, String routing) thro AggregationBuilders .dateRange("curr24Hour") .field("timestamp") - .addRange(currDt.minusDays(1), currDt) + .addRange(zonedDateTime.minusDays(1), zonedDateTime) .subAggregation( //聚合并且按小时分组查询当天内的数据 AggregationBuilders .dateHistogram("statDate") .field("timestamp") - .dateHistogramInterval(new DateHistogramInterval("90m")) + .fixedInterval(new DateHistogramInterval("90m")) .format(CommonConstant.DATETIME_FORMAT) //时区相差8小时 - .timeZone(DateTimeZone.forOffsetHours(8)) + .timeZone(ZoneId.of(CommonConstant.TIME_ZONE_GMT8)) .minDocCount(0L) - .extendedBounds(new ExtendedBounds( + .extendedBounds(new LongBounds( curDateTime.minusDays(1).format(DateTimeFormatter.ofPattern(CommonConstant.DATETIME_FORMAT)), curDateTime.format(DateTimeFormatter.ofPattern(CommonConstant.DATETIME_FORMAT)) )) @@ -164,18 +166,18 @@ public Map requestStatAgg(String indexName, String routing) thro AggregationBuilders .dateRange("currWeek") .field("timestamp") - .addRange(currDt.minusDays(7), currDt) + .addRange(zonedDateTime.minusDays(7), zonedDateTime) .subAggregation( //聚合并且按日期分组查询7天内的数据 AggregationBuilders .dateHistogram("statWeek") .field("timestamp") - .dateHistogramInterval(DateHistogramInterval.DAY) + .calendarInterval(DateHistogramInterval.DAY) .format(CommonConstant.DATE_FORMAT) //时区相差8小时 - .timeZone(DateTimeZone.forOffsetHours(8)) + .timeZone(ZoneId.of(CommonConstant.TIME_ZONE_GMT8)) .minDocCount(0L) - .extendedBounds(new ExtendedBounds( + .extendedBounds(new LongBounds( localDate.minusDays(6).format(DateTimeFormatter.ofPattern(CommonConstant.DATE_FORMAT)), localDate.format(DateTimeFormatter.ofPattern(CommonConstant.DATE_FORMAT)) )) @@ -190,7 +192,7 @@ public Map requestStatAgg(String indexName, String routing) thro AggregationBuilders .dateRange("currMonth") .field("timestamp") - .addRange(currDt.minusDays(30), currDt) + .addRange(zonedDateTime.minusDays(30), zonedDateTime) ).aggregation( //聚合查询浏览器的数据 AggregationBuilders @@ -207,7 +209,7 @@ public Map requestStatAgg(String indexName, String routing) thro .dateRange("currHour") .field("timestamp") .addRange( - currDt.minusHours(1), currDt + zonedDateTime.minusHours(1), zonedDateTime ) .subAggregation( AggregationBuilders @@ -216,7 +218,6 @@ public Map requestStatAgg(String indexName, String routing) thro ) ).size(0); - RestHighLevelClient client = elasticsearchRestTemplate.getClient(); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); Aggregations aggregations = response.getAggregations(); Map result = new HashMap<>(15); @@ -237,7 +238,7 @@ public Map requestStatAgg(String indexName, String routing) thro private void setCurrDate(Map result, Aggregations aggregations) { ParsedDateRange currDate = aggregations.get("currDate"); Range.Bucket bucket = currDate.getBuckets().get(0); - Cardinality cardinality = bucket.getAggregations().get("uv"); + ParsedCardinality cardinality = bucket.getAggregations().get("uv"); result.put("currDate_pv", bucket.getDocCount()); result.put("currDate_uv", cardinality.getValue()); } @@ -293,7 +294,7 @@ private void setStatWeek(Map result, Aggregations aggregations) List items = new ArrayList<>(); List uv = new ArrayList<>(); List pv = new ArrayList<>(); - Cardinality cardinality; + ParsedCardinality cardinality; for (Histogram.Bucket bucket : agg.getBuckets()) { items.add(bucket.getKeyAsString()); pv.add(bucket.getDocCount()); @@ -311,7 +312,7 @@ private void setStatWeek(Map result, Aggregations aggregations) private void setCurrHour(Map result, Aggregations aggregations) { ParsedDateRange currDate = aggregations.get("currHour"); Range.Bucket bucket = currDate.getBuckets().get(0); - Cardinality cardinality = bucket.getAggregations().get("uv"); + ParsedCardinality cardinality = bucket.getAggregations().get("uv"); result.put("currHour_uv", cardinality.getValue()); } /** @@ -322,7 +323,7 @@ private void setStatDate(Map result, Aggregations aggregations) List items = new ArrayList<>(); List uv = new ArrayList<>(); List pv = new ArrayList<>(); - Cardinality cardinality; + ParsedCardinality cardinality; for (Histogram.Bucket bucket : agg.getBuckets()) { items.add(getTimeByDatetimeStr(bucket.getKeyAsString())); pv.add(bucket.getDocCount()); diff --git a/zlt-business/search-center/search-server/src/main/java/com/central/search/service/impl/SearchServiceImpl.java b/zlt-business/search-center/search-server/src/main/java/com/central/search/service/impl/SearchServiceImpl.java index bed63fd5..b0395c8e 100644 --- a/zlt-business/search-center/search-server/src/main/java/com/central/search/service/impl/SearchServiceImpl.java +++ b/zlt-business/search-center/search-server/src/main/java/com/central/search/service/impl/SearchServiceImpl.java @@ -1,12 +1,12 @@ package com.central.search.service.impl; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; import com.central.es.utils.SearchBuilder; import com.central.search.model.SearchDto; import com.central.search.service.ISearchService; +import com.fasterxml.jackson.databind.JsonNode; +import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.search.sort.SortOrder; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.stereotype.Service; import java.io.IOException; @@ -19,10 +19,10 @@ */ @Service public class SearchServiceImpl implements ISearchService { - private final ElasticsearchRestTemplate elasticsearchRestTemplate; + private final RestHighLevelClient client; - public SearchServiceImpl(ElasticsearchRestTemplate elasticsearchRestTemplate) { - this.elasticsearchRestTemplate = elasticsearchRestTemplate; + public SearchServiceImpl(RestHighLevelClient client) { + this.client = client; } /** @@ -32,10 +32,10 @@ public SearchServiceImpl(ElasticsearchRestTemplate elasticsearchRestTemplate) { * @return */ @Override - public PageResult strQuery(String indexName, SearchDto searchDto) throws IOException { - return SearchBuilder.builder(elasticsearchRestTemplate, indexName) + public PageResult strQuery(String indexName, SearchDto searchDto) throws IOException { + return SearchBuilder.builder(client, indexName) .setStringQuery(searchDto.getQueryStr()) - .addSort(searchDto.getSortCol(), SortOrder.DESC) + .addSort(searchDto.getSortCol(), searchDto.getSortOrder()) .setIsHighlight(searchDto.getIsHighlighter()) .getPage(searchDto.getPage(), searchDto.getLimit()); } diff --git a/zlt-business/user-center/Dockerfile b/zlt-business/user-center/Dockerfile new file mode 100644 index 00000000..93bf7f66 --- /dev/null +++ b/zlt-business/user-center/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:8u292 + +LABEL MAINTAINER=Andy +WORKDIR /apps +ADD target/user-center.jar /apps/ +CMD ["java","-jar","user-center.jar"] +# docker run --name user-center \ +# -e spring_cloud_nacos_server_addr=10.0.0.12:8848 \ +# -e zlt_datasource_ip=10.0.0.12 \ +# -e zlt_datasource_username=root \ +# -e zlt_datasource_password=redhat \ +# -e spring_redis_host=10.0.0.12 \ +# -d user-center:4.5 \ No newline at end of file diff --git a/zlt-business/user-center/pom.xml b/zlt-business/user-center/pom.xml index 87c93685..acbf12b1 100644 --- a/zlt-business/user-center/pom.xml +++ b/zlt-business/user-center/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-business - 3.5.0 + 5.4.0 user-center 用户中心 @@ -28,7 +28,7 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter com.zlt @@ -52,11 +52,6 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery - - org.springframework - spring-context-support - - org.springframework.boot spring-boot-starter-actuator @@ -71,10 +66,14 @@ io.micrometer micrometer-registry-prometheus - - org.springframework.social - spring-social-security + junit + junit + test + + + org.hibernate + hibernate-validator diff --git a/zlt-business/user-center/src/main/java/com/central/UserCenterApp.java b/zlt-business/user-center/src/main/java/com/central/UserCenterApp.java index 4ba97bcd..5da788be 100644 --- a/zlt-business/user-center/src/main/java/com/central/UserCenterApp.java +++ b/zlt-business/user-center/src/main/java/com/central/UserCenterApp.java @@ -1,6 +1,6 @@ package com.central; -import com.central.common.ribbon.annotation.EnableFeignInterceptor; +import com.central.common.lb.annotation.EnableFeignInterceptor; import com.central.search.annotation.EnableSearchClient; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/zlt-business/user-center/src/main/java/com/central/user/controller/SysMenuController.java b/zlt-business/user-center/src/main/java/com/central/user/controller/SysMenuController.java index 531844f5..064f6019 100644 --- a/zlt-business/user-center/src/main/java/com/central/user/controller/SysMenuController.java +++ b/zlt-business/user-center/src/main/java/com/central/user/controller/SysMenuController.java @@ -5,11 +5,11 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; import com.central.common.annotation.LoginUser; import com.central.common.constant.CommonConstant; import com.central.common.model.*; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; @@ -40,7 +40,7 @@ public class SysMenuController { public static List treeBuilder(List sysMenus) { List menus = new ArrayList<>(); for (SysMenu sysMenu : sysMenus) { - if (ObjectUtils.equals(-1L, sysMenu.getParentId())) { + if (ObjectUtil.equal(-1L, sysMenu.getParentId())) { menus.add(sysMenu); } for (SysMenu menu : sysMenus) { @@ -167,7 +167,7 @@ public List findMyMenu(@LoginUser SysUser user) { if (CollectionUtil.isEmpty(roles)) { return Collections.emptyList(); } - List menus = menuService.findByRoleCodes(roles.parallelStream().map(SysRole::getCode).collect(Collectors.toSet()), CommonConstant.MENU); + List menus = menuService.findByRoleCodes(roles.stream().map(SysRole::getCode).collect(Collectors.toSet()), CommonConstant.MENU); return treeBuilder(menus); } } diff --git a/zlt-business/user-center/src/main/java/com/central/user/controller/SysRoleController.java b/zlt-business/user-center/src/main/java/com/central/user/controller/SysRoleController.java index 9cccefc4..80de7d23 100644 --- a/zlt-business/user-center/src/main/java/com/central/user/controller/SysRoleController.java +++ b/zlt-business/user-center/src/main/java/com/central/user/controller/SysRoleController.java @@ -61,7 +61,7 @@ public Result> findAll() { * @return */ @PostMapping("/roles/saveOrUpdate") - public Result saveOrUpdate(@RequestBody SysRole sysRole) { + public Result saveOrUpdate(@RequestBody SysRole sysRole) throws Exception { return sysRoleService.saveOrUpdateRole(sysRole); } diff --git a/zlt-business/user-center/src/main/java/com/central/user/controller/SysUserController.java b/zlt-business/user-center/src/main/java/com/central/user/controller/SysUserController.java index 0db93f8b..01d84597 100644 --- a/zlt-business/user-center/src/main/java/com/central/user/controller/SysUserController.java +++ b/zlt-business/user-center/src/main/java/com/central/user/controller/SysUserController.java @@ -7,7 +7,6 @@ import java.util.Set; import cn.hutool.core.bean.BeanUtil; -import com.alibaba.fastjson.JSONObject; import com.central.common.annotation.LoginUser; import com.central.common.constant.CommonConstant; import com.central.common.model.*; @@ -18,6 +17,7 @@ import com.central.search.model.LogicDelDto; import com.central.search.model.SearchDto; import com.central.user.model.SysUserExcel; +import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.collections4.MapUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; @@ -236,7 +236,7 @@ public Result delete(@PathVariable Long id) { @CacheEvict(value = "user", key = "#sysUser.username") @PostMapping("/users/saveOrUpdate") @AuditLog(operation = "'新增或更新用户:' + #sysUser.username") - public Result saveOrUpdate(@RequestBody SysUser sysUser) { + public Result saveOrUpdate(@RequestBody SysUser sysUser) throws Exception { return appUserService.saveOrUpdateUser(sysUser); } @@ -280,7 +280,7 @@ public Result importExcl(@RequestParam("file") MultipartFile excl) throws Except @ApiImplicitParam(name = "queryStr", value = "搜索关键字", dataType = "String") }) @GetMapping("/users/search") - public PageResult search(SearchDto searchDto) { + public PageResult search(SearchDto searchDto) { searchDto.setIsHighlighter(true); searchDto.setSortCol("createTime"); return queryService.strQuery("sys_user", searchDto, SEARCH_LOGIC_DEL_DTO); diff --git a/zlt-business/user-center/src/main/java/com/central/user/service/ISysRoleService.java b/zlt-business/user-center/src/main/java/com/central/user/service/ISysRoleService.java index 716a48a5..2d29f858 100644 --- a/zlt-business/user-center/src/main/java/com/central/user/service/ISysRoleService.java +++ b/zlt-business/user-center/src/main/java/com/central/user/service/ISysRoleService.java @@ -9,10 +9,13 @@ import com.central.common.service.ISuperService; /** -* @author zlt + * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public interface ISysRoleService extends ISuperService { - void saveRole(SysRole sysRole); + void saveRole(SysRole sysRole) throws Exception; void deleteRole(Long id); @@ -28,7 +31,7 @@ public interface ISysRoleService extends ISuperService { * @param sysRole * @return Result */ - Result saveOrUpdateRole(SysRole sysRole); + Result saveOrUpdateRole(SysRole sysRole) throws Exception; /** * 查询所有角色 diff --git a/zlt-business/user-center/src/main/java/com/central/user/service/ISysUserService.java b/zlt-business/user-center/src/main/java/com/central/user/service/ISysUserService.java index 26adcb5d..aaf899fd 100644 --- a/zlt-business/user-center/src/main/java/com/central/user/service/ISysUserService.java +++ b/zlt-business/user-center/src/main/java/com/central/user/service/ISysUserService.java @@ -13,7 +13,10 @@ import com.central.common.model.SysUser; /** -* @author zlt + * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public interface ISysUserService extends ISuperService { /** @@ -98,7 +101,7 @@ public interface ISysUserService extends ISuperService { */ List findAllUsers(Map params); - Result saveOrUpdateUser(SysUser sysUser); + Result saveOrUpdateUser(SysUser sysUser) throws Exception; /** * 删除用户 diff --git a/zlt-business/user-center/src/main/java/com/central/user/service/impl/SysRoleServiceImpl.java b/zlt-business/user-center/src/main/java/com/central/user/service/impl/SysRoleServiceImpl.java index 4d28373d..8550e08d 100644 --- a/zlt-business/user-center/src/main/java/com/central/user/service/impl/SysRoleServiceImpl.java +++ b/zlt-business/user-center/src/main/java/com/central/user/service/impl/SysRoleServiceImpl.java @@ -29,7 +29,7 @@ @Slf4j @Service public class SysRoleServiceImpl extends SuperServiceImpl implements ISysRoleService { - private final static String LOCK_KEY_ROLECODE = CommonConstant.LOCK_KEY_PREFIX+"rolecode:"; + private final static String LOCK_KEY_ROLECODE = "rolecode:"; @Resource private SysUserRoleMapper userRoleMapper; @@ -42,7 +42,7 @@ public class SysRoleServiceImpl extends SuperServiceImpl @Transactional(rollbackFor = Exception.class) @Override - public void saveRole(SysRole sysRole) { + public void saveRole(SysRole sysRole) throws Exception { String roleCode = sysRole.getCode(); super.saveIdempotency(sysRole, lock , LOCK_KEY_ROLECODE+roleCode, new QueryWrapper().eq("code", roleCode), "角色code已存在"); @@ -67,7 +67,7 @@ public PageResult findRoles(Map params) { @Override @Transactional - public Result saveOrUpdateRole(SysRole sysRole) { + public Result saveOrUpdateRole(SysRole sysRole) throws Exception { if (sysRole.getId() == null) { this.saveRole(sysRole); } else { diff --git a/zlt-business/user-center/src/main/java/com/central/user/service/impl/SysUserServiceImpl.java b/zlt-business/user-center/src/main/java/com/central/user/service/impl/SysUserServiceImpl.java index ce667390..eedada4c 100644 --- a/zlt-business/user-center/src/main/java/com/central/user/service/impl/SysUserServiceImpl.java +++ b/zlt-business/user-center/src/main/java/com/central/user/service/impl/SysUserServiceImpl.java @@ -37,7 +37,7 @@ @Slf4j @Service public class SysUserServiceImpl extends SuperServiceImpl implements ISysUserService { - private final static String LOCK_KEY_USERNAME = CommonConstant.LOCK_KEY_PREFIX+"username:"; + private final static String LOCK_KEY_USERNAME = "username:"; @Autowired private PasswordEncoder passwordEncoder; @@ -80,10 +80,10 @@ public LoginAppUser getLoginAppUser(SysUser sysUser) { loginAppUser.setRoles(sysRoles); if (!CollectionUtils.isEmpty(sysRoles)) { - Set roleIds = sysRoles.parallelStream().map(SuperEntity::getId).collect(Collectors.toSet()); + Set roleIds = sysRoles.stream().map(SuperEntity::getId).collect(Collectors.toSet()); List menus = roleMenuMapper.findMenusByRoleIds(roleIds, CommonConstant.PERMISSION); if (!CollectionUtils.isEmpty(menus)) { - Set permissions = menus.parallelStream().map(p -> p.getPath()) + Set permissions = menus.stream().map(p -> p.getPath()) .collect(Collectors.toSet()); // 设置权限集合 loginAppUser.setPermissions(permissions); @@ -219,7 +219,7 @@ public Result updateEnabled(Map params) { @Transactional(rollbackFor = Exception.class) @Override - public Result saveOrUpdateUser(SysUser sysUser) { + public Result saveOrUpdateUser(SysUser sysUser) throws Exception { if (sysUser.getId() == null) { if (StringUtils.isBlank(sysUser.getType())) { sysUser.setType(UserType.BACKEND.name()); diff --git a/zlt-business/user-center/src/main/resources/application.yml b/zlt-business/user-center/src/main/resources/application.yml index a9c65586..f9a8c71b 100644 --- a/zlt-business/user-center/src/main/resources/application.yml +++ b/zlt-business/user-center/src/main/resources/application.yml @@ -34,7 +34,10 @@ zlt: - sys_role_user - sys_role_menu ignoreSqls: + # 用户关联角色时,显示所有角色 - com.central.user.mapper.SysRoleMapper.findAll + # 用户列表显示用户所关联的所有角色 + - com.central.user.mapper.SysUserRoleMapper.findRolesByUserIds #审计日志 # audit-log: # enabled: true diff --git a/zlt-commons/pom.xml b/zlt-commons/pom.xml index 2fbe456a..617efea0 100644 --- a/zlt-commons/pom.xml +++ b/zlt-commons/pom.xml @@ -4,7 +4,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-commons 通用组件 @@ -16,10 +16,12 @@ zlt-db-spring-boot-starter zlt-swagger2-spring-boot-starter zlt-log-spring-boot-starter - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter zlt-auth-client-spring-boot-starter zlt-sentinel-spring-boot-starter zlt-common-core zlt-elasticsearch-spring-boot-starter + zlt-oss-spring-boot-starter + zlt-zookeeper-spring-boot-starter \ No newline at end of file diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/pom.xml b/zlt-commons/zlt-auth-client-spring-boot-starter/pom.xml index 710df120..f6a3d8a4 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 4.0.0 jar @@ -42,5 +42,10 @@ javax.servlet-api provided + + org.apache.tomcat.embed + tomcat-embed-websocket + true + diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/AuthClientAutoConfiguration.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/AuthClientAutoConfiguration.java new file mode 100644 index 00000000..17ede133 --- /dev/null +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/AuthClientAutoConfiguration.java @@ -0,0 +1,21 @@ +package com.central.oauth2.common; + +import com.central.oauth2.common.properties.SecurityProperties; +import com.central.oauth2.common.properties.TokenStoreProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; + +/** + * 鉴权自动配置 + * + * @author zlt + * @version 1.0 + * @date 2021/7/24 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@EnableConfigurationProperties({SecurityProperties.class, TokenStoreProperties.class}) +@ComponentScan +public class AuthClientAutoConfiguration { +} diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/DefaultResourceServerConf.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/DefaultResourceServerConf.java index e3c4ccf7..71e6d114 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/DefaultResourceServerConf.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/DefaultResourceServerConf.java @@ -9,6 +9,7 @@ import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.authentication.TokenExtractor; import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler; import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler; import org.springframework.security.oauth2.provider.token.TokenStore; @@ -36,13 +37,17 @@ public class DefaultResourceServerConf extends ResourceServerConfigurerAdapter { @Autowired private SecurityProperties securityProperties; + @Resource + private TokenExtractor tokenExtractor; + @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.tokenStore(tokenStore) .stateless(true) .authenticationEntryPoint(authenticationEntryPoint) .expressionHandler(expressionHandler) - .accessDeniedHandler(oAuth2AccessDeniedHandler); + .accessDeniedHandler(oAuth2AccessDeniedHandler) + .tokenExtractor(tokenExtractor); } @Override diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/SecurityPropertiesConfig.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/SecurityPropertiesConfig.java deleted file mode 100644 index e85ed2a2..00000000 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/SecurityPropertiesConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.central.oauth2.common.config; - -import com.central.oauth2.common.properties.SecurityProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; - -/** - * @author zlt - * @date 2019/10/7 - *

- * Blog: https://blog.csdn.net/zlt2000 - * Github: https://github.com/zlt2000 - */ -@EnableConfigurationProperties(SecurityProperties.class) -public class SecurityPropertiesConfig { -} diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/WcAuthConfigurator.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/WcAuthConfigurator.java new file mode 100644 index 00000000..26caf368 --- /dev/null +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/config/WcAuthConfigurator.java @@ -0,0 +1,34 @@ +package com.central.oauth2.common.config; + +import com.central.oauth2.common.util.AuthUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.websocket.server.ServerEndpointConfig; + +/** + * webSocket鉴权配置 + * + * @author zlt + * @version 1.0 + * @date 2022/5/8 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +public class WcAuthConfigurator extends ServerEndpointConfig.Configurator { + @Override + public boolean checkOrigin(String originHeaderValue) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + try { + //检查token有效性 + AuthUtils.checkAccessToken(servletRequestAttributes.getRequest()); + } catch (Exception e) { + log.error("WebSocket-auth-error", e); + return false; + } + return super.checkOrigin(originHeaderValue); + } +} diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/constants/IdTokenClaimNames.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/constants/IdTokenClaimNames.java new file mode 100644 index 00000000..23865987 --- /dev/null +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/constants/IdTokenClaimNames.java @@ -0,0 +1,89 @@ +package com.central.oauth2.common.constants; + +/** + * id_token属性名常量 + * + * @author zlt + * @version 1.0 + * @date 2021/4/23 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class IdTokenClaimNames { + /** + * {@code iss} - the Issuer identifier + */ + public final static String ISS = "iss"; + + /** + * {@code sub} - the Subject identifier + */ + public final static String SUB = "sub"; + + /** + * {@code aud} - the Audience(s) that the ID Token is intended for + */ + public final static String AUD = "aud"; + + /** + * {@code exp} - the Expiration time on or after which the ID Token MUST NOT be accepted + */ + public final static String EXP = "exp"; + + /** + * {@code iat} - the time at which the ID Token was issued + */ + public final static String IAT = "iat"; + + /** + * {@code auth_time} - the time when the End-User authentication occurred + */ + public final static String AUTH_TIME = "auth_time"; + + /** + * {@code nonce} - a {@code String} value used to associate a Client session with an ID Token, + * and to mitigate replay attacks. + */ + public final static String NONCE = "nonce"; + + /** + * {@code acr} - the Authentication Context Class Reference + */ + public final static String ACR = "acr"; + + /** + * {@code amr} - the Authentication Methods References + */ + public final static String AMR = "amr"; + + /** + * {@code azp} - the Authorized party to which the ID Token was issued + */ + public final static String AZP = "azp"; + + /** + * {@code at_hash} - the Access Token hash value + */ + public final static String AT_HASH = "at_hash"; + + /** + * {@code c_hash} - the Authorization Code hash value + */ + public final static String C_HASH = "c_hash"; + + /** + * {@code name} - 用户姓名 + */ + public final static String NAME = "name"; + + /** + * {@code login_name} - 登录名 + */ + public final static String L_NAME = "login_name"; + + /** + * {@code picture} - 头像照片 + */ + public final static String PIC = "picture"; +} diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/converter/CustomUserAuthenticationConverter.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/converter/CustomUserAuthenticationConverter.java index 7bf24315..fbc86581 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/converter/CustomUserAuthenticationConverter.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/converter/CustomUserAuthenticationConverter.java @@ -1,5 +1,6 @@ package com.central.oauth2.common.converter; +import com.central.common.model.LoginAppUser; import com.central.common.model.SysUser; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -68,7 +69,7 @@ public Authentication extractAuthentication(Map map) { principal = user; } else { Integer id = (Integer)map.get("id"); - SysUser user = new SysUser(); + LoginAppUser user = new LoginAppUser(); user.setUsername((String)principal); user.setId(Long.valueOf(id)); principal = user; diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/AuthProperties.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/AuthProperties.java index bdc0048c..c89860b9 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/AuthProperties.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/AuthProperties.java @@ -28,4 +28,23 @@ public class AuthProperties { * url权限配置 */ private UrlPermissionProperties urlPermission = new UrlPermissionProperties(); + + /** + * 是否开启统一登出 + * 1. 登出时把同一个用户名下的所有token都注销 + * 2. 登出信息通知所有单点登录系统 + */ + private Boolean unifiedLogout = false; + + /** + * 是否同应用同账号登录互踢 + */ + private Boolean isSingleLogin = false; + + /** + * 是否同应用同账号登录时共用token + * true: 多个用户使用同一账号登录时共用一个token + * false: 就算使用同一账号登录时都会新建一个token + */ + private Boolean isShareToken = true; } diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/PermitProperties.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/PermitProperties.java index 16a5b653..e0c3c15f 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/PermitProperties.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/PermitProperties.java @@ -23,6 +23,7 @@ public class PermitProperties { "/*/v2/api-docs", "/swagger/api-docs", "/swagger-ui.html", + "/doc.html", "/swagger-resources/**", "/webjars/**", "/druid/**" diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/TokenStoreProperties.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/TokenStoreProperties.java new file mode 100644 index 00000000..0f2687fb --- /dev/null +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/properties/TokenStoreProperties.java @@ -0,0 +1,27 @@ +package com.central.oauth2.common.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; + +/** + * Token配置 + * + * @author zlt + * @version 1.0 + * @date 2021/5/19 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +@Getter +@ConfigurationProperties(prefix = "zlt.oauth2.token.store") +@RefreshScope +public class TokenStoreProperties { + /** + * token存储类型(redis/db/authJwt/resJwt) + */ + private String type = "redis"; +} diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/service/impl/CustomBearerTokenExtractor.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/service/impl/CustomBearerTokenExtractor.java new file mode 100644 index 00000000..cc21489d --- /dev/null +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/service/impl/CustomBearerTokenExtractor.java @@ -0,0 +1,44 @@ +package com.central.oauth2.common.service.impl; + +import com.central.oauth2.common.properties.SecurityProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +/** + * 自定义 TokenExtractor + * + * @author zlt + * @version 1.0 + * @date 2022/6/4 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@ConditionalOnClass(HttpServletRequest.class) +@Component +public class CustomBearerTokenExtractor extends BearerTokenExtractor { + @Resource + private SecurityProperties securityProperties; + + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + /** + * 解决只要请求携带access_token,排除鉴权的url依然会被拦截 + */ + @Override + public Authentication extract(HttpServletRequest request) { + //判断当前请求为排除鉴权的url时,直接返回null + for (String url : securityProperties.getIgnore().getUrls()) { + if (antPathMatcher.match(url, request.getRequestURI())) { + return null; + } + } + return super.extract(request); + } +} diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/service/impl/DefaultPermissionServiceImpl.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/service/impl/DefaultPermissionServiceImpl.java index 954cf054..c08a178b 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/service/impl/DefaultPermissionServiceImpl.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/service/impl/DefaultPermissionServiceImpl.java @@ -25,6 +25,9 @@ * * @author zlt * @date 2018/10/28 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Slf4j public abstract class DefaultPermissionServiceImpl { @@ -32,7 +35,7 @@ public abstract class DefaultPermissionServiceImpl { @Autowired private SecurityProperties securityProperties; - private AntPathMatcher antPathMatcher = new AntPathMatcher(); + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); /** * 查询当前用户拥有的资源权限 diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthDbTokenStore.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthDbTokenStore.java index d084e549..1d0b1ec9 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthDbTokenStore.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthDbTokenStore.java @@ -3,6 +3,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; @@ -14,6 +15,7 @@ * @author zlt * @date 2018/7/24 16:23 */ +@Configuration @ConditionalOnProperty(prefix = "zlt.oauth2.token.store", name = "type", havingValue = "db") public class AuthDbTokenStore { @Autowired diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthJwtTokenStore.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthJwtTokenStore.java index 9b99fb79..2d261222 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthJwtTokenStore.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthJwtTokenStore.java @@ -5,6 +5,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.bootstrap.encrypt.KeyProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; import org.springframework.security.oauth2.provider.token.TokenEnhancer; @@ -23,16 +25,14 @@ * * @author zlt * @date 2018/7/24 16:21 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ +@Configuration @ConditionalOnProperty(prefix = "zlt.oauth2.token.store", name = "type", havingValue = "authJwt") public class AuthJwtTokenStore { - - @Bean("keyProp") - public KeyProperties keyProperties() { - return new KeyProperties(); - } - - @Resource(name = "keyProp") + @Resource private KeyProperties keyProperties; @Bean @@ -41,6 +41,7 @@ public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) { } @Bean + @Order(2) public JwtAccessTokenConverter jwtAccessTokenConverter() { final JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); KeyPair keyPair = new KeyStoreKeyFactory @@ -51,25 +52,4 @@ public JwtAccessTokenConverter jwtAccessTokenConverter() { tokenConverter.setUserTokenConverter(new CustomUserAuthenticationConverter()); return converter; } - - /** - * jwt 生成token 定制化处理 - * 添加一些额外的用户信息到token里面 - * - * @return TokenEnhancer - */ - @Bean - public TokenEnhancer tokenEnhancer() { - return (accessToken, authentication) -> { - final Map additionalInfo = new HashMap<>(1); - Object principal = authentication.getPrincipal(); - //增加id参数 - if (principal instanceof SysUser) { - SysUser user = (SysUser)principal; - additionalInfo.put("id", user.getId()); - } - ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); - return accessToken; - }; - } } diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthRedisTokenStore.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthRedisTokenStore.java index a8651589..239dc420 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthRedisTokenStore.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/AuthRedisTokenStore.java @@ -4,7 +4,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.security.oauth2.provider.token.TokenStore; /** @@ -13,17 +15,15 @@ * * @author zlt * @date 2018/7/25 9:36 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ +@Configuration @ConditionalOnProperty(prefix = "zlt.oauth2.token.store", name = "type", havingValue = "redis", matchIfMissing = true) public class AuthRedisTokenStore { - @Autowired - private RedisConnectionFactory connectionFactory; - - @Autowired - private SecurityProperties securityProperties; - @Bean - public TokenStore tokenStore() { - return new CustomRedisTokenStore(connectionFactory, securityProperties); + public TokenStore tokenStore(RedisConnectionFactory connectionFactory, SecurityProperties securityProperties, RedisSerializer redisValueSerializer) { + return new CustomRedisTokenStore(connectionFactory, securityProperties, redisValueSerializer); } } diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/CustomRedisTokenStore.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/CustomRedisTokenStore.java index 43193a37..f07ddb0d 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/CustomRedisTokenStore.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/CustomRedisTokenStore.java @@ -4,6 +4,7 @@ import com.central.oauth2.common.properties.SecurityProperties; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; @@ -38,6 +39,7 @@ */ public class CustomRedisTokenStore implements TokenStore { private static final String ACCESS = "access:"; + private static final String ACCESS_BAK = "access_bak:"; private static final String AUTH_TO_ACCESS = "auth_to_access:"; private static final String REFRESH_AUTH = "refresh_auth:"; private static final String ACCESS_TO_REFRESH = "access_to_refresh:"; @@ -61,9 +63,15 @@ public class CustomRedisTokenStore implements TokenStore { */ private SecurityProperties securityProperties; - public CustomRedisTokenStore(RedisConnectionFactory connectionFactory, SecurityProperties securityProperties) { + /** + * 业务redis的value序列化 + */ + private RedisSerializer redisValueSerializer; + + public CustomRedisTokenStore(RedisConnectionFactory connectionFactory, SecurityProperties securityProperties, RedisSerializer redisValueSerializer) { this.connectionFactory = connectionFactory; this.securityProperties = securityProperties; + this.redisValueSerializer = redisValueSerializer; if (springDataRedis_2_0) { this.loadRedisConnectionMethods_2_0(); } @@ -111,7 +119,7 @@ private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) { } private ClientDetails deserializeClientDetails(byte[] bytes) { - return serializationStrategy.deserialize(bytes, ClientDetails.class); + return (ClientDetails) redisValueSerializer.deserialize(bytes); } private byte[] serialize(String string) { @@ -159,7 +167,7 @@ public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { //获取过期时长 int validitySeconds = getAccessTokenValiditySeconds(clientAuth.getClientId()); if (validitySeconds > 0) { - double expiresRatio = token.getExpiresIn() / (double)validitySeconds; + double expiresRatio = token.getExpiresIn() / (double) validitySeconds; //判断是否需要续签,当前剩余时间小于过期时长的50%则续签 if (expiresRatio <= securityProperties.getAuth().getRenew().getTimeRatio()) { //更新AccessToken过期时间 @@ -175,6 +183,7 @@ public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { /** * 判断应用自动续签是否满足白名单和黑名单的过滤逻辑 + * * @param clientId 应用id * @return 是否满足 */ @@ -186,7 +195,7 @@ private boolean checkRenewClientId(String clientId) { List exclusiveClientIds = securityProperties.getAuth().getRenew().getExclusiveClientIds(); if (includeClientIds.size() > 0) { result = includeClientIds.contains(clientId); - } else if(exclusiveClientIds.size() > 0) { + } else if (exclusiveClientIds.size() > 0) { result = !exclusiveClientIds.contains(clientId); } return result; @@ -194,6 +203,7 @@ private boolean checkRenewClientId(String clientId) { /** * 获取token的总有效时长 + * * @param clientId 应用id */ private int getAccessTokenValiditySeconds(String clientId) { @@ -249,12 +259,14 @@ public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authe /** * 存储token + * * @param isRenew 是否续签 */ private void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication, boolean isRenew) { byte[] serializedAccessToken = serialize(token); byte[] serializedAuth = serialize(authentication); byte[] accessKey = serializeKey(ACCESS + token.getValue()); + byte[] accessBakKey = serializeKey(ACCESS_BAK + token.getValue()); byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + token.getValue()); byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication)); byte[] approvalKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(authentication)); @@ -272,6 +284,7 @@ private void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication auth if (springDataRedis_2_0) { try { this.redisConnectionSet_2_0.invoke(conn, accessKey, serializedAccessToken); + this.redisConnectionSet_2_0.invoke(conn, accessBakKey, serializedAccessToken); this.redisConnectionSet_2_0.invoke(conn, authKey, serializedAuth); this.redisConnectionSet_2_0.invoke(conn, authToAccessKey, serializedAccessToken); } catch (Exception ex) { @@ -279,6 +292,7 @@ private void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication auth } } else { conn.set(accessKey, serializedAccessToken); + conn.set(accessBakKey, serializedAccessToken); conn.set(authKey, serializedAuth); conn.set(authToAccessKey, serializedAccessToken); } @@ -296,6 +310,7 @@ private void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication auth if (token.getExpiration() != null) { int seconds = token.getExpiresIn(); conn.expire(accessKey, seconds); + conn.expire(accessBakKey, seconds + 60); conn.expire(authKey, seconds); conn.expire(authToAccessKey, seconds); conn.expire(clientId, seconds); @@ -356,20 +371,20 @@ public OAuth2AccessToken readAccessToken(String tokenValue) { public void removeAccessToken(String tokenValue) { byte[] accessKey = serializeKey(ACCESS + tokenValue); + byte[] accessBakKey = serializeKey(ACCESS_BAK + tokenValue); byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + tokenValue); byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue); RedisConnection conn = getConnection(); try { + byte[] access = conn.get(accessKey); + byte[] auth = conn.get(authKey); conn.openPipeline(); - conn.get(accessKey); - conn.get(authKey); conn.del(accessKey); + conn.del(accessBakKey); conn.del(accessToRefreshKey); // Don't remove the refresh token - it's up to the caller to do that conn.del(authKey); - List results = conn.closePipeline(); - byte[] access = (byte[]) results.get(0); - byte[] auth = (byte[]) results.get(1); + conn.closePipeline(); OAuth2Authentication authentication = deserializeAuthentication(auth); if (authentication != null) { @@ -471,20 +486,17 @@ public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) private void removeAccessTokenUsingRefreshToken(String refreshToken) { byte[] key = serializeKey(REFRESH_TO_ACCESS + refreshToken); - List results = null; RedisConnection conn = getConnection(); + byte[] bytes = null; try { - conn.openPipeline(); - conn.get(key); + bytes = conn.get(key); conn.del(key); - results = conn.closePipeline(); } finally { conn.close(); } - if (results == null) { + if (bytes == null) { return; } - byte[] bytes = (byte[]) results.get(0); String accessToken = deserializeString(bytes); if (accessToken != null) { removeAccessToken(accessToken); diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/ResJwtTokenStore.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/ResJwtTokenStore.java index 606fbb68..c7655696 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/ResJwtTokenStore.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/store/ResJwtTokenStore.java @@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; @@ -31,6 +32,7 @@ * @author zlt * @date 2018/8/20 9:25 */ +@Configuration @ConditionalOnProperty(prefix = "zlt.oauth2.token.store", name = "type", havingValue = "resJwt") public class ResJwtTokenStore { @Autowired diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/CustomWebAuthenticationDetails.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/CustomWebAuthenticationDetails.java new file mode 100644 index 00000000..95dd1c18 --- /dev/null +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/CustomWebAuthenticationDetails.java @@ -0,0 +1,37 @@ +package com.central.oauth2.common.token; + +import lombok.Getter; + +import java.io.Serializable; + +/** + * 表单登录的认证信息对象 + * + * @author zlt + * @version 1.0 + * @date 2021/7/24 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Getter +public class CustomWebAuthenticationDetails implements Serializable { + private static final long serialVersionUID = - 1; + + private final String accountType; + private final String remoteAddress; + private final String sessionId; + + public CustomWebAuthenticationDetails(String remoteAddress, String sessionId, String accountType) { + this.remoteAddress = remoteAddress; + this.sessionId = sessionId; + this.accountType = accountType; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append("; accountType: ").append(this.getAccountType()); + return sb.toString(); + } +} diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/TenantUsernamePasswordAuthenticationToken.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/TenantUsernamePasswordAuthenticationToken.java new file mode 100644 index 00000000..e9546d50 --- /dev/null +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/TenantUsernamePasswordAuthenticationToken.java @@ -0,0 +1,37 @@ +package com.central.oauth2.common.token; + +import lombok.Getter; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +/** + * 增加租户id,解决不同租户单点登录时角色没变化 + * + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class TenantUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken { + private static final long serialVersionUID = -5638287853803374687L; + + /** + * 租户id + */ + @Getter + private final String clientId; + + public TenantUsernamePasswordAuthenticationToken(Object principal, Object credentials, String clientId) { + super(principal, credentials); + this.clientId = clientId; + } + + public TenantUsernamePasswordAuthenticationToken(Object principal, Object credentials, + Collection authorities, String clientId) { + super(principal, credentials, authorities); + this.clientId = clientId; + } +} diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/util/AuthUtils.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/util/AuthUtils.java index bad4669f..2ed47e13 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/util/AuthUtils.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/util/AuthUtils.java @@ -1,23 +1,33 @@ package com.central.oauth2.common.util; import com.central.common.constant.CommonConstant; +import com.central.common.constant.SecurityConstants; +import com.central.common.context.LoginUserContextHolder; import com.central.common.model.SysUser; +import com.central.common.utils.SpringUtil; +import com.central.oauth2.common.token.CustomWebAuthenticationDetails; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.TokenStore; import javax.servlet.http.HttpServletRequest; -import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.Enumeration; +import java.util.*; /** * 认证授权相关工具类 * * @author zlt * @date 2018/5/13 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Slf4j public class AuthUtils { @@ -64,6 +74,40 @@ private static String extractHeaderToken(HttpServletRequest request) { return null; } + /** + * 校验accessToken + */ + public static SysUser checkAccessToken(HttpServletRequest request) { + String accessToken = extractToken(request); + return checkAccessToken(accessToken); + } + + public static SysUser checkAccessToken(String accessTokenValue) { + TokenStore tokenStore = SpringUtil.getBean(TokenStore.class); + OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue); + if (accessToken == null || accessToken.getValue() == null) { + throw new InvalidTokenException("Invalid access token: " + accessTokenValue); + } else if (accessToken.isExpired()) { + tokenStore.removeAccessToken(accessToken); + throw new InvalidTokenException("Access token expired: " + accessTokenValue); + } + OAuth2Authentication result = tokenStore.readAuthentication(accessToken); + if (result == null) { + throw new InvalidTokenException("Invalid access token: " + accessTokenValue); + } + return setContext(result); + } + + /** + * 用户信息赋值 context 对象 + */ + public static SysUser setContext(Authentication authentication) { + SecurityContextHolder.getContext().setAuthentication(authentication); + SysUser user = getUser(authentication); + LoginUserContextHolder.setUser(user); + return user; + } + /** * *从header 请求中的clientId:clientSecret */ @@ -104,4 +148,41 @@ public static String getUsername(Authentication authentication) { } return username; } + + /** + * 获取登陆的用户对象 + */ + public static SysUser getUser(Authentication authentication) { + SysUser user = null; + if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken)) { + Object principal = authentication.getPrincipal(); + //客户端模式只返回一个clientId + if (principal instanceof SysUser) { + user = (SysUser)principal; + } + } + return user; + } + + /** + * 获取登陆的帐户类型 + */ + public static String getAccountType(Authentication authentication) { + String accountType = null; + if (authentication != null) { + Object details = authentication.getDetails(); + if (details != null) { + if (details instanceof CustomWebAuthenticationDetails) { + CustomWebAuthenticationDetails detailsObj = (CustomWebAuthenticationDetails) details; + accountType = detailsObj.getAccountType(); + } else { + Map detailsMap = (Map) details; + if (detailsMap != null) { + accountType = detailsMap.get(SecurityConstants.ACCOUNT_TYPE_PARAM_NAME); + } + } + } + } + return accountType; + } } diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/util/JwtUtils.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/util/JwtUtils.java index 507dfdef..e5627405 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/util/JwtUtils.java +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/util/JwtUtils.java @@ -1,17 +1,25 @@ package com.central.oauth2.common.util; -import com.alibaba.fastjson.JSONObject; import com.central.common.constant.SecurityConstants; +import com.central.common.utils.JsonUtil; import com.central.common.utils.RsaUtils; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.cloud.bootstrap.encrypt.KeyProperties; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.security.jwt.Jwt; import org.springframework.security.jwt.JwtHelper; +import org.springframework.security.jwt.crypto.sign.RsaSigner; import org.springframework.security.jwt.crypto.sign.RsaVerifier; import org.springframework.security.jwt.crypto.sign.SignatureVerifier; +import org.springframework.security.jwt.crypto.sign.Signer; +import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory; import java.io.BufferedReader; import java.io.InputStreamReader; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.stream.Collectors; @@ -46,10 +54,10 @@ public static RSAPublicKey getPubKeyObj() { * @param rsaPublicKey 公钥 * @return */ - public static JSONObject decodeAndVerify(String jwtToken, RSAPublicKey rsaPublicKey) { + public static JsonNode decodeAndVerify(String jwtToken, RSAPublicKey rsaPublicKey) { SignatureVerifier rsaVerifier = new RsaVerifier(rsaPublicKey); Jwt jwt = JwtHelper.decodeAndVerify(jwtToken, rsaVerifier); - return JSONObject.parseObject(jwt.getClaims()); + return JsonUtil.parse(jwt.getClaims()); } /** @@ -57,7 +65,7 @@ public static JSONObject decodeAndVerify(String jwtToken, RSAPublicKey rsaPublic * @param jwtToken token值 * @return */ - public static JSONObject decodeAndVerify(String jwtToken) { + public static JsonNode decodeAndVerify(String jwtToken) { return decodeAndVerify(jwtToken, getPubKeyObj()); } @@ -67,8 +75,8 @@ public static JSONObject decodeAndVerify(String jwtToken) { * @param currTime 当前时间 * @return 未过期:true,已过期:false */ - public static boolean checkExp(JSONObject claims, long currTime) { - long exp = claims.getLong("exp"); + public static boolean checkExp(JsonNode claims, long currTime) { + long exp = claims.get("exp").asLong(); if (exp < currTime) { return false; } @@ -80,7 +88,21 @@ public static boolean checkExp(JSONObject claims, long currTime) { * @param claims jwt内容 * @return 未过期:true,已过期:false */ - public static boolean checkExp(JSONObject claims) { + public static boolean checkExp(JsonNode claims) { return checkExp(claims, System.currentTimeMillis()); } + + public static Jwt encode(CharSequence content, KeyProperties keyProperties) { + KeyPair keyPair = new KeyStoreKeyFactory( + keyProperties.getKeyStore().getLocation(), + keyProperties.getKeyStore().getSecret().toCharArray()) + .getKeyPair(keyProperties.getKeyStore().getAlias()); + PrivateKey privateKey = keyPair.getPrivate(); + Signer rsaSigner = new RsaSigner((RSAPrivateKey) privateKey); + return JwtHelper.encode(content, rsaSigner); + } + + public static String encodeStr(CharSequence content, KeyProperties keyProperties) { + return encode(content, keyProperties).getEncoded(); + } } diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/resources/META-INF/spring.factories index f45f3e08..9862b1d3 100644 --- a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1,6 +1,2 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.central.oauth2.common.config.SecurityPropertiesConfig,\ -com.central.oauth2.common.store.AuthDbTokenStore,\ -com.central.oauth2.common.store.AuthRedisTokenStore,\ -com.central.oauth2.common.store.AuthJwtTokenStore,\ -com.central.oauth2.common.store.ResJwtTokenStore \ No newline at end of file +com.central.oauth2.common.AuthClientAutoConfiguration \ No newline at end of file diff --git a/zlt-commons/zlt-common-core/pom.xml b/zlt-commons/zlt-common-core/pom.xml index 998a39ca..693f390c 100644 --- a/zlt-commons/zlt-common-core/pom.xml +++ b/zlt-commons/zlt-common-core/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 zlt-common-core 公共通用组件 @@ -16,17 +16,15 @@ com.baomidou - mybatis-plus - true + mybatis-plus-extension org.springframework.social spring-social-security - org.springframework.security.oauth - spring-security-oauth2 - true + org.springframework.security + spring-security-core @@ -86,5 +84,10 @@ reactor-core true + + + com.fasterxml.jackson.core + jackson-databind + \ No newline at end of file diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/BannerInitializer.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/BannerInitializer.java index d02365d1..63ff6c20 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/BannerInitializer.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/BannerInitializer.java @@ -22,8 +22,8 @@ public void initialize(ConfigurableApplicationContext applicationContext) { if (!(applicationContext instanceof AnnotationConfigApplicationContext)) { LogoBanner logoBanner = new LogoBanner(BannerInitializer.class, "/zltmp/logo.txt", "Welcome to zlt", 5, 6, new Color[5], true); CustomBanner.show(logoBanner, new Description(BannerConstant.VERSION + ":", CommonConstant.PROJECT_VERSION, 0, 1) - , new Description("Gitee:", "https://gitee.com/zlt2000/microservices-platform", 0, 1) - , new Description("Blog:", "https://blog.csdn.net/zlt2000", 0, 1) + , new Description("Github:", "https://github.com/zlt2000", 0, 1) + , new Description("Blog:", "https://zlt2000.gitee.io", 0, 1) ); } } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/DefaultPasswordConfig.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/DefaultPasswordConfig.java index 372024cc..225736b2 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/DefaultPasswordConfig.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/DefaultPasswordConfig.java @@ -1,20 +1,22 @@ package com.central.common.config; +import com.central.common.utils.PwdEncoderUtil; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -/** -* @author zlt -* 密码工具类 -*/ +/** + * 密码加密配置类 + * + * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ public class DefaultPasswordConfig { - /** - * 装配BCryptPasswordEncoder用户密码的匹配 - * @return - */ @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); + @ConditionalOnMissingBean + public PasswordEncoder passwordEncoder() { + return PwdEncoderUtil.getDelegatingPasswordEncoder("bcrypt"); } } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/DefaultWebMvcConfig.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/DefaultWebMvcConfig.java index d2d446ee..225bf366 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/DefaultWebMvcConfig.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/DefaultWebMvcConfig.java @@ -16,7 +16,7 @@ * @author zlt * @date 2019/8/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ public class DefaultWebMvcConfig implements WebMvcConfigurer { diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/LoginArgResolverConfig.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/LoginArgResolverConfig.java deleted file mode 100644 index bbffbf4f..00000000 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/config/LoginArgResolverConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.central.common.config; - -import com.central.common.feign.UserService; -import com.central.common.resolver.ClientArgumentResolver; -import com.central.common.resolver.TokenArgumentResolver; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import java.util.List; - -/** - * 公共配置类, 一些公共工具配置 - * - * @author zlt - * @date 2018/8/25 - */ -public class LoginArgResolverConfig implements WebMvcConfigurer { - @Lazy - @Autowired - private UserService userService; - /** - * Token参数解析 - * - * @param argumentResolvers 解析类 - */ - @Override - public void addArgumentResolvers(List argumentResolvers) { - //注入用户信息 - argumentResolvers.add(new TokenArgumentResolver(userService)); - //注入应用信息 - argumentResolvers.add(new ClientArgumentResolver()); - } -} diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/CommonConstant.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/CommonConstant.java index 3c7b3238..e5a83b24 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/CommonConstant.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/CommonConstant.java @@ -10,7 +10,7 @@ public interface CommonConstant { /** * 项目版本号(banner使用) */ - String PROJECT_VERSION = "3.5.0"; + String PROJECT_VERSION = "5.4.0"; /** * token请求头名称 @@ -98,10 +98,11 @@ public interface CommonConstant { String SIMPLE_MONTH_FORMAT = "yyyyMM"; String SIMPLE_DATE_FORMAT = "yyyyMMdd"; String SIMPLE_DATETIME_FORMAT = "yyyyMMddHHmmss"; + String TIME_ZONE_GMT8 = "GMT+8"; String DEF_USER_PASSWORD = "123456"; - String LOCK_KEY_PREFIX = "LOCK_KEY:"; + String LOCK_KEY_PREFIX = "LOCK_KEY"; /** * 租户id参数 @@ -125,4 +126,9 @@ public interface CommonConstant { * 注册中心元数据 版本号 */ String METADATA_VERSION = "version"; + + /** + * 文件分隔符 + */ + String PATH_SPLIT = "/"; } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/ConfigConstants.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/ConfigConstants.java index cd8375cd..af2cadbd 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/ConfigConstants.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/ConfigConstants.java @@ -11,4 +11,14 @@ public interface ConfigConstants { * 是否开启自定义隔离规则 */ String CONFIG_RIBBON_ISOLATION_ENABLED = "zlt.ribbon.isolation.enabled"; + + String CONFIG_LOADBALANCE_ISOLATION = "zlt.loadbalance.isolation"; + + String CONFIG_LOADBALANCE_ISOLATION_ENABLE = CONFIG_LOADBALANCE_ISOLATION + ".enabled"; + + String CONFIG_LOADBALANCE_ISOLATION_CHOOSER = CONFIG_LOADBALANCE_ISOLATION + ".chooser"; + + String CONFIG_LOADBALANCE_VERSION = "zlt.loadbalance.version"; + + } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/SecurityConstants.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/SecurityConstants.java index 788ed929..4cca9640 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/SecurityConstants.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/constant/SecurityConstants.java @@ -4,6 +4,9 @@ * Security 权限常量 * * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public interface SecurityConstants { /** @@ -31,6 +34,11 @@ public interface SecurityConstants { */ String TENANT_HEADER = "x-tenant-header"; + /** + * 账号类型信息头 + */ + String ACCOUNT_TYPE_HEADER = "x-account-type-header"; + /** * 基础角色 */ @@ -122,10 +130,6 @@ public interface SecurityConstants { * OAUTH模式登录处理地址 */ String OAUTH_LOGIN_PRO_URL = "/user/login"; - /** - * PASSWORD模式登录处理地址 - */ - String PASSWORD_LOGIN_PRO_URL = "/oauth/user/token"; /** * 获取授权码地址 */ @@ -135,13 +139,9 @@ public interface SecurityConstants { */ String LOGIN_PAGE = "/login.html"; /** - * 默认的OPENID登录请求处理url + * 登录失败页面 */ - String OPENID_TOKEN_URL = "/oauth/openId/token"; - /** - * 手机登录URL - */ - String MOBILE_TOKEN_URL = "/oauth/mobile/token"; + String LOGIN_FAILURE_PAGE = LOGIN_PAGE + "?error"; /** * 登出URL */ @@ -154,6 +154,10 @@ public interface SecurityConstants { * redis中授权token对应的key */ String REDIS_TOKEN_AUTH = "auth:"; + /** + * 值同access 过期时间+60 + */ + String ACCESS_BAK = "access_bak:"; /** * redis中应用对应的token集合的key */ @@ -166,4 +170,23 @@ public interface SecurityConstants { * rsa公钥 */ String RSA_PUBLIC_KEY = "pubkey.txt"; + /** + * 获取id_token的response_type + */ + String ID_TOKEN = "id_token"; + + /** + * 令牌颁发者 + */ + String ISS = "http://zlt2000.cn"; + + /** + * 默认账号类型 + */ + String DEF_ACCOUNT_TYPE = "admin"; + + /** + * 账号类型参数名 + */ + String ACCOUNT_TYPE_PARAM_NAME = "account_type"; } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/context/LoginUserContextHolder.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/context/LoginUserContextHolder.java new file mode 100644 index 00000000..64500e98 --- /dev/null +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/context/LoginUserContextHolder.java @@ -0,0 +1,29 @@ +package com.central.common.context; + +import com.alibaba.ttl.TransmittableThreadLocal; +import com.central.common.model.SysUser; + +/** + * 登录用户holder + * + * @author zlt + * @date 2022/6/26 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class LoginUserContextHolder { + private static final ThreadLocal CONTEXT = new TransmittableThreadLocal<>(); + + public static void setUser(SysUser user) { + CONTEXT.set(user); + } + + public static SysUser getUser() { + return CONTEXT.get(); + } + + public static void clear() { + CONTEXT.remove(); + } +} \ No newline at end of file diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/feign/fallback/UserServiceFallbackFactory.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/feign/fallback/UserServiceFallbackFactory.java index cde1ff9a..1b7bbec8 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/feign/fallback/UserServiceFallbackFactory.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/feign/fallback/UserServiceFallbackFactory.java @@ -3,7 +3,7 @@ import com.central.common.feign.UserService; import com.central.common.model.LoginAppUser; import com.central.common.model.SysUser; -import feign.hystrix.FallbackFactory; +import org.springframework.cloud.openfeign.FallbackFactory; import lombok.extern.slf4j.Slf4j; /** diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/AbstractDistributedLock.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/AbstractDistributedLock.java deleted file mode 100644 index f2863f9c..00000000 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/AbstractDistributedLock.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.central.common.lock; - -/** - * 分布式锁抽象类 - * - * @author zlt - * @date 2018/5/29 14:14 - */ -public abstract class AbstractDistributedLock implements DistributedLock{ - - @Override - public boolean lock(String key) { - return lock(key, TIMEOUT_MILLIS, RETRY_TIMES, SLEEP_MILLIS); - } - - @Override - public boolean lock(String key, int retryTimes) { - return lock(key, TIMEOUT_MILLIS, retryTimes, SLEEP_MILLIS); - } - - @Override - public boolean lock(String key, int retryTimes, long sleepMillis) { - return lock(key, TIMEOUT_MILLIS, retryTimes, sleepMillis); - } - - @Override - public boolean lock(String key, long expire) { - return lock(key, expire, RETRY_TIMES, SLEEP_MILLIS); - } - - @Override - public boolean lock(String key, long expire, int retryTimes) { - return lock(key, expire, retryTimes, SLEEP_MILLIS); - } -} diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/DistributedLock.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/DistributedLock.java index b44043a5..6bdbefcd 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/DistributedLock.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/DistributedLock.java @@ -1,93 +1,72 @@ package com.central.common.lock; +import java.util.concurrent.TimeUnit; + /** * 分布式锁顶级接口 - * 例如: - * RETRY_TIMES=100,SLEEP_MILLIS=100 - * RETRY_TIMES * SLEEP_MILLIS = 10000 意味着如果一直获取不了锁,最长会等待10秒后抛超时异常 * * @author zlt * @date 2018/5/29 14:12 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public interface DistributedLock { - - /** - * 默认超时时间 - */ - long TIMEOUT_MILLIS = 5000; - /** - * 重试次数 + * 获取锁,如果获取不成功则一直等待直到lock被获取 + * @param key 锁的key + * @param leaseTime 加锁的时间,超过这个时间后锁便自动解锁; + * 如果leaseTime为-1,则保持锁定直到显式解锁 + * @param unit {@code leaseTime} 参数的时间单位 + * @param isFair 是否公平锁 + * @return 锁对象 */ - int RETRY_TIMES = 100; + ZLock lock(String key, long leaseTime, TimeUnit unit, boolean isFair) throws Exception; - /** - * 每次重试后等待的时间 - */ - long SLEEP_MILLIS = 100; + default ZLock lock(String key, long leaseTime, TimeUnit unit) throws Exception { + return this.lock(key, leaseTime, unit, false); + } + default ZLock lock(String key, boolean isFair) throws Exception { + return this.lock(key, -1, null, isFair); + } + default ZLock lock(String key) throws Exception { + return this.lock(key, -1, null, false); + } /** - * 获取锁 - * - * @param key key - * @return 成功/失败 + * 尝试获取锁,如果锁不可用则等待最多waitTime时间后放弃 + * @param key 锁的key + * @param waitTime 获取锁的最大尝试时间(单位 {@code unit}) + * @param leaseTime 加锁的时间,超过这个时间后锁便自动解锁; + * 如果leaseTime为-1,则保持锁定直到显式解锁 + * @param unit {@code waitTime} 和 {@code leaseTime} 参数的时间单位 + * @return 锁对象,如果获取锁失败则为null */ - boolean lock(String key); + ZLock tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, boolean isFair) throws Exception; - /** - * 获取锁 - * - * @param key key - * @param retryTimes 重试次数 - * @return 成功/失败 - */ - boolean lock(String key, int retryTimes); + default ZLock tryLock(String key, long waitTime, long leaseTime, TimeUnit unit) throws Exception { + return this.tryLock(key, waitTime, leaseTime, unit, false); + } + default ZLock tryLock(String key, long waitTime, TimeUnit unit, boolean isFair) throws Exception { + return this.tryLock(key, waitTime, -1, unit, isFair); + } + default ZLock tryLock(String key, long waitTime, TimeUnit unit) throws Exception { + return this.tryLock(key, waitTime, -1, unit, false); + } /** - * 获取锁 - * - * @param key key - * @param retryTimes 重试次数 - * @param sleepMillis 获取锁失败的重试间隔 - * @return 成功/失败 - */ - boolean lock(String key, int retryTimes, long sleepMillis); - - /** - * 获取锁 - * - * @param key key - * @param expire 获取锁超时时间 - * @return 成功/失败 - */ - boolean lock(String key, long expire); - - /** - * 获取锁 - * - * @param key key - * @param expire 获取锁超时时间 - * @param retryTimes 重试次数 - * @return 成功/失败 - */ - boolean lock(String key, long expire, int retryTimes); - - /** - * 获取锁 - * - * @param key key - * @param expire 获取锁超时时间 - * @param retryTimes 重试次数 - * @param sleepMillis 获取锁失败的重试间隔 - * @return 成功/失败 + * 释放锁 + * @param lock 锁对象 */ - boolean lock(String key, long expire, int retryTimes, long sleepMillis); + void unlock(Object lock) throws Exception; /** * 释放锁 - * - * @param key key值 - * @return 释放结果 + * @param zLock 锁抽象对象 */ - boolean releaseLock(String key); + default void unlock(ZLock zLock) throws Exception { + if (zLock != null) { + this.unlock(zLock.getLock()); + } + } } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/Lock.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/Lock.java new file mode 100644 index 00000000..28d35749 --- /dev/null +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/Lock.java @@ -0,0 +1,39 @@ +package com.central.common.lock; + +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; + +/** + * @author zlt + * @date 2020/6/6 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Lock { + /** + * 锁的key + */ + String key(); + /** + * 获取锁的最大尝试时间(单位 {@code unit}) + * 该值大于0则使用 locker.tryLock 方法加锁,否则使用 locker.lock 方法 + */ + long waitTime() default 0; + /** + * 加锁的时间(单位 {@code unit}),超过这个时间后锁便自动解锁; + * 如果leaseTime为-1,则保持锁定直到显式解锁 + */ + long leaseTime() default -1; + /** + * 参数的时间单位 + */ + TimeUnit unit() default TimeUnit.SECONDS; + /** + * 是否公平锁 + */ + boolean isFair() default false; +} diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/LockAspect.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/LockAspect.java new file mode 100644 index 00000000..f160e335 --- /dev/null +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/LockAspect.java @@ -0,0 +1,98 @@ +package com.central.common.lock; + +import cn.hutool.core.util.StrUtil; +import com.central.common.exception.LockException; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +/** + * 分布式锁切面 + * + * @author zlt + * @date 2020/6/6 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@Aspect +public class LockAspect { + @Autowired(required = false) + private DistributedLock locker; + + /** + * 用于SpEL表达式解析. + */ + private SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); + /** + * 用于获取方法参数定义名字. + */ + private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); + + @Around("@within(lock) || @annotation(lock)") + public Object aroundLock(ProceedingJoinPoint point, Lock lock) throws Throwable { + if (lock == null) { + // 获取类上的注解 + lock = point.getTarget().getClass().getDeclaredAnnotation(Lock.class); + } + String lockKey = lock.key(); + if (locker == null) { + throw new LockException("DistributedLock is null"); + } + if (StrUtil.isEmpty(lockKey)) { + throw new LockException("lockKey is null"); + } + + if (lockKey.contains("#")) { + MethodSignature methodSignature = (MethodSignature)point.getSignature(); + //获取方法参数值 + Object[] args = point.getArgs(); + lockKey = getValBySpEL(lockKey, methodSignature, args); + } + ZLock lockObj = null; + try { + //加锁 + if (lock.waitTime() > 0) { + lockObj = locker.tryLock(lockKey, lock.waitTime(), lock.leaseTime(), lock.unit(), lock.isFair()); + } else { + lockObj = locker.lock(lockKey, lock.leaseTime(), lock.unit(), lock.isFair()); + } + + if (lockObj != null) { + return point.proceed(); + } else { + throw new LockException("锁等待超时"); + } + } finally { + locker.unlock(lockObj); + } + } + + /** + * 解析spEL表达式 + */ + private String getValBySpEL(String spEL, MethodSignature methodSignature, Object[] args) { + //获取方法形参名数组 + String[] paramNames = nameDiscoverer.getParameterNames(methodSignature.getMethod()); + if (paramNames != null && paramNames.length > 0) { + Expression expression = spelExpressionParser.parseExpression(spEL); + // spring的表达式上下文对象 + EvaluationContext context = new StandardEvaluationContext(); + // 给上下文赋值 + for(int i = 0; i < args.length; i++) { + context.setVariable(paramNames[i], args[i]); + } + return expression.getValue(context).toString(); + } + return null; + } +} diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/ZLock.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/ZLock.java new file mode 100644 index 00000000..ffe60769 --- /dev/null +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/lock/ZLock.java @@ -0,0 +1,26 @@ +package com.central.common.lock; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 锁对象抽象 + * + * @author zlt + * @date 2020/7/28 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@AllArgsConstructor +public class ZLock implements AutoCloseable { + @Getter + private final Object lock; + + private final DistributedLock locker; + + @Override + public void close() throws Exception { + locker.unlock(lock); + } +} diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/LoginAppUser.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/LoginAppUser.java index 2001764b..558b5c89 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/LoginAppUser.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/LoginAppUser.java @@ -33,7 +33,7 @@ public class LoginAppUser extends SysUser implements SocialUserDetails { public Collection getAuthorities() { Collection collection = new HashSet<>(); if (!CollectionUtils.isEmpty(super.getRoles())) { - super.getRoles().parallelStream().forEach(role -> collection.add(new SimpleGrantedAuthority(role.getCode()))); + super.getRoles().forEach(role -> collection.add(new SimpleGrantedAuthority(role.getCode()))); } return collection; } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/SuperEntity.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/SuperEntity.java index f7778346..30894a40 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/SuperEntity.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/SuperEntity.java @@ -27,9 +27,4 @@ public class SuperEntity> extends Model { private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; - - @Override - protected Serializable pkVal() { - return this.id; - } } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/SysRole.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/SysRole.java index 303071b4..45f050bb 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/SysRole.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/model/SysRole.java @@ -1,5 +1,6 @@ package com.central.common.model; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,5 +16,6 @@ public class SysRole extends SuperEntity { private static final long serialVersionUID = 4497149010220586111L; private String code; private String name; + @TableField(exist = false) private Long userId; } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/resolver/TokenArgumentResolver.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/resolver/TokenArgumentResolver.java index 19a1183f..d035c708 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/resolver/TokenArgumentResolver.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/resolver/TokenArgumentResolver.java @@ -6,8 +6,12 @@ import com.central.common.feign.UserService; import com.central.common.model.SysRole; import com.central.common.model.SysUser; +import com.central.common.utils.LoginUserUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.core.MethodParameter; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -23,6 +27,9 @@ * * @author zlt * @date 2018/12/21 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Slf4j public class TokenArgumentResolver implements HandlerMethodArgumentResolver { @@ -58,28 +65,9 @@ public Object resolveArgument(MethodParameter methodParameter, LoginUser loginUser = methodParameter.getParameterAnnotation(LoginUser.class); boolean isFull = loginUser.isFull(); HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class); - String userId = request.getHeader(SecurityConstants.USER_ID_HEADER); - String username = request.getHeader(SecurityConstants.USER_HEADER); - String roles = request.getHeader(SecurityConstants.ROLE_HEADER); - if (StrUtil.isBlank(username)) { - log.warn("resolveArgument error username is empty"); - return null; - } - SysUser user; - if (isFull) { - user = userService.selectByUsername(username); - } else { - user = new SysUser(); - user.setId(Long.valueOf(userId)); - user.setUsername(username); - } - List sysRoleList = new ArrayList<>(); - Arrays.stream(roles.split(",")).forEach(role -> { - SysRole sysRole = new SysRole(); - sysRole.setCode(role); - sysRoleList.add(sysRole); - }); - user.setRoles(sysRoleList); - return user; + //账号类型 + String accountType = request.getHeader(SecurityConstants.ACCOUNT_TYPE_HEADER); + + return LoginUserUtils.getCurrentUser(request, isFull); } } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/service/ISuperService.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/service/ISuperService.java index 95e258bb..97af475f 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/service/ISuperService.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/service/ISuperService.java @@ -9,51 +9,46 @@ * * @author zlt * @date 2019/1/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public interface ISuperService extends IService { /** * 幂等性新增记录 + * 例子如下: + * String username = sysUser.getUsername(); + * boolean result = super.saveIdempotency(sysUser, lock + * , LOCK_KEY_USERNAME+username + * , new QueryWrapper().eq("username", username)); * * @param entity 实体对象 - * @param lock 锁实例 + * @param locker 锁实例 * @param lockKey 锁的key * @param countWrapper 判断是否存在的条件 * @param msg 对象已存在提示信息 * @return */ - boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper, String msg); + boolean saveIdempotency(T entity, DistributedLock locker, String lockKey, Wrapper countWrapper, String msg) throws Exception; - /** - * 幂等性新增记录 - * - * @param entity 实体对象 - * @param lock 锁实例 - * @param lockKey 锁的key - * @param countWrapper 判断是否存在的条件 - * @return - */ - boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper); + boolean saveIdempotency(T entity, DistributedLock locker, String lockKey, Wrapper countWrapper) throws Exception; /** * 幂等性新增或更新记录 + * 例子如下: + * String username = sysUser.getUsername(); + * boolean result = super.saveOrUpdateIdempotency(sysUser, lock + * , LOCK_KEY_USERNAME+username + * , new QueryWrapper().eq("username", username)); * * @param entity 实体对象 - * @param lock 锁实例 + * @param locker 锁实例 * @param lockKey 锁的key * @param countWrapper 判断是否存在的条件 * @param msg 对象已存在提示信息 * @return */ - boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper, String msg); + boolean saveOrUpdateIdempotency(T entity, DistributedLock locker, String lockKey, Wrapper countWrapper, String msg) throws Exception; - /** - * 幂等性新增或更新记录 - * - * @param entity 实体对象 - * @param lock 锁实例 - * @param lockKey 锁的key - * @param countWrapper 判断是否存在的条件 - * @return - */ - boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper); + boolean saveOrUpdateIdempotency(T entity, DistributedLock locker, String lockKey, Wrapper countWrapper) throws Exception; } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/service/impl/SuperServiceImpl.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/service/impl/SuperServiceImpl.java index 0eab2a52..8c678084 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/service/impl/SuperServiceImpl.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/service/impl/SuperServiceImpl.java @@ -12,47 +12,37 @@ import com.central.common.exception.IdempotencyException; import com.central.common.exception.LockException; import com.central.common.lock.DistributedLock; +import com.central.common.lock.ZLock; import com.central.common.service.ISuperService; import java.io.Serializable; import java.util.Objects; +import java.util.concurrent.TimeUnit; /** * service实现父类 * * @author zlt * @date 2019/1/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public class SuperServiceImpl, T> extends ServiceImpl implements ISuperService { - /** - * 幂等性新增记录 - * 例子如下: - * String username = sysUser.getUsername(); - * boolean result = super.saveIdempotency(sysUser, lock - * , LOCK_KEY_USERNAME+username - * , new QueryWrapper().eq("username", username)); - * - * @param entity 实体对象 - * @param lock 锁实例 - * @param lockKey 锁的key - * @param countWrapper 判断是否存在的条件 - * @param msg 对象已存在提示信息 - * @return - */ @Override - public boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper, String msg) { - if (lock == null) { + public boolean saveIdempotency(T entity, DistributedLock locker, String lockKey, Wrapper countWrapper, String msg) throws Exception { + if (locker == null) { throw new LockException("DistributedLock is null"); } if (StrUtil.isEmpty(lockKey)) { throw new LockException("lockKey is null"); } - try { - //加锁 - boolean isLock = lock.lock(lockKey); - if (isLock) { + try ( + ZLock lock = locker.tryLock(lockKey, 10, 60, TimeUnit.SECONDS); + ) { + if (lock != null) { //判断记录是否已存在 - int count = super.count(countWrapper); + long count = super.count(countWrapper); if (count == 0) { return super.save(entity); } else { @@ -64,47 +54,21 @@ public boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, W } else { throw new LockException("锁等待超时"); } - } finally { - lock.releaseLock(lockKey); } } - /** - * 幂等性新增记录 - * - * @param entity 实体对象 - * @param lock 锁实例 - * @param lockKey 锁的key - * @param countWrapper 判断是否存在的条件 - * @return - */ @Override - public boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper) { + public boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper) throws Exception { return saveIdempotency(entity, lock, lockKey, countWrapper, null); } - /** - * 幂等性新增或更新记录 - * 例子如下: - * String username = sysUser.getUsername(); - * boolean result = super.saveOrUpdateIdempotency(sysUser, lock - * , LOCK_KEY_USERNAME+username - * , new QueryWrapper().eq("username", username)); - * - * @param entity 实体对象 - * @param lock 锁实例 - * @param lockKey 锁的key - * @param countWrapper 判断是否存在的条件 - * @param msg 对象已存在提示信息 - * @return - */ @Override - public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper, String msg) { + public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper, String msg) throws Exception { if (null != entity) { Class cls = entity.getClass(); TableInfo tableInfo = TableInfoHelper.getTableInfo(cls); if (null != tableInfo && StrUtil.isNotEmpty(tableInfo.getKeyProperty())) { - Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty()); + Object idVal = ReflectionKit.getFieldValue(entity, tableInfo.getKeyProperty()); if (StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) { if (StrUtil.isEmpty(msg)) { msg = "已存在"; @@ -120,22 +84,8 @@ public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lo return false; } - /** - * 幂等性新增或更新记录 - * 例子如下: - * String username = sysUser.getUsername(); - * boolean result = super.saveOrUpdateIdempotency(sysUser, lock - * , LOCK_KEY_USERNAME+username - * , new QueryWrapper().eq("username", username)); - * - * @param entity 实体对象 - * @param lock 锁实例 - * @param lockKey 锁的key - * @param countWrapper 判断是否存在的条件 - * @return - */ @Override - public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper) { + public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper countWrapper) throws Exception { return this.saveOrUpdateIdempotency(entity, lock, lockKey, countWrapper, null); } } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/JsonUtil.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/JsonUtil.java new file mode 100644 index 00000000..575dd56d --- /dev/null +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/JsonUtil.java @@ -0,0 +1,209 @@ +package com.central.common.utils; + +import com.central.common.constant.CommonConstant; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.json.JsonReadFeature; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +/** + * 基于 Jackson 的 json 工具类 + * + * @author zlt + * @date 2020/10/8 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class JsonUtil { + private final static ObjectMapper MAPPER = new ObjectMapper(); + + static { + // 忽略在json字符串中存在,但是在java对象中不存在对应属性的情况 + MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + // 忽略空Bean转json的错误 + MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS,false); + // 允许不带引号的字段名称 + MAPPER.configure(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES.mappedFeature(), true); + // 允许单引号 + MAPPER.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true); + // allow int startWith 0 + MAPPER.configure(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS.mappedFeature(), true); + // 允许字符串存在转义字符:\r \n \t + MAPPER.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + // 排除空值字段 + MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); + // 使用驼峰式 + MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE); + // 使用bean名称 + MAPPER.enable(MapperFeature.USE_STD_BEAN_NAMING); + // 所有日期格式都统一为固定格式 + MAPPER.setDateFormat(new SimpleDateFormat(CommonConstant.DATETIME_FORMAT)); + MAPPER.setTimeZone(TimeZone.getTimeZone(CommonConstant.TIME_ZONE_GMT8)); + } + + /** + * 对象转换为json字符串 + * @param o 要转换的对象 + */ + public static String toJSONString(Object o) { + return toJSONString(o, false); + } + + /** + * 对象转换为json字符串 + * @param o 要转换的对象 + * @param format 是否格式化json + */ + public static String toJSONString(Object o, boolean format) { + try { + if (o == null) { + return ""; + } + if (o instanceof Number) { + return o.toString(); + } + if (o instanceof String) { + return (String)o; + } + if (format) { + return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(o); + } + return MAPPER.writeValueAsString(o); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + /** + * 字符串转换为指定对象 + * @param json json字符串 + * @param cls 目标对象 + */ + public static T toObject(JsonNode json, Class cls) { + if(json == null || cls == null){ + return null; + } + return MAPPER.convertValue(json, cls); + } + + /** + * 字符串转换为指定对象,并增加泛型转义 + * 例如:List test = toObject(jsonStr, List.class, Integer.class); + * @param json json字符串 + * @param parametrized 目标对象 + * @param parameterClasses 泛型对象 + */ + public static T toObject(String json, Class parametrized, Class... parameterClasses) { + if(StringUtils.isBlank(json) || parametrized == null){ + return null; + } + try { + JavaType javaType = MAPPER.getTypeFactory().constructParametricType(parametrized, parameterClasses); + return MAPPER.readValue(json, javaType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 字符串转换为指定对象 + * @param json json字符串 + * @param typeReference 目标对象类型 + */ + public static T toObject(String json, TypeReference typeReference) { + if(StringUtils.isBlank(json) || typeReference == null){ + return null; + } + try { + return MAPPER.readValue(json, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 字符串转换为指定对象 + * @param json json字符串 + * @param cls 目标对象 + */ + public static T toObject(String json, Class cls) { + if(StringUtils.isBlank(json) || cls == null){ + return null; + } + try { + return MAPPER.readValue(json, cls); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 字符串转换为JsonNode对象 + * @param json json字符串 + */ + public static JsonNode parse(String json) { + if (StringUtils.isBlank(json)) { + return null; + } + try { + return MAPPER.readTree(json); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 对象转换为map对象 + * @param o 要转换的对象 + */ + public static Map toMap(Object o) { + if (o == null) { + return null; + } + if (o instanceof String) { + return toObject((String)o, Map.class); + } + return MAPPER.convertValue(o, Map.class); + } + + /** + * json字符串转换为list对象 + * @param json json字符串 + */ + public static List toList(String json) { + if (StringUtils.isBlank(json)) { + return null; + } + try { + return MAPPER.readValue(json, List.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + /** + * json字符串转换为list对象,并指定元素类型 + * @param json json字符串 + * @param cls list的元素类型 + */ + public static List toList(String json, Class cls) { + if (StringUtils.isBlank(json)) { + return null; + } + try { + JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, cls); + return MAPPER.readValue(json, javaType); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/LoginUserUtils.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/LoginUserUtils.java new file mode 100644 index 00000000..3fdf1be4 --- /dev/null +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/LoginUserUtils.java @@ -0,0 +1,69 @@ +package com.central.common.utils; + +import cn.hutool.core.util.StrUtil; +import com.central.common.constant.SecurityConstants; +import com.central.common.feign.UserService; +import com.central.common.model.SysRole; +import com.central.common.model.SysUser; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 获取当前登录人工具类 + * + * @author zlt + * @version 1.0 + * @date 2022/6/26 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class LoginUserUtils { + /** + * 获取当前登录人 + */ + public static SysUser getCurrentUser(HttpServletRequest request, boolean isFull) { + SysUser user = null; + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken)) { + Object principal = authentication.getPrincipal(); + //客户端模式只返回一个clientId + if (principal instanceof SysUser) { + user = (SysUser)principal; + } + } + if (user == null) { + String userId = request.getHeader(SecurityConstants.USER_ID_HEADER); + String username = request.getHeader(SecurityConstants.USER_HEADER); + String roles = request.getHeader(SecurityConstants.ROLE_HEADER); + + if (StrUtil.isAllNotBlank(username, userId)) { + if (isFull) { + UserService userService = SpringUtil.getBean(UserService.class); + user = userService.selectByUsername(username); + } else { + user = new SysUser(); + user.setId(Long.valueOf(userId)); + user.setUsername(username); + } + if (StrUtil.isNotBlank(roles)) { + List sysRoleList = new ArrayList<>(); + Arrays.stream(roles.split(",")).forEach(role -> { + SysRole sysRole = new SysRole(); + sysRole.setCode(role); + sysRoleList.add(sysRole); + }); + user.setRoles(sysRoleList); + } + } + } + return user; + } +} diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/PwdEncoderUtil.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/PwdEncoderUtil.java new file mode 100644 index 00000000..dfe0b18c --- /dev/null +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/PwdEncoderUtil.java @@ -0,0 +1,45 @@ +package com.central.common.utils; + +import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.DelegatingPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; +import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; +import org.springframework.util.Assert; + +import java.util.HashMap; +import java.util.Map; + +/** + * PasswordEncoder实现工具类 + * + * @author zlt + * @version 1.0 + * @date 2021/5/7 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class PwdEncoderUtil { + public static PasswordEncoder getDelegatingPasswordEncoder(String encodingId) { + Map encoders = new HashMap<>(); + encoders.put("bcrypt", new BCryptPasswordEncoder()); + encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder()); + encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder()); + encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5")); + encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()); + encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); + encoders.put("scrypt", new SCryptPasswordEncoder()); + encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1")); + encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256")); + encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder()); + encoders.put("argon2", new Argon2PasswordEncoder()); + + Assert.isTrue(encoders.containsKey(encodingId), encodingId + " is not found in idToPasswordEncoder"); + + DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(encodingId, encoders); + delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(encoders.get(encodingId)); + return delegatingPasswordEncoder; + } +} diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/ResponseUtil.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/ResponseUtil.java index f489366c..2da8db68 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/ResponseUtil.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/ResponseUtil.java @@ -1,21 +1,12 @@ package com.central.common.utils; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.Result; import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferFactory; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.Writer; -import java.nio.charset.Charset; /** * @author zlt @@ -63,7 +54,7 @@ public static void responseFailed(ObjectMapper objectMapper, HttpServletResponse } private static void responseWrite(ObjectMapper objectMapper, HttpServletResponse response, Result result) throws IOException { - response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); try ( Writer writer = response.getWriter() ) { @@ -71,38 +62,4 @@ private static void responseWrite(ObjectMapper objectMapper, HttpServletResponse writer.flush(); } } - - /** - * webflux的response返回json对象 - */ - public static Mono responseWriter(ServerWebExchange exchange, int httpStatus, String msg) { - Result result = Result.of(null, httpStatus, msg); - return responseWrite(exchange, httpStatus, result); - } - - public static Mono responseFailed(ServerWebExchange exchange, String msg) { - Result result = Result.failed(msg); - return responseWrite(exchange, HttpStatus.INTERNAL_SERVER_ERROR.value(), result); - } - - public static Mono responseFailed(ServerWebExchange exchange, int httpStatus, String msg) { - Result result = Result.failed(msg); - return responseWrite(exchange, httpStatus, result); - } - - public static Mono responseWrite(ServerWebExchange exchange, int httpStatus, Result result) { - if (httpStatus == 0) { - httpStatus = HttpStatus.INTERNAL_SERVER_ERROR.value(); - } - ServerHttpResponse response = exchange.getResponse(); - response.getHeaders().setAccessControlAllowCredentials(true); - response.getHeaders().setAccessControlAllowOrigin("*"); - response.setStatusCode(HttpStatus.valueOf(httpStatus)); - response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); - DataBufferFactory dataBufferFactory = response.bufferFactory(); - DataBuffer buffer = dataBufferFactory.wrap(JSONObject.toJSONString(result).getBytes(Charset.defaultCharset())); - return response.writeWith(Mono.just(buffer)).doOnError((error) -> { - DataBufferUtils.release(buffer); - }); - } } diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/Sequence.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/Sequence.java index e40f2250..efb246e4 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/Sequence.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/Sequence.java @@ -177,8 +177,8 @@ public synchronized long nextId() { timestamp = tilNextMillis(lastTimestamp); } } else { - // 不同毫秒内,序列号置为 1 - 3 随机数 - sequence = ThreadLocalRandom.current().nextLong(1, 3); + // 不同毫秒内,序列号置为 1 - 100 随机数 + sequence = ThreadLocalRandom.current().nextLong(1, 101); } lastTimestamp = timestamp; diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/SpringUtil.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/SpringUtil.java index 687b6bab..678fca78 100644 --- a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/SpringUtil.java +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/SpringUtil.java @@ -2,6 +2,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; @@ -11,6 +12,7 @@ * @author 作者 owen E-mail: 624191343@qq.com */ @Component +@Order(0) public class SpringUtil implements ApplicationContextAware { private static ApplicationContext applicationContext = null; diff --git a/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/WebfluxResponseUtil.java b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/WebfluxResponseUtil.java new file mode 100644 index 00000000..14d7dc9a --- /dev/null +++ b/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/WebfluxResponseUtil.java @@ -0,0 +1,56 @@ +package com.central.common.utils; + +import com.central.common.model.Result; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.nio.charset.Charset; + +/** + * @author zlt + * @date 2020/5/5 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class WebfluxResponseUtil { + /** + * webflux的response返回json对象 + */ + public static Mono responseWriter(ServerWebExchange exchange, int httpStatus, String msg) { + Result result = Result.of(null, httpStatus, msg); + return responseWrite(exchange, httpStatus, result); + } + + public static Mono responseFailed(ServerWebExchange exchange, String msg) { + Result result = Result.failed(msg); + return responseWrite(exchange, HttpStatus.INTERNAL_SERVER_ERROR.value(), result); + } + + public static Mono responseFailed(ServerWebExchange exchange, int httpStatus, String msg) { + Result result = Result.failed(msg); + return responseWrite(exchange, httpStatus, result); + } + + public static Mono responseWrite(ServerWebExchange exchange, int httpStatus, Result result) { + if (httpStatus == 0) { + httpStatus = HttpStatus.INTERNAL_SERVER_ERROR.value(); + } + ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().setAccessControlAllowCredentials(true); + response.getHeaders().setAccessControlAllowOrigin("*"); + response.setStatusCode(HttpStatus.valueOf(httpStatus)); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + DataBufferFactory dataBufferFactory = response.bufferFactory(); + DataBuffer buffer = dataBufferFactory.wrap(JsonUtil.toJSONString(result).getBytes(Charset.defaultCharset())); + return response.writeWith(Mono.just(buffer)).doOnError((error) -> { + DataBufferUtils.release(buffer); + }); + } +} diff --git a/zlt-commons/zlt-common-core/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-common-core/src/main/resources/META-INF/spring.factories index 247a1118..091a4a1e 100644 --- a/zlt-commons/zlt-common-core/src/main/resources/META-INF/spring.factories +++ b/zlt-commons/zlt-common-core/src/main/resources/META-INF/spring.factories @@ -2,4 +2,6 @@ org.springframework.context.ApplicationContextInitializer=\ com.central.common.config.BannerInitializer org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.central.common.feign.fallback.UserServiceFallbackFactory \ No newline at end of file +com.central.common.feign.fallback.UserServiceFallbackFactory,\ +com.central.common.lock.LockAspect,\ +com.central.common.utils.SpringUtil \ No newline at end of file diff --git a/zlt-commons/zlt-common-spring-boot-starter/pom.xml b/zlt-commons/zlt-common-spring-boot-starter/pom.xml index 9c0225b3..c327e8d9 100644 --- a/zlt-commons/zlt-common-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-common-spring-boot-starter/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 zlt-common-spring-boot-starter 公共通用组件 diff --git a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/filter/LoginUserFilter.java b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/filter/LoginUserFilter.java new file mode 100644 index 00000000..21c9a3eb --- /dev/null +++ b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/filter/LoginUserFilter.java @@ -0,0 +1,40 @@ +package com.central.common.filter; + +import com.central.common.context.LoginUserContextHolder; +import com.central.common.model.SysUser; +import com.central.common.utils.LoginUserUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.core.annotation.Order; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 获取当前登录人过滤器 + * + * @author zlt + * @date 2022/6/26 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@ConditionalOnClass(Filter.class) +@Order +public class LoginUserFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws IOException, ServletException { + try { + SysUser user = LoginUserUtils.getCurrentUser(request, false); + LoginUserContextHolder.setUser(user); + filterChain.doFilter(request, response); + } finally { + LoginUserContextHolder.clear(); + } + } +} diff --git a/zlt-commons/zlt-common-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-common-spring-boot-starter/src/main/resources/META-INF/spring.factories index a277125f..ffb5ffd9 100644 --- a/zlt-commons/zlt-common-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/zlt-commons/zlt-common-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1,3 +1,3 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.central.common.filter.TenantFilter,\ -com.central.common.filter.TraceFilter \ No newline at end of file +com.central.common.filter.TenantFilter, \ +com.central.common.filter.LoginUserFilter \ No newline at end of file diff --git a/zlt-commons/zlt-db-spring-boot-starter/pom.xml b/zlt-commons/zlt-db-spring-boot-starter/pom.xml index 69c4e936..f4dd384c 100644 --- a/zlt-commons/zlt-db-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-db-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 4.0.0 jar diff --git a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/DateMetaObjectHandler.java b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/DateMetaObjectHandler.java index 5c258020..cf86429e 100644 --- a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/DateMetaObjectHandler.java +++ b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/DateMetaObjectHandler.java @@ -12,7 +12,7 @@ * @author zlt * @date 2018/12/11 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ public class DateMetaObjectHandler implements MetaObjectHandler { diff --git a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/MybatisPlusAutoConfigure.java b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/MybatisPlusAutoConfigure.java index 05635297..f5f44853 100644 --- a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/MybatisPlusAutoConfigure.java +++ b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/MybatisPlusAutoConfigure.java @@ -1,12 +1,12 @@ package com.central.db.config; -import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; -import com.baomidou.mybatisplus.core.parser.ISqlParserFilter; -import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; -import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler; -import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.central.common.properties.TenantProperties; +import com.central.db.interceptor.CustomTenantInterceptor; import com.central.db.properties.MybatisPlusAutoFillProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -20,16 +20,13 @@ * @author zlt * @date 2020/4/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @EnableConfigurationProperties(MybatisPlusAutoFillProperties.class) public class MybatisPlusAutoConfigure { @Autowired - private TenantHandler tenantHandler; - - @Autowired - private ISqlParserFilter sqlParserFilter; + private TenantLineHandler tenantLineHandler; @Autowired private TenantProperties tenantProperties; @@ -41,17 +38,17 @@ public class MybatisPlusAutoConfigure { * 分页插件,自动识别数据库类型 */ @Bean - public PaginationInterceptor paginationInterceptor() { - PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); + public MybatisPlusInterceptor paginationInterceptor() { + MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor(); boolean enableTenant = tenantProperties.getEnable(); //是否开启多租户隔离 if (enableTenant) { - TenantSqlParser tenantSqlParser = new TenantSqlParser() - .setTenantHandler(tenantHandler); - paginationInterceptor.setSqlParserList(CollUtil.toList(tenantSqlParser)); - paginationInterceptor.setSqlParserFilter(sqlParserFilter); + CustomTenantInterceptor tenantInterceptor = new CustomTenantInterceptor( + tenantLineHandler, tenantProperties.getIgnoreSqls()); + mpInterceptor.addInnerInterceptor(tenantInterceptor); } - return paginationInterceptor; + mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return mpInterceptor; } @Bean diff --git a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/TenantAutoConfigure.java b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/TenantAutoConfigure.java index 40f38d6e..8778dd6d 100644 --- a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/TenantAutoConfigure.java +++ b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/config/TenantAutoConfigure.java @@ -1,14 +1,11 @@ package com.central.db.config; -import com.baomidou.mybatisplus.core.parser.ISqlParserFilter; -import com.baomidou.mybatisplus.core.parser.SqlParserHelper; -import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import com.central.common.context.TenantContextHolder; import com.central.common.properties.TenantProperties; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.NullValue; import net.sf.jsqlparser.expression.StringValue; -import org.apache.ibatis.mapping.MappedStatement; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -25,13 +22,13 @@ public class TenantAutoConfigure { private TenantProperties tenantProperties; @Bean - public TenantHandler tenantHandler() { - return new TenantHandler() { + public TenantLineHandler tenantLineHandler() { + return new TenantLineHandler() { /** * 获取租户id */ @Override - public Expression getTenantId(boolean where) { + public Expression getTenantId() { String tenant = TenantContextHolder.getTenant(); if (tenant != null) { return new StringValue(TenantContextHolder.getTenant()); @@ -39,37 +36,16 @@ public Expression getTenantId(boolean where) { return new NullValue(); } - /** - * 获取租户列名 - */ - @Override - public String getTenantIdColumn() { - return "tenant_id"; - } - /** * 过滤不需要根据租户隔离的表 * @param tableName 表名 */ @Override - public boolean doTableFilter(String tableName) { + public boolean ignoreTable(String tableName) { return tenantProperties.getIgnoreTables().stream().anyMatch( (e) -> e.equalsIgnoreCase(tableName) ); } }; } - - /** - * 过滤不需要根据租户隔离的MappedStatement - */ - @Bean - public ISqlParserFilter sqlParserFilter() { - return metaObject -> { - MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject); - return tenantProperties.getIgnoreSqls().stream().anyMatch( - (e) -> e.equalsIgnoreCase(ms.getId()) - ); - }; - } } diff --git a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/interceptor/CustomTenantInterceptor.java b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/interceptor/CustomTenantInterceptor.java new file mode 100644 index 00000000..f2083e7f --- /dev/null +++ b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/interceptor/CustomTenantInterceptor.java @@ -0,0 +1,44 @@ +package com.central.db.interceptor; + +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import java.sql.SQLException; +import java.util.List; + +/** + * MyBatis-plus租户拦截器 + * + * @author zlt + * @version 1.0 + * @date 2022/5/6 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class CustomTenantInterceptor extends TenantLineInnerInterceptor { + private List ignoreSqls; + + public CustomTenantInterceptor(TenantLineHandler tenantLineHandler, List ignoreSqls) { + super(tenantLineHandler); + this.ignoreSqls = ignoreSqls; + } + + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds + , ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + if (isIgnoreMappedStatement(ms.getId())) { + return; + } + super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql); + } + + private boolean isIgnoreMappedStatement(String msId) { + return ignoreSqls.stream().anyMatch((e) -> e.equalsIgnoreCase(msId)); + } +} diff --git a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/properties/MybatisPlusAutoFillProperties.java b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/properties/MybatisPlusAutoFillProperties.java index fe50bdd7..44b88f0f 100644 --- a/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/properties/MybatisPlusAutoFillProperties.java +++ b/zlt-commons/zlt-db-spring-boot-starter/src/main/java/com/central/db/properties/MybatisPlusAutoFillProperties.java @@ -11,7 +11,7 @@ * @author zlt * @date 2020/4/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Setter diff --git a/zlt-commons/zlt-elasticsearch-spring-boot-starter/pom.xml b/zlt-commons/zlt-elasticsearch-spring-boot-starter/pom.xml index 4377c535..97cea36f 100644 --- a/zlt-commons/zlt-elasticsearch-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-elasticsearch-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 4.0.0 jar diff --git a/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/config/RestAutoConfigure.java b/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/config/RestAutoConfigure.java index f6fc6695..40711835 100644 --- a/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/config/RestAutoConfigure.java +++ b/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/config/RestAutoConfigure.java @@ -8,8 +8,8 @@ import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientBuilderCustomizer; -import org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientProperties; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientProperties; +import org.springframework.boot.autoconfigure.elasticsearch.RestClientBuilderCustomizer; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; @@ -21,14 +21,14 @@ * @author zlt * @date 2020/3/28 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @EnableConfigurationProperties(RestClientPoolProperties.class) public class RestAutoConfigure { @Bean public RestClientBuilderCustomizer restClientBuilderCustomizer(RestClientPoolProperties poolProperties - , RestClientProperties restProperties) { + , ElasticsearchRestClientProperties restProperties) { return (builder) -> { setRequestConfig(builder, poolProperties); @@ -51,7 +51,7 @@ private void setRequestConfig(RestClientBuilder builder, RestClientPoolPropertie /** * 异步httpclient连接数配置 */ - private void setHttpClientConfig(RestClientBuilder builder, RestClientPoolProperties poolProperties, RestClientProperties restProperties){ + private void setHttpClientConfig(RestClientBuilder builder, RestClientPoolProperties poolProperties, ElasticsearchRestClientProperties restProperties){ builder.setHttpClientConfigCallback(httpClientBuilder -> { httpClientBuilder.setMaxConnTotal(poolProperties.getMaxConnectNum()) .setMaxConnPerRoute(poolProperties.getMaxConnectPerRoute()); diff --git a/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/properties/RestClientPoolProperties.java b/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/properties/RestClientPoolProperties.java index 0c9344a6..16b9d232 100644 --- a/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/properties/RestClientPoolProperties.java +++ b/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/properties/RestClientPoolProperties.java @@ -11,7 +11,7 @@ * @author zlt * @date 2020/3/28 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Setter diff --git a/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/utils/SearchBuilder.java b/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/utils/SearchBuilder.java index 05defd88..ba2521a3 100644 --- a/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/utils/SearchBuilder.java +++ b/zlt-commons/zlt-elasticsearch-spring-boot-starter/src/main/java/com/central/es/utils/SearchBuilder.java @@ -3,9 +3,10 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; +import com.central.common.utils.JsonUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Getter; import lombok.Setter; import org.apache.commons.beanutils.PropertyUtils; @@ -21,8 +22,7 @@ import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; import org.elasticsearch.search.sort.SortOrder; -import org.springframework.data.elasticsearch.ElasticsearchException; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.UncategorizedElasticsearchException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -37,7 +37,7 @@ * @author zlt * @date 2020/3/28 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Setter @@ -51,6 +51,10 @@ public class SearchBuilder { * 高亮后缀 */ private static final String HIGHLIGHTER_POST_TAGS = ""; + /** + * 排序顺序 + */ + private static final String SORT_ORDER_ASC = "ASC"; private SearchRequest searchRequest; private SearchSourceBuilder searchBuilder; @@ -64,26 +68,24 @@ private SearchBuilder(SearchRequest searchRequest, SearchSourceBuilder searchBui /** * 生成SearchBuilder实例 - * @param elasticsearchTemplate + * @param client * @param indexName */ - public static SearchBuilder builder(ElasticsearchRestTemplate elasticsearchTemplate, String indexName) { + public static SearchBuilder builder(RestHighLevelClient client, String indexName) { SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); SearchRequest searchRequest = new SearchRequest(indexName); searchRequest.source(searchSourceBuilder); - RestHighLevelClient client = elasticsearchTemplate.getClient(); return new SearchBuilder(searchRequest, searchSourceBuilder, client); } /** * 生成SearchBuilder实例 - * @param elasticsearchTemplate + * @param client */ - public static SearchBuilder builder(ElasticsearchRestTemplate elasticsearchTemplate) { + public static SearchBuilder builder(RestHighLevelClient client) { SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); SearchRequest searchRequest = new SearchRequest(); searchRequest.source(searchSourceBuilder); - RestHighLevelClient client = elasticsearchTemplate.getClient(); return new SearchBuilder(searchRequest, searchSourceBuilder, client); } @@ -119,9 +121,24 @@ public SearchBuilder setStringQuery(String queryStr) { * @param limit 每页显示数 */ public SearchBuilder setPage(Integer page, Integer limit) { + setPage(page, limit, false); + return this; + } + + /** + * 设置分页 + * @param page 当前页数 + * @param limit 每页显示数 + * @param trackTotalHits 分页总数是否显示所有条数,默认只显示10000 + */ + public SearchBuilder setPage(Integer page, Integer limit, boolean trackTotalHits) { if (page != null && limit != null) { searchBuilder.from((page - 1) * limit) .size(limit); + if (trackTotalHits) { + searchBuilder.trackTotalHits(trackTotalHits); + } + } return this; } @@ -131,9 +148,15 @@ public SearchBuilder setPage(Integer page, Integer limit) { * @param field 排序字段 * @param order 顺序方向 */ - public SearchBuilder addSort(String field, SortOrder order) { + public SearchBuilder addSort(String field, String order) { if (StrUtil.isNotEmpty(field) && order != null) { - searchBuilder.sort(field, order); + SortOrder so; + if (SORT_ORDER_ASC.equals(order)) { + so = SortOrder.ASC; + } else { + so = SortOrder.DESC; + } + searchBuilder.sort(field, so); } return this; } @@ -185,16 +208,16 @@ public SearchResponse get() throws IOException { } /** - * 返回列表结果 List + * 返回列表结果 List */ - public List getList() throws IOException { + public List getList() throws IOException { return getList(this.get().getHits()); } /** * 返回分页结果 PageResult */ - public PageResult getPage() throws IOException { + public PageResult getPage() throws IOException { return this.getPage(null, null); } @@ -203,30 +226,31 @@ public PageResult getPage() throws IOException { * @param page 当前页数 * @param limit 每页显示 */ - public PageResult getPage(Integer page, Integer limit) throws IOException { + public PageResult getPage(Integer page, Integer limit) throws IOException { this.setPage(page, limit); SearchResponse response = this.get(); SearchHits searchHits = response.getHits(); - long totalCnt = searchHits.getTotalHits(); - List list = getList(searchHits); - return PageResult.builder().data(list).code(0).count(totalCnt).build(); + long totalCnt = searchHits.getTotalHits().value; + List list = getList(searchHits); + return PageResult.builder().data(list).code(0).count(totalCnt).build(); } /** * 返回JSON列表数据 */ - private List getList(SearchHits searchHits) { - List list = new ArrayList<>(); + private List getList(SearchHits searchHits) { + List list = new ArrayList<>(); if (searchHits != null) { searchHits.forEach(item -> { - JSONObject jsonObject = JSON.parseObject(item.getSourceAsString()); - jsonObject.put("id", item.getId()); + JsonNode jsonNode = JsonUtil.parse(item.getSourceAsString()); + ObjectNode objectNode = (ObjectNode)jsonNode; + objectNode.put("id", item.getId()); Map highlightFields = item.getHighlightFields(); if (highlightFields != null) { - populateHighLightedFields(jsonObject, highlightFields); + populateHighLightedFields(objectNode, highlightFields); } - list.add(jsonObject); + list.add(objectNode); }); } return list; @@ -242,10 +266,14 @@ private void populateHighLightedFields(T result, Map try { String name = field.getName(); if (!name.endsWith(".keyword")) { - PropertyUtils.setProperty(result, field.getName(), concat(field.fragments())); + if (result instanceof ObjectNode) { + ((ObjectNode)result).put(field.getName(), concat(field.fragments())); + } else { + PropertyUtils.setProperty(result, field.getName(), concat(field.fragments())); + } } } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { - throw new ElasticsearchException("failed to set highlighted value for field: " + field.getName() + throw new UncategorizedElasticsearchException("failed to set highlighted value for field: " + field.getName() + " with value: " + Arrays.toString(field.getFragments()), e); } } diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/pom.xml b/zlt-commons/zlt-loadbalancer-spring-boot-starter/pom.xml similarity index 85% rename from zlt-commons/zlt-ribbon-spring-boot-starter/pom.xml rename to zlt-commons/zlt-loadbalancer-spring-boot-starter/pom.xml index ecfb929b..ae21c49f 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/pom.xml @@ -1,48 +1,48 @@ - - - - com.zlt - zlt-commons - 3.5.0 - - 4.0.0 - jar - zlt-ribbon-spring-boot-starter - 远程通信通用组件 - - - com.zlt - zlt-common-core - - - org.springframework.cloud - spring-cloud-starter-openfeign - - - org.springframework.cloud - spring-cloud-starter-netflix-ribbon - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - true - - - org.apache.httpcomponents - httpclient - - - javax.servlet - javax.servlet-api - true - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - + + + + com.zlt + zlt-commons + 5.4.0 + + 4.0.0 + jar + zlt-loadbalancer-spring-boot-starter + 负载均衡通用组件 + + + com.zlt + zlt-common-core + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + true + + + org.apache.httpcomponents + httpclient + + + javax.servlet + javax.servlet-api + true + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RestTemplateAutoConfigure.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/RestTemplateAutoConfigure.java similarity index 87% rename from zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RestTemplateAutoConfigure.java rename to zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/RestTemplateAutoConfigure.java index c96fb859..b5fe3ef3 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RestTemplateAutoConfigure.java +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/RestTemplateAutoConfigure.java @@ -1,77 +1,79 @@ -package com.central.common.ribbon; - -import com.central.common.ribbon.config.RestTemplateProperties; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.config.Registry; -import org.apache.http.config.RegistryBuilder; -import org.apache.http.conn.socket.ConnectionSocketFactory; -import org.apache.http.conn.socket.PlainConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.context.annotation.Bean; -import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; -import org.springframework.web.client.RestTemplate; - -/** - * @author zlt - * @date 2018/11/17 - */ -@EnableConfigurationProperties(RestTemplateProperties.class) -public class RestTemplateAutoConfigure { - @Autowired - private RestTemplateProperties restTemplateProperties; - - @LoadBalanced - @Bean - public RestTemplate restTemplate() { - RestTemplate restTemplate = new RestTemplate(); - restTemplate.setRequestFactory(httpRequestFactory()); - return restTemplate; - } - - /** - * httpclient 实现的ClientHttpRequestFactory - */ - @Bean - public ClientHttpRequestFactory httpRequestFactory() { - return new HttpComponentsClientHttpRequestFactory(httpClient()); - } - - /** - * 使用连接池的 httpclient - */ - @Bean - public HttpClient httpClient() { - Registry registry = RegistryBuilder.create() - .register("http", PlainConnectionSocketFactory.getSocketFactory()) - .register("https", SSLConnectionSocketFactory.getSocketFactory()) - .build(); - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry); - // 最大链接数 - connectionManager.setMaxTotal(restTemplateProperties.getMaxTotal()); - // 同路由并发数20 - connectionManager.setDefaultMaxPerRoute(restTemplateProperties.getMaxPerRoute()); - - RequestConfig requestConfig = RequestConfig.custom() - // 读超时 - .setSocketTimeout(restTemplateProperties.getReadTimeout()) - // 链接超时 - .setConnectTimeout(restTemplateProperties.getConnectTimeout()) - // 链接不够用的等待时间 - .setConnectionRequestTimeout(restTemplateProperties.getReadTimeout()) - .build(); - - return HttpClientBuilder.create() - .setDefaultRequestConfig(requestConfig) - .setConnectionManager(connectionManager) - .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) - .build(); - } -} +package com.central.common.lb; + +import com.central.common.lb.config.RestTemplateProperties; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * @author zlt + * @date 2018/11/17 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@EnableConfigurationProperties(RestTemplateProperties.class) +public class RestTemplateAutoConfigure { + @Autowired + private RestTemplateProperties restTemplateProperties; + + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory httpRequestFactory) { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setRequestFactory(httpRequestFactory); + return restTemplate; + } + + /** + * httpclient 实现的ClientHttpRequestFactory + */ + @Bean + public ClientHttpRequestFactory httpRequestFactory(HttpClient httpClient) { + return new HttpComponentsClientHttpRequestFactory(httpClient); + } + + /** + * 使用连接池的 httpclient + */ + @Bean + public HttpClient httpClient() { + Registry registry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", SSLConnectionSocketFactory.getSocketFactory()) + .build(); + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry); + // 最大链接数 + connectionManager.setMaxTotal(restTemplateProperties.getMaxTotal()); + // 同路由并发数20 + connectionManager.setDefaultMaxPerRoute(restTemplateProperties.getMaxPerRoute()); + + RequestConfig requestConfig = RequestConfig.custom() + // 读超时 + .setSocketTimeout(restTemplateProperties.getReadTimeout()) + // 链接超时 + .setConnectTimeout(restTemplateProperties.getConnectTimeout()) + // 链接不够用的等待时间 + .setConnectionRequestTimeout(restTemplateProperties.getReadTimeout()) + .build(); + + return HttpClientBuilder.create() + .setDefaultRequestConfig(requestConfig) + .setConnectionManager(connectionManager) + .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) + .build(); + } +} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/annotation/EnableBaseFeignInterceptor.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/annotation/EnableBaseFeignInterceptor.java similarity index 77% rename from zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/annotation/EnableBaseFeignInterceptor.java rename to zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/annotation/EnableBaseFeignInterceptor.java index 6dc5fd3f..a0a9a817 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/annotation/EnableBaseFeignInterceptor.java +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/annotation/EnableBaseFeignInterceptor.java @@ -1,6 +1,6 @@ -package com.central.common.ribbon.annotation; +package com.central.common.lb.annotation; -import com.central.common.ribbon.config.FeignInterceptorConfig; +import com.central.common.lb.config.FeignInterceptorConfig; import org.springframework.context.annotation.Import; import java.lang.annotation.ElementType; @@ -14,7 +14,7 @@ * @author zlt * @date 2019/10/26 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Target(ElementType.TYPE) @@ -22,4 +22,4 @@ @Import(FeignInterceptorConfig.class) public @interface EnableBaseFeignInterceptor { -} +} \ No newline at end of file diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/annotation/EnableFeignInterceptor.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/annotation/EnableFeignInterceptor.java similarity index 74% rename from zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/annotation/EnableFeignInterceptor.java rename to zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/annotation/EnableFeignInterceptor.java index 5f97ac6b..9bd4c02e 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/annotation/EnableFeignInterceptor.java +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/annotation/EnableFeignInterceptor.java @@ -1,7 +1,7 @@ -package com.central.common.ribbon.annotation; +package com.central.common.lb.annotation; -import com.central.common.ribbon.config.FeignHttpInterceptorConfig; -import com.central.common.ribbon.config.FeignInterceptorConfig; +import com.central.common.lb.config.FeignHttpInterceptorConfig; +import com.central.common.lb.config.FeignInterceptorConfig; import org.springframework.context.annotation.Import; import java.lang.annotation.ElementType; @@ -14,9 +14,11 @@ * * @author zlt */ + @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import({FeignInterceptorConfig.class, FeignHttpInterceptorConfig.class}) public @interface EnableFeignInterceptor { } + diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/IRuleChooser.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/IRuleChooser.java new file mode 100644 index 00000000..356d2c7d --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/IRuleChooser.java @@ -0,0 +1,14 @@ +package com.central.common.lb.chooser; + +import org.springframework.cloud.client.ServiceInstance; + +import java.util.List; + +/** + * service选择器类 + * + * @author jarvis create by 2022/3/13 + */ +public interface IRuleChooser { + ServiceInstance choose(List instances); +} diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/RandomRuleChooser.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/RandomRuleChooser.java new file mode 100644 index 00000000..7520472e --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/RandomRuleChooser.java @@ -0,0 +1,27 @@ +package com.central.common.lb.chooser; + +import com.alibaba.nacos.common.utils.CollectionUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.client.ServiceInstance; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 随机的选择器 + * + * @author jarvis create by 2022/3/13 + */ +@Slf4j +public class RandomRuleChooser implements IRuleChooser { + @Override + public ServiceInstance choose(List instances) { + if(CollectionUtils.isNotEmpty(instances)){ + int randomValue = ThreadLocalRandom.current().nextInt(instances.size()); + ServiceInstance serviceInstance = instances.get(randomValue); + log.info("选择了ip为{}, 端口为:{}的服务", serviceInstance.getHost(), serviceInstance.getPort()); + return serviceInstance; + } + return null; + } +} diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/RoundRuleChooser.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/RoundRuleChooser.java new file mode 100644 index 00000000..395c5e74 --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/chooser/RoundRuleChooser.java @@ -0,0 +1,33 @@ +package com.central.common.lb.chooser; + +import com.alibaba.nacos.common.utils.CollectionUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.client.ServiceInstance; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 轮询选择器 + * + * @author jarvis create by 2022/3/13 + */ +@Slf4j +public class RoundRuleChooser implements IRuleChooser{ + + private AtomicInteger position; + + public RoundRuleChooser() { + this.position = new AtomicInteger(1000); + } + + @Override + public ServiceInstance choose(List instances) { + if(CollectionUtils.isNotEmpty(instances)){ + ServiceInstance serviceInstance = instances.get(Math.abs(position.incrementAndGet() % instances.size())); + log.info("选择了ip为{}, 端口为:{}的服务", serviceInstance.getHost(), serviceInstance.getPort()); + return serviceInstance; + } + return null; + } +} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignHttpInterceptorConfig.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/FeignHttpInterceptorConfig.java similarity index 90% rename from zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignHttpInterceptorConfig.java rename to zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/FeignHttpInterceptorConfig.java index d85ece2e..4e0e006a 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignHttpInterceptorConfig.java +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/FeignHttpInterceptorConfig.java @@ -1,8 +1,11 @@ -package com.central.common.ribbon.config; +package com.central.common.lb.config; import com.central.common.constant.CommonConstant; import com.central.common.constant.SecurityConstants; +import com.central.common.lb.utils.QueryUtils; import feign.RequestInterceptor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -12,6 +15,7 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.List; +import java.util.Map; /** * feign拦截器,只包含http相关数据 @@ -19,7 +23,7 @@ * @author zlt * @date 2019/10/26 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ public class FeignHttpInterceptorConfig { @@ -37,6 +41,7 @@ public void initialize() { * 使用feign client访问别的微服务时,将上游传过来的access_token、username、roles等信息放入header传递给下一个服务 */ @Bean + @ConditionalOnClass(HttpServletRequest.class) public RequestInterceptor httpFeignInterceptor() { return template -> { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder @@ -77,7 +82,7 @@ private String extractHeaderToken(HttpServletRequest request) { Enumeration headers = request.getHeaders(CommonConstant.TOKEN_HEADER); while (headers.hasMoreElements()) { String value = headers.nextElement(); - if ((value.toLowerCase().startsWith(CommonConstant.BEARER_TYPE))) { + if ((value.toLowerCase().startsWith(CommonConstant.BEARER_TYPE.toLowerCase()))) { String authHeaderValue = value.substring(CommonConstant.BEARER_TYPE.length()).trim(); int commaIndex = authHeaderValue.indexOf(','); if (commaIndex > 0) { diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignInterceptorConfig.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/FeignInterceptorConfig.java similarity index 64% rename from zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignInterceptorConfig.java rename to zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/FeignInterceptorConfig.java index 3ae3c0b6..59da835e 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignInterceptorConfig.java +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/FeignInterceptorConfig.java @@ -1,21 +1,22 @@ -package com.central.common.ribbon.config; +package com.central.common.lb.config; import cn.hutool.core.util.StrUtil; -import com.central.common.constant.CommonConstant; import com.central.common.constant.SecurityConstants; import com.central.common.context.TenantContextHolder; import feign.RequestInterceptor; -import org.slf4j.MDC; import org.springframework.context.annotation.Bean; /** * feign拦截器,只包含基础数据 * * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public class FeignInterceptorConfig { /** - * 使用feign client访问别的微服务时,将上游传过来的client、traceid等信息放入header传递给下一个服务 + * 使用feign client访问别的微服务时,将上游传过来的client等信息放入header传递给下一个服务 */ @Bean public RequestInterceptor baseFeignInterceptor() { @@ -25,12 +26,6 @@ public RequestInterceptor baseFeignInterceptor() { if (StrUtil.isNotEmpty(tenant)) { template.header(SecurityConstants.TENANT_HEADER, tenant); } - - //传递日志traceId - String traceId = MDC.get(CommonConstant.LOG_TRACE_ID); - if (StrUtil.isNotEmpty(traceId)) { - template.header(CommonConstant.TRACE_ID_HEADER, traceId); - } }; } } diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/RestTemplateProperties.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/RestTemplateProperties.java similarity index 84% rename from zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/RestTemplateProperties.java rename to zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/RestTemplateProperties.java index bbd13882..b0dd15cb 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/RestTemplateProperties.java +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/RestTemplateProperties.java @@ -1,33 +1,36 @@ -package com.central.common.ribbon.config; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * RestTemplate 配置 - * - * @author zlt - * @date 2017/11/17 - */ -@Setter -@Getter -@ConfigurationProperties(prefix = "zlt.rest-template") -public class RestTemplateProperties { - /** - * 最大链接数 - */ - private int maxTotal = 200; - /** - * 同路由最大并发数 - */ - private int maxPerRoute = 50; - /** - * 读取超时时间 ms - */ - private int readTimeout = 35000; - /** - * 链接超时时间 ms - */ - private int connectTimeout = 10000; -} +package com.central.common.lb.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * RestTemplate 配置 + * + * @author zlt + * @date 2017/11/17 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +@Getter +@ConfigurationProperties(prefix = "zlt.rest-template") +public class RestTemplateProperties { + /** + * 最大链接数 + */ + private int maxTotal = 200; + /** + * 同路由最大并发数 + */ + private int maxPerRoute = 50; + /** + * 读取超时时间 ms + */ + private int readTimeout = 35000; + /** + * 链接超时时间 ms + */ + private int connectTimeout = 10000; +} diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VerionIsolationAutoConfig.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VerionIsolationAutoConfig.java new file mode 100644 index 00000000..2ecd5166 --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VerionIsolationAutoConfig.java @@ -0,0 +1,17 @@ +package com.central.common.lb.config; + +import com.central.common.constant.ConfigConstants; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients; +import org.springframework.context.annotation.Import; + +/** + * + * + * @author jarvis create by 2022/4/10 + */ +@LoadBalancerClients(defaultConfiguration = VersionLoadBalancerConfig.class) +@ConditionalOnProperty(prefix = ConfigConstants.CONFIG_LOADBALANCE_ISOLATION, name = "enabled", havingValue = "true", matchIfMissing = false) +@Import({VersionRegisterBeanPostProcessor.class}) +public class VerionIsolationAutoConfig { +} diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VersionLoadBalancerConfig.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VersionLoadBalancerConfig.java new file mode 100644 index 00000000..7a64f04e --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VersionLoadBalancerConfig.java @@ -0,0 +1,68 @@ +package com.central.common.lb.config; + +import com.central.common.constant.ConfigConstants; +import com.central.common.lb.chooser.IRuleChooser; +import com.central.common.lb.chooser.RoundRuleChooser; +import com.central.common.lb.loadbalancer.VersionLoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.util.ClassUtils; + +/** + * 版本控制的路由选择类配置 + * + * @author jarvis create by 2022/3/9 + */ +@Slf4j +public class VersionLoadBalancerConfig{ + + private IRuleChooser defaultRuleChooser = null; + + @Bean + @ConditionalOnMissingBean(IRuleChooser.class) + @ConditionalOnProperty(prefix = ConfigConstants.CONFIG_LOADBALANCE_ISOLATION, value = "chooser") + public IRuleChooser customRuleChooser(Environment environment, ApplicationContext context){ + + IRuleChooser chooser = new RoundRuleChooser(); + if (environment.containsProperty(ConfigConstants.CONFIG_LOADBALANCE_ISOLATION_CHOOSER)) { + String chooserRuleClassString = environment.getProperty(ConfigConstants.CONFIG_LOADBALANCE_ISOLATION_CHOOSER); + if(StringUtils.isNotBlank(chooserRuleClassString)){ + try { + Class ruleClass = ClassUtils.forName(chooserRuleClassString, context.getClassLoader()); + chooser = (IRuleChooser) ruleClass.newInstance(); + } catch (ClassNotFoundException e) { + log.error("没有找到定义的选择器,将使用内置的选择器", e); + } catch (InstantiationException e) { + log.error("没法创建定义的选择器,将使用内置的选择器", e); + } catch (IllegalAccessException e) { + log.error("没法创建定义的选择器,将使用内置的选择器", e); + } + } + } + return chooser; + } + + @Bean + @ConditionalOnMissingBean(value = IRuleChooser.class) + public IRuleChooser defaultRuleChooser(){ + return new RoundRuleChooser(); + } + + + @Bean + @ConditionalOnProperty(prefix = ConfigConstants.CONFIG_LOADBALANCE_ISOLATION, name = "enabled", havingValue = "true", matchIfMissing = false) + public ReactorServiceInstanceLoadBalancer versionServiceLoadBalancer(Environment environment + , LoadBalancerClientFactory loadBalancerClientFactory, IRuleChooser ruleChooser){ + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new VersionLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class) + , name, ruleChooser); + } +} diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VersionRegisterBeanPostProcessor.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VersionRegisterBeanPostProcessor.java new file mode 100644 index 00000000..7f5a3d1f --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/config/VersionRegisterBeanPostProcessor.java @@ -0,0 +1,27 @@ +package com.central.common.lb.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.common.utils.StringUtils; +import com.central.common.constant.CommonConstant; +import com.central.common.constant.ConfigConstants; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.BeanPostProcessor; + +/** + * 将版本注册到注册中心的组件 + * + * @author jarvis create by 2022/3/20 + */ +public class VersionRegisterBeanPostProcessor implements BeanPostProcessor { + @Value("${"+ ConfigConstants.CONFIG_LOADBALANCE_VERSION+":}") + private String version; + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if(bean instanceof NacosDiscoveryProperties && StringUtils.isNotBlank(version)){ + NacosDiscoveryProperties nacosDiscoveryProperties = (NacosDiscoveryProperties) bean; + nacosDiscoveryProperties.getMetadata().putIfAbsent(CommonConstant.METADATA_VERSION, version); + } + return bean; + } +} diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/loadbalancer/VersionLoadBalancer.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/loadbalancer/VersionLoadBalancer.java new file mode 100644 index 00000000..db197ed4 --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/loadbalancer/VersionLoadBalancer.java @@ -0,0 +1,100 @@ +package com.central.common.lb.loadbalancer; + +import com.central.common.constant.CommonConstant; +import com.central.common.lb.chooser.IRuleChooser; +import com.central.common.lb.utils.QueryUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.*; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import reactor.core.publisher.Mono; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 自定义版本路由选择 + * + * @author jarvis create by 2022/3/9 + */ +@Slf4j +public class VersionLoadBalancer implements ReactorServiceInstanceLoadBalancer { + + private final static String KEY_DEFAULT = "default"; + + private ObjectProvider serviceInstanceListSuppliers; + + private String serviceId; + + private IRuleChooser ruleChooser; + + public VersionLoadBalancer(ObjectProvider serviceInstanceListSuppliers, String serviceId, IRuleChooser ruleChooser) { + this.serviceInstanceListSuppliers = serviceInstanceListSuppliers; + this.serviceId = serviceId; + this.ruleChooser = ruleChooser; + } + + @Override + public Mono> choose(Request request) { + // 从request中获取版本,兼容webflux方式 + RequestData requestData = ((RequestDataContext) (request.getContext())).getClientRequest(); + String version = getVersionFromRequestData(requestData); + log.debug("选择的版本号为:{}", version); + return serviceInstanceListSuppliers.getIfAvailable().get(request).next().map(instanceList->{ + return getInstanceResponse(instanceList, version); + }); + } + + private String getVersionFromRequestData(RequestData requestData){ + Map queryMap = QueryUtils.getQueryMap(requestData.getUrl()); + if(MapUtils.isNotEmpty(queryMap)&& queryMap.containsKey(CommonConstant.Z_L_T_VERSION)&& StringUtils.isNotBlank(queryMap.get(CommonConstant.Z_L_T_VERSION))){ + return queryMap.get(CommonConstant.Z_L_T_VERSION); + }else if(requestData.getHeaders().containsKey(CommonConstant.Z_L_T_VERSION)){ + return requestData.getHeaders().get(CommonConstant.Z_L_T_VERSION).get(0); + } + return null; + } + /** + * 1. 先获取到拦截的版本,如果不为空的话就将service列表过滤,寻找metadata中哪个服务是配置的版本, + * 如果版本为空则不需要进行过滤直接提交给service选择器进行选择 + * 2. 如果没有找到版本对应的实例,则找所有版本为空或者版本号为default的实例 + * 3.将instance列表提交到选择器根据对应的策略返回一个instance + * @param instances + * @return + */ + private Response getInstanceResponse(Listinstances, String version){ + List filteredServiceIstanceList = instances; + if(StringUtils.isNotBlank(version)){ + if(CollectionUtils.isNotEmpty(instances)){ + filteredServiceIstanceList = instances.stream() + .filter(item->item.getMetadata().containsKey(CommonConstant.METADATA_VERSION)&& + version.equals(item.getMetadata().get(CommonConstant.METADATA_VERSION))) + .collect(Collectors.toList()); + } + } + // 如果没有找到对应的版本实例时,选择版本号为空的或这版本为default的实例 + if(CollectionUtils.isEmpty(filteredServiceIstanceList)){ + filteredServiceIstanceList = instances.stream() + .filter(item->!item.getMetadata().containsKey(CommonConstant.METADATA_VERSION)|| + StringUtils.isBlank(item.getMetadata().get(CommonConstant.METADATA_VERSION)) + || "default".equals(item.getMetadata().get(CommonConstant.METADATA_VERSION))) + .collect(Collectors.toList()); + } + // 经过两轮过滤后如果能找到的话就选择,不然返回空 + if(CollectionUtils.isNotEmpty(filteredServiceIstanceList)){ + ServiceInstance serviceInstance = this.ruleChooser.choose(filteredServiceIstanceList); + if(!Objects.isNull(serviceInstance)){ + log.debug("使用serviceId为:{}服务, 选择version为:{}, 地址:{}:{},", serviceId, version + , serviceInstance.getHost(), serviceInstance.getPort()); + return new DefaultResponse(serviceInstance); + } + } + // 返回空的返回体 + return new EmptyResponse(); + } +} diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/utils/QueryUtils.java b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/utils/QueryUtils.java new file mode 100644 index 00000000..2321d5bf --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/java/com/central/common/lb/utils/QueryUtils.java @@ -0,0 +1,42 @@ +package com.central.common.lb.utils; + +import org.apache.commons.lang3.StringUtils; + +import java.net.URI; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 解析request的query参数工具 + * + * @author jarvis create by 2022/5/8 + */ +public class QueryUtils { + /** + * 通过query字符串得到参数的map + * @param queryString ?后的字符 + * @return + */ + public static Map getQueryMap(String queryString){ + if(StringUtils.isNotBlank(queryString)){ + return Arrays.stream(queryString.split("&")).map(item -> item.split("=")) + .collect(Collectors.toMap(key -> key[0], value -> value.length > 1 && StringUtils.isNotBlank(value[1]) ? value[1] : "")); + } + return Collections.emptyMap(); + } + + /** + * 通过url获取参数map + * @param uri + * @return + */ + public static Map getQueryMap(URI uri){ + if(Objects.nonNull(uri)){ + return getQueryMap(uri.getQuery()); + } + return Collections.emptyMap(); + } +} diff --git a/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..6154b24d --- /dev/null +++ b/zlt-commons/zlt-loadbalancer-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.central.common.lb.RestTemplateAutoConfigure,\ +com.central.common.lb.config.VerionIsolationAutoConfig \ No newline at end of file diff --git a/zlt-commons/zlt-log-spring-boot-starter/pom.xml b/zlt-commons/zlt-log-spring-boot-starter/pom.xml index 031c20f5..d7ad982a 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-log-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 4.0.0 jar @@ -42,6 +42,11 @@ org.springframework.boot spring-boot-starter-aop + + cn.hutool + hutool-all + + org.springframework spring-web @@ -57,5 +62,15 @@ spring-boot-starter-jdbc true + + org.apache.dubbo + dubbo + true + + + io.github.openfeign + feign-core + true + diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/config/LogAutoConfigure.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/LogAutoConfigure.java similarity index 88% rename from zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/config/LogAutoConfigure.java rename to zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/LogAutoConfigure.java index 6559c6f4..92ae7a80 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/config/LogAutoConfigure.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/LogAutoConfigure.java @@ -1,4 +1,4 @@ -package com.central.log.config; +package com.central.log; import com.central.log.properties.AuditLogProperties; import com.central.log.properties.LogDbProperties; @@ -6,6 +6,7 @@ import com.zaxxer.hikari.HikariConfig; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** @@ -14,6 +15,7 @@ * @author zlt * @date 2019/8/13 */ +@ComponentScan @EnableConfigurationProperties({TraceProperties.class, AuditLogProperties.class}) public class LogAutoConfigure { /** diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/annotation/AuditLog.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/annotation/AuditLog.java index b0681557..cde75670 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/annotation/AuditLog.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/annotation/AuditLog.java @@ -6,7 +6,7 @@ * @author zlt * @date 2020/2/3 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Target({ElementType.METHOD, ElementType.TYPE}) diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/aspect/AuditLogAspect.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/aspect/AuditLogAspect.java index a4cd4686..a94fd8a6 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/aspect/AuditLogAspect.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/aspect/AuditLogAspect.java @@ -16,6 +16,7 @@ import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -28,11 +29,12 @@ * @author zlt * @date 2020/2/3 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j @Aspect +@Component @ConditionalOnClass({HttpServletRequest.class, RequestContextHolder.class}) public class AuditLogAspect { @Value("${spring.application.name}") diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/model/Audit.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/model/Audit.java index 622cc55a..6cdc07ee 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/model/Audit.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/model/Audit.java @@ -11,7 +11,7 @@ * @author zlt * @date 2020/2/3 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Setter diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/monitor/PointUtil.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/monitor/PointUtil.java index 804a10c1..d7a38fa6 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/monitor/PointUtil.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/monitor/PointUtil.java @@ -1,18 +1,41 @@ package com.central.log.monitor; +import cn.hutool.core.util.ReflectUtil; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.util.ObjectUtils; + +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.Map; /** * 日志埋点工具类 * * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Slf4j public class PointUtil { private static final String MSG_PATTERN = "{}|{}|{}"; + private static final String PROPERTIES_SPLIT = "&"; + private static final String PROPERTIES_VALUE_SPLIT = "="; + + private final PointEntry pointEntry; private PointUtil() { - throw new IllegalStateException("Utility class"); + pointEntry = new PointEntry(); + } + + @Setter + @Getter + private class PointEntry { + String id; + String type; + Object properties; } /** @@ -31,4 +54,66 @@ public static void info(String id, String type, String message) { public static void debug(String id, String type, String message) { log.debug(MSG_PATTERN, id, type, message); } + + public static PointUtil builder() { + return new PointUtil(); + } + + /** + * @param businessId 业务id/对象id + */ + public PointUtil id(Object businessId) { + this.pointEntry.setId(String.valueOf(businessId)); + return this; + } + + /** + * @param type 类型 + */ + public PointUtil type(String type) { + this.pointEntry.setType(type); + return this; + } + + /** + * @param properties 对象属性 + */ + public PointUtil properties(Object properties) { + this.pointEntry.setProperties(properties); + return this; + } + + private String getPropertiesStr() { + Object properties = this.pointEntry.getProperties(); + StringBuilder result = new StringBuilder(); + if (!ObjectUtils.isEmpty(properties)) { + //解析map + if (properties instanceof Map) { + Map proMap = (Map)properties; + Iterator ite = proMap.entrySet().iterator(); + while (ite.hasNext()) { + Map.Entry entry = ite.next(); + if (result.length() > 0) { + result.append(PROPERTIES_SPLIT); + } + result.append(entry.getKey()).append(PROPERTIES_VALUE_SPLIT).append(entry.getValue()); + } + } else {//解析对象 + Field[] allFields = ReflectUtil.getFields(properties.getClass()); + for (Field field : allFields) { + String fieldName = field.getName(); + Object fieldValue = ReflectUtil.getFieldValue(properties, field); + if (result.length() > 0) { + result.append(PROPERTIES_SPLIT); + } + result.append(fieldName).append(PROPERTIES_VALUE_SPLIT).append(fieldValue); + } + } + } + return result.toString(); + } + + public void build() { + PointUtil.debug(this.pointEntry.getId(), this.pointEntry.getType(), this.getPropertiesStr()); + } } diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/properties/AuditLogProperties.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/properties/AuditLogProperties.java index af73dd17..3f6713a8 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/properties/AuditLogProperties.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/properties/AuditLogProperties.java @@ -11,7 +11,7 @@ * @author zlt * @date 2020/2/3 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Setter diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/properties/LogDbProperties.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/properties/LogDbProperties.java index 511dbb78..518fc72f 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/properties/LogDbProperties.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/properties/LogDbProperties.java @@ -12,7 +12,7 @@ * @author zlt * @date 2020/2/8 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Setter diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/IAuditService.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/IAuditService.java index fdefd1ef..9e0c11b7 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/IAuditService.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/IAuditService.java @@ -8,7 +8,7 @@ * @author zlt * @date 2020/2/3 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ public interface IAuditService { diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/impl/DbAuditServiceImpl.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/impl/DbAuditServiceImpl.java index 444c02d3..1cae063c 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/impl/DbAuditServiceImpl.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/impl/DbAuditServiceImpl.java @@ -12,6 +12,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.sql.DataSource; @@ -22,10 +23,11 @@ * @author zlt * @date 2020/2/8 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j +@Service @ConditionalOnProperty(name = "zlt.audit-log.log-type", havingValue = "db") @ConditionalOnClass(JdbcTemplate.class) public class DbAuditServiceImpl implements IAuditService { diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/impl/LoggerAuditServiceImpl.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/impl/LoggerAuditServiceImpl.java index 5feb5d59..facfd34e 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/impl/LoggerAuditServiceImpl.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/service/impl/LoggerAuditServiceImpl.java @@ -4,6 +4,7 @@ import com.central.log.service.IAuditService; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; import java.time.format.DateTimeFormatter; @@ -13,10 +14,11 @@ * @author zlt * @date 2020/2/3 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j +@Service @ConditionalOnProperty(name = "zlt.audit-log.log-type", havingValue = "logger", matchIfMissing = true) public class LoggerAuditServiceImpl implements IAuditService { private static final String MSG_PATTERN = "{}|{}|{}|{}|{}|{}|{}|{}"; diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/DubboTraceFilter.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/DubboTraceFilter.java new file mode 100644 index 00000000..1cd42cdf --- /dev/null +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/DubboTraceFilter.java @@ -0,0 +1,49 @@ +package com.central.log.trace; + +import cn.hutool.core.util.StrUtil; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.*; + +/** + * dubbo过滤器,传递traceId + * + * @author zlt + * @date 2021/1/30 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER}, order = MDCTraceUtils.FILTER_ORDER) +public class DubboTraceFilter implements Filter { + /** + * 服务消费者:传递traceId给下游服务 + * 服务提供者:获取traceId并赋值给MDC + */ + @Override + public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { + boolean isProviderSide = RpcContext.getContext().isProviderSide(); + if (isProviderSide) { //服务提供者逻辑 + String traceId = invocation.getAttachment(MDCTraceUtils.KEY_TRACE_ID); + String spanId = invocation.getAttachment(MDCTraceUtils.KEY_SPAN_ID); + if (StrUtil.isEmpty(traceId)) { + MDCTraceUtils.addTrace(); + } else { + MDCTraceUtils.putTrace(traceId, spanId); + } + } else { //服务消费者逻辑 + String traceId = MDCTraceUtils.getTraceId(); + if (StrUtil.isNotEmpty(traceId)) { + invocation.setAttachment(MDCTraceUtils.KEY_TRACE_ID, traceId); + invocation.setAttachment(MDCTraceUtils.KEY_SPAN_ID, MDCTraceUtils.getNextSpanId()); + } + } + try { + return invoker.invoke(invocation); + } finally { + if (isProviderSide) { + MDCTraceUtils.removeTrace(); + } + } + } +} diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/FeignTraceConfig.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/FeignTraceConfig.java new file mode 100644 index 00000000..d5a0d1e4 --- /dev/null +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/FeignTraceConfig.java @@ -0,0 +1,40 @@ +package com.central.log.trace; + +import cn.hutool.core.util.StrUtil; +import com.central.log.properties.TraceProperties; +import feign.RequestInterceptor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.Resource; + +/** + * feign拦截器,传递traceId + * + * @author zlt + * @date 2021/1/28 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Configuration +@ConditionalOnClass(value = {RequestInterceptor.class}) +public class FeignTraceConfig { + @Resource + private TraceProperties traceProperties; + + @Bean + public RequestInterceptor feignTraceInterceptor() { + return template -> { + if (traceProperties.getEnable()) { + //传递日志traceId + String traceId = MDCTraceUtils.getTraceId(); + if (StrUtil.isNotEmpty(traceId)) { + template.header(MDCTraceUtils.TRACE_ID_HEADER, traceId); + template.header(MDCTraceUtils.SPAN_ID_HEADER, MDCTraceUtils.getNextSpanId()); + } + } + }; + } +} diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/MDCTraceUtils.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/MDCTraceUtils.java new file mode 100644 index 00000000..d9bf610f --- /dev/null +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/MDCTraceUtils.java @@ -0,0 +1,100 @@ +package com.central.log.trace; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.ttl.TransmittableThreadLocal; +import org.slf4j.MDC; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 日志追踪工具类 + * + * @author zlt + * @date 2020/10/14 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class MDCTraceUtils { + /** + * 追踪id的名称 + */ + public static final String KEY_TRACE_ID = "traceId"; + /** + * 块id的名称 + */ + public static final String KEY_SPAN_ID = "spanId"; + + /** + * 日志链路追踪id信息头 + */ + public static final String TRACE_ID_HEADER = "x-traceId-header"; + /** + * 日志链路块id信息头 + */ + public static final String SPAN_ID_HEADER = "x-spanId-header"; + + /** + * filter的优先级,值越低越优先 + */ + public static final int FILTER_ORDER = -1; + + private static final TransmittableThreadLocal spanNumber = new TransmittableThreadLocal<>(); + + /** + * 创建traceId并赋值MDC + */ + public static void addTrace() { + String traceId = createTraceId(); + MDC.put(KEY_TRACE_ID, traceId); + MDC.put(KEY_SPAN_ID, "0"); + initSpanNumber(); + } + + /** + * 赋值MDC + */ + public static void putTrace(String traceId, String spanId) { + MDC.put(KEY_TRACE_ID, traceId); + MDC.put(KEY_SPAN_ID, spanId); + initSpanNumber(); + } + + /** + * 获取MDC中的traceId值 + */ + public static String getTraceId() { + return MDC.get(KEY_TRACE_ID); + } + /** + * 获取MDC中的spanId值 + */ + public static String getSpanId() { + return MDC.get(KEY_SPAN_ID); + } + + /** + * 清除MDC的值 + */ + public static void removeTrace() { + MDC.remove(KEY_TRACE_ID); + MDC.remove(KEY_SPAN_ID); + spanNumber.remove(); + } + + /** + * 创建traceId + */ + public static String createTraceId() { + return IdUtil.getSnowflake().nextIdStr(); + } + + public static String getNextSpanId() { + return StrUtil.format("{}.{}", getSpanId(), spanNumber.get().incrementAndGet()); + } + + private static void initSpanNumber() { + spanNumber.set(new AtomicInteger(0)); + } +} diff --git a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/filter/TraceFilter.java b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/WebTraceFilter.java similarity index 54% rename from zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/filter/TraceFilter.java rename to zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/WebTraceFilter.java index d07fe343..c7415f3e 100644 --- a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/filter/TraceFilter.java +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/java/com/central/log/trace/WebTraceFilter.java @@ -1,14 +1,13 @@ -package com.central.common.filter; +package com.central.log.trace; import cn.hutool.core.util.StrUtil; -import com.central.common.constant.CommonConstant; import com.central.log.properties.TraceProperties; -import org.slf4j.MDC; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.annotation.Resource; -import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -16,13 +15,18 @@ import java.io.IOException; /** - * 日志链路追踪过滤器 + * web过滤器,生成日志链路追踪id,并赋值MDC * * @author zlt - * @date 2019/9/15 + * @date 2020/10/14 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ -@ConditionalOnClass(Filter.class) -public class TraceFilter extends OncePerRequestFilter { +@Component +@ConditionalOnClass(value = {HttpServletRequest.class, OncePerRequestFilter.class}) +@Order(value = MDCTraceUtils.FILTER_ORDER) +public class WebTraceFilter extends OncePerRequestFilter { @Resource private TraceProperties traceProperties; @@ -35,14 +39,16 @@ protected boolean shouldNotFilter(HttpServletRequest request) { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { try { - String traceId = request.getHeader(CommonConstant.TRACE_ID_HEADER); - if (StrUtil.isNotEmpty(traceId)) { - MDC.put(CommonConstant.LOG_TRACE_ID, traceId); + String traceId = request.getHeader(MDCTraceUtils.TRACE_ID_HEADER); + String spanId = request.getHeader(MDCTraceUtils.SPAN_ID_HEADER); + if (StrUtil.isEmpty(traceId)) { + MDCTraceUtils.addTrace(); + } else { + MDCTraceUtils.putTrace(traceId, spanId); } - filterChain.doFilter(request, response); } finally { - MDC.clear(); + MDCTraceUtils.removeTrace(); } } } diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter new file mode 100644 index 00000000..4414eb1b --- /dev/null +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter @@ -0,0 +1 @@ +logTrace=com.central.log.trace.DubboTraceFilter \ No newline at end of file diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/META-INF/spring.factories index 9fbeb78f..afcf8d5a 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -2,7 +2,4 @@ org.springframework.context.ApplicationContextInitializer=\ com.central.log.config.TtlMDCAdapterInitializer org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.central.log.config.LogAutoConfigure,\ -com.central.log.service.impl.LoggerAuditServiceImpl,\ -com.central.log.service.impl.DbAuditServiceImpl,\ -com.central.log.aspect.AuditLogAspect +com.central.log.LogAutoConfigure \ No newline at end of file diff --git a/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/logback-spring.xml b/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/logback-spring.xml index d4eec0b6..3fe98f07 100644 --- a/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/logback-spring.xml +++ b/zlt-commons/zlt-log-spring-boot-starter/src/main/resources/logback-spring.xml @@ -18,8 +18,8 @@ - + value="[${APP_NAME}:${ServerIP}:${ServerPort}] %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%level){blue} %clr(${PID}){magenta} %clr([%X{traceId}-%X{spanId}]){yellow} %clr([%thread]){orange} %clr(%-40.40logger{39}){cyan} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}" /> + diff --git a/zlt-commons/zlt-oss-spring-boot-starter/pom.xml b/zlt-commons/zlt-oss-spring-boot-starter/pom.xml new file mode 100644 index 00000000..5b909027 --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/pom.xml @@ -0,0 +1,38 @@ + + + + com.zlt + zlt-commons + 5.4.0 + + 4.0.0 + jar + zlt-oss-spring-boot-starter + 对象存储通用组件 + + + + com.github.tobato + fastdfs-client + true + + + + com.amazonaws + aws-java-sdk-s3 + true + + + org.springframework + spring-web + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + diff --git a/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/config/OssAutoConfigure.java b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/config/OssAutoConfigure.java new file mode 100644 index 00000000..725bc196 --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/config/OssAutoConfigure.java @@ -0,0 +1,20 @@ +package com.central.oss.config; + +import com.central.oss.properties.FileServerProperties; +import com.central.oss.template.FdfsTemplate; +import com.central.oss.template.S3Template; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Import; + +/** + * @author zlt + * @date 2021/2/13 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@EnableConfigurationProperties(FileServerProperties.class) +@Import({FdfsTemplate.class, S3Template.class}) +public class OssAutoConfigure { + +} diff --git a/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/model/ObjectInfo.java b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/model/ObjectInfo.java new file mode 100644 index 00000000..15883a23 --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/model/ObjectInfo.java @@ -0,0 +1,24 @@ +package com.central.oss.model; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author zlt + * @date 2021/2/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +@Getter +public class ObjectInfo { + /** + * 对象查看路径 + */ + private String objectUrl; + /** + * 对象保存路径 + */ + private String objectPath; +} diff --git a/zlt-business/file-center/src/main/java/com/central/file/properties/FdfsProperties.java b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/FdfsProperties.java similarity index 55% rename from zlt-business/file-center/src/main/java/com/central/file/properties/FdfsProperties.java rename to zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/FdfsProperties.java index f2a3c172..94a27ce1 100644 --- a/zlt-business/file-center/src/main/java/com/central/file/properties/FdfsProperties.java +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/FdfsProperties.java @@ -1,10 +1,16 @@ -package com.central.file.properties; +package com.central.oss.properties; import lombok.Getter; import lombok.Setter; /** + * fastdfs配置 + * * @author zlt + * @date 2021/2/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Setter @Getter diff --git a/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/FileServerProperties.java b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/FileServerProperties.java new file mode 100644 index 00000000..ac5a786d --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/FileServerProperties.java @@ -0,0 +1,38 @@ +package com.central.oss.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author zlt + * @date 2021/2/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +@Getter +@ConfigurationProperties(prefix = FileServerProperties.PREFIX) +public class FileServerProperties { + public static final String PREFIX = "zlt.file-server"; + public static final String TYPE_FDFS = "fastdfs"; + public static final String TYPE_S3 = "s3"; + + /** + * 为以下2个值,指定不同的自动化配置 + * s3:aws s3协议的存储(七牛oss、阿里云oss、minio等) + * fastdfs:本地部署的fastDFS + */ + private String type; + + /** + * aws s3配置 + */ + S3Properties s3 = new S3Properties(); + + /** + * fastDFS配置 + */ + FdfsProperties fdfs = new FdfsProperties(); +} diff --git a/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/S3Properties.java b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/S3Properties.java new file mode 100644 index 00000000..6cf0f25d --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/properties/S3Properties.java @@ -0,0 +1,42 @@ +package com.central.oss.properties; + +import lombok.Getter; +import lombok.Setter; + +/** + * aws s3协议配置 + * + * @author zlt + * @date 2021/2/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +@Getter +public class S3Properties { + /** + * 用户名 + */ + private String accessKey; + /** + * 密码 + */ + private String accessKeySecret; + /** + * 访问端点 + */ + private String endpoint; + /** + * bucket名称 + */ + private String bucketName; + /** + * 区域 + */ + private String region; + /** + * path-style + */ + private Boolean pathStyleAccessEnabled = true; +} diff --git a/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/service/IOssService.java b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/service/IOssService.java new file mode 100644 index 00000000..c4fe6a58 --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/service/IOssService.java @@ -0,0 +1,42 @@ +package com.central.oss.service; + +import com.central.oss.model.ObjectInfo; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author zlt + * @date 2021/2/9 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public interface IOssService { + /** + * 上传对象 + * @param objectName 对象名 + * @param is 对象流 + */ + ObjectInfo upload(String objectName, InputStream is); + + /** + * 上传对象 + * @param file 对象 + */ + ObjectInfo upload(MultipartFile file); + + /** + * 删除对象 + * @param objectKey 对象标识 + */ + void delete(String objectKey); + + /** + * 查看文件 + * @param objectPath 对象路径 + * @param os 输出流 + */ + void view(String objectPath, OutputStream os); +} diff --git a/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/template/FdfsTemplate.java b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/template/FdfsTemplate.java new file mode 100644 index 00000000..8390a563 --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/template/FdfsTemplate.java @@ -0,0 +1,83 @@ +package com.central.oss.template; + +import com.central.oss.model.ObjectInfo; +import com.central.oss.properties.FileServerProperties; +import com.github.tobato.fastdfs.domain.fdfs.StorePath; +import com.github.tobato.fastdfs.domain.proto.storage.DownloadCallback; +import com.github.tobato.fastdfs.service.FastFileStorageClient; +import lombok.SneakyThrows; +import org.apache.commons.io.FilenameUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.InputStream; + +/** + * FastDFS配置 + * + * @author zlt + * @date 2021/2/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@ConditionalOnClass(FastFileStorageClient.class) +@ConditionalOnProperty(prefix = FileServerProperties.PREFIX, name = "type", havingValue = FileServerProperties.TYPE_FDFS) +public class FdfsTemplate { + @Resource + private FileServerProperties fileProperties; + + @Resource + private FastFileStorageClient storageClient; + + @SneakyThrows + public ObjectInfo upload(String objectName, InputStream is) { + return upload(objectName, is, is.available()); + } + + @SneakyThrows + public ObjectInfo upload(MultipartFile file) { + return upload(file.getOriginalFilename(), file.getInputStream(), file.getSize()); + } + + /** + * 上传对象 + * @param objectName 对象名 + * @param is 对象流 + * @param size 大小 + */ + private ObjectInfo upload(String objectName, InputStream is, long size) { + StorePath storePath = storageClient.uploadFile(is, size, FilenameUtils.getExtension(objectName), null); + ObjectInfo obj = new ObjectInfo(); + obj.setObjectPath(storePath.getFullPath()); + obj.setObjectUrl("http://" + fileProperties.getFdfs().getWebUrl() + "/" + storePath.getFullPath()); + return obj; + } + + /** + * 删除对象 + * @param objectPath 对象路径 + */ + public void delete(String objectPath) { + if (!StringUtils.isEmpty(objectPath)) { + StorePath storePath = StorePath.parseFromUrl(objectPath); + storageClient.deleteFile(storePath.getGroup(), storePath.getPath()); + } + } + + /** + * 下载对象 + * @param objectPath 对象路径 + * @param callback 回调 + */ + public T download(String objectPath, DownloadCallback callback) { + if (!StringUtils.isEmpty(objectPath)) { + StorePath storePath = StorePath.parseFromUrl(objectPath); + return storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), callback); + } + return null; + } +} diff --git a/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/template/S3Template.java b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/template/S3Template.java new file mode 100644 index 00000000..eebf5433 --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/java/com/central/oss/template/S3Template.java @@ -0,0 +1,147 @@ +package com.central.oss.template; + +import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.util.IOUtils; +import com.central.oss.model.ObjectInfo; +import com.central.oss.properties.FileServerProperties; +import lombok.SneakyThrows; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.Calendar; + +/** + * aws s3配置 + * + * @author zlt + * @date 2021/2/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@ConditionalOnClass(AmazonS3.class) +@ConditionalOnProperty(prefix = FileServerProperties.PREFIX, name = "type", havingValue = FileServerProperties.TYPE_S3) +public class S3Template implements InitializingBean { + private static final String DEF_CONTEXT_TYPE = "application/octet-stream"; + private static final String PATH_SPLIT = "/"; + + @Autowired + private FileServerProperties fileProperties; + + private AmazonS3 amazonS3; + + @Override + public void afterPropertiesSet() { + ClientConfiguration config = new ClientConfiguration(); + AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration(fileProperties.getS3().getEndpoint(), fileProperties.getS3().getRegion()); + AWSCredentials credentials = new BasicAWSCredentials(fileProperties.getS3().getAccessKey(), fileProperties.getS3().getAccessKeySecret()); + AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(credentials); + this.amazonS3 = AmazonS3Client.builder() + .withEndpointConfiguration(endpoint) + .withClientConfiguration(config) + .withCredentials(awsCredentialsProvider) + .withPathStyleAccessEnabled(fileProperties.getS3().getPathStyleAccessEnabled()) + .disableChunkedEncoding() + .build(); + } + + @SneakyThrows + public ObjectInfo upload(String fileName, InputStream is) { + return upload(fileProperties.getS3().getBucketName(), fileName, is, is.available(), DEF_CONTEXT_TYPE); + } + + @SneakyThrows + public ObjectInfo upload(MultipartFile file) { + return upload(fileProperties.getS3().getBucketName(), file.getOriginalFilename(), file.getInputStream() + , ((Long)file.getSize()).intValue(), file.getContentType()); + } + + @SneakyThrows + public ObjectInfo upload(String bucketName, String fileName, InputStream is) { + return upload(bucketName, fileName, is, is.available(), DEF_CONTEXT_TYPE); + } + + /** + * 上传对象 + * @param bucketName bucket名称 + * @param objectName 对象名 + * @param is 对象流 + * @param size 大小 + * @param contentType 类型 + */ + private ObjectInfo upload(String bucketName, String objectName, InputStream is, int size, String contentType) { + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(size); + objectMetadata.setContentType(contentType); + PutObjectRequest putObjectRequest = new PutObjectRequest( + bucketName, objectName, is, objectMetadata); + putObjectRequest.getRequestClientOptions().setReadLimit(size + 1); + amazonS3.putObject(putObjectRequest); + + ObjectInfo obj = new ObjectInfo(); + obj.setObjectPath(bucketName + PATH_SPLIT + objectName); + obj.setObjectUrl(fileProperties.getS3().getEndpoint() + PATH_SPLIT + obj.getObjectPath()); + return obj; + } + + public void delete(String objectName) { + delete(fileProperties.getS3().getBucketName(), objectName); + } + + public void delete(String bucketName, String objectName) { + amazonS3.deleteObject(bucketName, objectName); + } + + /** + * 获取预览地址 + * @param bucketName bucket名称 + * @param objectName 对象名 + * @param expires 有效时间(分钟),最大7天有效 + * @return + */ + public String getViewUrl(String bucketName, String objectName, int expires) { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.MINUTE, expires); + URL url = amazonS3.generatePresignedUrl(bucketName, objectName, cal.getTime()); + return url.toString(); + } + + public void out(String objectName, OutputStream os) { + out(fileProperties.getS3().getBucketName(), objectName, os); + } + + /** + * 输出对象 + * @param bucketName bucket名称 + * @param objectName 对象名 + * @param os 输出流 + */ + @SneakyThrows + public void out(String bucketName, String objectName, OutputStream os) { + S3Object s3Object = amazonS3.getObject(bucketName, objectName); + try ( + S3ObjectInputStream s3is = s3Object.getObjectContent(); + ) { + IOUtils.copy(s3is, os); + } + } +} diff --git a/zlt-commons/zlt-oss-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-oss-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..43d799a7 --- /dev/null +++ b/zlt-commons/zlt-oss-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.central.oss.config.OssAutoConfigure \ No newline at end of file diff --git a/zlt-commons/zlt-redis-spring-boot-starter/pom.xml b/zlt-commons/zlt-redis-spring-boot-starter/pom.xml index fd525214..7360b0b1 100644 --- a/zlt-commons/zlt-redis-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-redis-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 4.0.0 jar @@ -16,19 +16,10 @@ com.zlt zlt-common-core + - org.springframework.boot - spring-boot-starter-data-redis - - - io.lettuce - lettuce-core - - - - - io.lettuce - lettuce-core + org.redisson + redisson-spring-boot-starter diff --git a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/RedisAutoConfigure.java b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/RedisAutoConfigure.java index 3bc5e5b8..6f25cec7 100644 --- a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/RedisAutoConfigure.java +++ b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/RedisAutoConfigure.java @@ -1,11 +1,8 @@ package com.central.common.redis; import com.central.common.redis.properties.CacheManagerProperties; -import com.central.common.redis.template.RedisRepository; -import com.central.common.redis.util.RedisObjectSerializer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.interceptor.KeyGenerator; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.CacheManager; @@ -27,6 +24,9 @@ * * @author zlt * @date 2018/11/6 11:02 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @EnableConfigurationProperties({RedisProperties.class, CacheManagerProperties.class}) @EnableCaching @@ -34,35 +34,45 @@ public class RedisAutoConfigure { @Autowired private CacheManagerProperties cacheManagerProperties; + @Bean + public RedisSerializer redisKeySerializer() { + return RedisSerializer.string(); + } + + @Bean + public RedisSerializer redisValueSerializer() { + return RedisSerializer.json(); + } + /** * RedisTemplate配置 * @param factory */ @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory factory) { + public RedisTemplate redisTemplate(RedisConnectionFactory factory + , RedisSerializer redisKeySerializer, RedisSerializer redisValueSerializer) { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); - RedisSerializer stringSerializer = new StringRedisSerializer(); - RedisSerializer redisObjectSerializer = new RedisObjectSerializer(); - redisTemplate.setKeySerializer(stringSerializer); - redisTemplate.setHashKeySerializer(stringSerializer); - redisTemplate.setValueSerializer(redisObjectSerializer); + redisTemplate.setDefaultSerializer(redisValueSerializer); + redisTemplate.setKeySerializer(redisKeySerializer); + redisTemplate.setHashKeySerializer(redisKeySerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean(name = "cacheManager") @Primary - public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { - RedisCacheConfiguration difConf = getDefConf().entryTtl(Duration.ofHours(1)); + public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory + , RedisSerializer redisKeySerializer, RedisSerializer redisValueSerializer) { + RedisCacheConfiguration difConf = getDefConf(redisKeySerializer, redisValueSerializer).entryTtl(Duration.ofHours(1)); //自定义的缓存过期时间配置 int configSize = cacheManagerProperties.getConfigs() == null ? 0 : cacheManagerProperties.getConfigs().size(); Map redisCacheConfigurationMap = new HashMap<>(configSize); if (configSize > 0) { cacheManagerProperties.getConfigs().forEach(e -> { - RedisCacheConfiguration conf = getDefConf().entryTtl(Duration.ofSeconds(e.getSecond())); + RedisCacheConfiguration conf = getDefConf(redisKeySerializer, redisValueSerializer).entryTtl(Duration.ofSeconds(e.getSecond())); redisCacheConfigurationMap.put(e.getKey(), conf); }); } @@ -86,11 +96,11 @@ public KeyGenerator keyGenerator() { }; } - private RedisCacheConfiguration getDefConf() { + private RedisCacheConfiguration getDefConf(RedisSerializer redisKeySerializer, RedisSerializer redisValueSerializer) { return RedisCacheConfiguration.defaultCacheConfig() .disableCachingNullValues() .computePrefixWith(cacheName -> "cache".concat(":").concat(cacheName).concat(":")) - .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) - .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new RedisObjectSerializer())); + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisKeySerializer)) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisValueSerializer)); } } diff --git a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/lock/RedisDistributedLock.java b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/lock/RedisDistributedLock.java index a4abd189..9b305e4c 100644 --- a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/lock/RedisDistributedLock.java +++ b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/lock/RedisDistributedLock.java @@ -1,14 +1,13 @@ package com.central.common.redis.lock; -import com.central.common.lock.AbstractDistributedLock; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.types.Expiration; -import org.springframework.stereotype.Component; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -18,9 +17,16 @@ * * @author zlt * @date 2018/5/29 14:16 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + * + * @deprecated 建议使用Redisson的实现方式 {@link RedissonDistributedLock} */ @Slf4j -public class RedisDistributedLock extends AbstractDistributedLock { +@ConditionalOnClass(RedisTemplate.class) +@Deprecated +public class RedisDistributedLock { @Autowired private RedisTemplate redisTemplate; @@ -45,7 +51,15 @@ public RedisDistributedLock(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } - @Override + /** + * 获取锁 + * + * @param key 锁的key + * @param expire 获取锁超时时间 + * @param retryTimes 重试次数 + * @param sleepMillis 获取锁失败的重试间隔 + * @return 成功/失败 + */ public boolean lock(String key, long expire, int retryTimes, long sleepMillis) { boolean result = setRedis(key, expire); // 如果获取锁失败,按照传入的重试次数进行重试 @@ -79,7 +93,11 @@ private boolean setRedis(final String key, final long expire) { return false; } - @Override + /** + * 释放锁 + * @param key 锁的key + * @return 成功/失败 + */ public boolean releaseLock(String key) { // 释放锁的时候,有可能因为持锁之后方法执行时间大于锁的有效期,此时有可能已经被另外一个线程持有锁,所以不能直接删除 try { diff --git a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/lock/RedissonDistributedLock.java b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/lock/RedissonDistributedLock.java new file mode 100644 index 00000000..fef19243 --- /dev/null +++ b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/lock/RedissonDistributedLock.java @@ -0,0 +1,73 @@ +package com.central.common.redis.lock; + +import com.central.common.constant.CommonConstant; +import com.central.common.exception.LockException; +import com.central.common.lock.DistributedLock; +import com.central.common.lock.ZLock; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +import java.util.concurrent.TimeUnit; + +/** + * redisson分布式锁实现,基本锁功能的抽象实现 + * 本接口能满足绝大部分的需求,高级的锁功能,请自行扩展或直接使用原生api + * https://gitbook.cn/gitchat/activity/5f02746f34b17609e14c7d5a + * + * @author zlt + * @date 2020/5/5 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@ConditionalOnClass(RedissonClient.class) +@ConditionalOnProperty(prefix = "zlt.lock", name = "lockerType", havingValue = "REDIS", matchIfMissing = true) +public class RedissonDistributedLock implements DistributedLock { + @Autowired + private RedissonClient redisson; + + private ZLock getLock(String key, boolean isFair) { + RLock lock; + if (isFair) { + lock = redisson.getFairLock(CommonConstant.LOCK_KEY_PREFIX + ":" + key); + } else { + lock = redisson.getLock(CommonConstant.LOCK_KEY_PREFIX + ":" + key); + } + return new ZLock(lock, this); + } + + @Override + public ZLock lock(String key, long leaseTime, TimeUnit unit, boolean isFair) { + ZLock zLock = getLock(key, isFair); + RLock lock = (RLock)zLock.getLock(); + lock.lock(leaseTime, unit); + return zLock; + } + + @Override + public ZLock tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, boolean isFair) throws InterruptedException { + ZLock zLock = getLock(key, isFair); + RLock lock = (RLock)zLock.getLock(); + if (lock.tryLock(waitTime, leaseTime, unit)) { + return zLock; + } + return null; + } + + @Override + public void unlock(Object lock) { + if (lock != null) { + if (lock instanceof RLock) { + RLock rLock = (RLock)lock; + if (rLock.isLocked()) { + rLock.unlock(); + } + } else { + throw new LockException("requires RLock type"); + } + } + } +} diff --git a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/template/RedisRepository.java b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/template/RedisRepository.java index abdcf2e9..7c80302f 100644 --- a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/template/RedisRepository.java +++ b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/template/RedisRepository.java @@ -1,505 +1,498 @@ -package com.central.common.redis.template; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections4.CollectionUtils; -import org.springframework.data.redis.connection.RedisClusterNode; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.RedisServerCommands; -import org.springframework.data.redis.core.HashOperations; -import org.springframework.data.redis.core.ListOperations; -import org.springframework.data.redis.core.RedisCallback; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.*; - -/** - * Redis Repository - * redis 基本操作 可扩展,基本够用了 - * - * @author zlt - */ -@Slf4j -public class RedisRepository { - /** - * 默认编码 - */ - private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; - - /** - * key序列化 - */ - private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer(); - - /** - * value 序列化 - */ - private static final JdkSerializationRedisSerializer OBJECT_SERIALIZER = new JdkSerializationRedisSerializer(); - - /** - * Spring Redis Template - */ - private RedisTemplate redisTemplate; - - public RedisRepository(RedisTemplate redisTemplate) { - this.redisTemplate = redisTemplate; - this.redisTemplate.setKeySerializer(STRING_SERIALIZER); - this.redisTemplate.setValueSerializer(OBJECT_SERIALIZER); - } - - /** - * 获取链接工厂 - */ - public RedisConnectionFactory getConnectionFactory() { - return this.redisTemplate.getConnectionFactory(); - } - - /** - * 获取 RedisTemplate对象 - */ - public RedisTemplate getRedisTemplate() { - return redisTemplate; - } - - /** - * 清空DB - * - * @param node redis 节点 - */ - public void flushDB(RedisClusterNode node) { - this.redisTemplate.opsForCluster().flushDb(node); - } - - /** - * 添加到带有 过期时间的 缓存 - * - * @param key redis主键 - * @param value 值 - * @param time 过期时间(单位秒) - */ - public void setExpire(final byte[] key, final byte[] value, final long time) { - redisTemplate.execute((RedisCallback) connection -> { - connection.setEx(key, time, value); - log.debug("[redisTemplate redis]放入 缓存 url:{} ========缓存时间为{}秒", key, time); - return 1L; - }); - } - - /** - * 添加到带有 过期时间的 缓存 - * - * @param key redis主键 - * @param value 值 - * @param time 过期时间(单位秒) - */ - public void setExpire(final String key, final Object value, final long time) { - redisTemplate.execute((RedisCallback) connection -> { - RedisSerializer serializer = getRedisSerializer(); - byte[] keys = serializer.serialize(key); - byte[] values = OBJECT_SERIALIZER.serialize(value); - connection.setEx(keys, time, values); - return 1L; - }); - } - - /** - * 一次性添加数组到 过期时间的 缓存,不用多次连接,节省开销 - * - * @param keys redis主键数组 - * @param values 值数组 - * @param time 过期时间(单位秒) - */ - public void setExpire(final String[] keys, final Object[] values, final long time) { - redisTemplate.execute((RedisCallback) connection -> { - RedisSerializer serializer = getRedisSerializer(); - for (int i = 0; i < keys.length; i++) { - byte[] bKeys = serializer.serialize(keys[i]); - byte[] bValues = OBJECT_SERIALIZER.serialize(values[i]); - connection.setEx(bKeys, time, bValues); - } - return 1L; - }); - } - - - /** - * 一次性添加数组到 过期时间的 缓存,不用多次连接,节省开销 - * - * @param keys the keys - * @param values the values - */ - public void set(final String[] keys, final Object[] values) { - redisTemplate.execute((RedisCallback) connection -> { - RedisSerializer serializer = getRedisSerializer(); - for (int i = 0; i < keys.length; i++) { - byte[] bKeys = serializer.serialize(keys[i]); - byte[] bValues = OBJECT_SERIALIZER.serialize(values[i]); - connection.set(bKeys, bValues); - } - return 1L; - }); - } - - - /** - * 添加到缓存 - * - * @param key the key - * @param value the value - */ - public void set(final String key, final Object value) { - redisTemplate.execute((RedisCallback) connection -> { - RedisSerializer serializer = getRedisSerializer(); - byte[] keys = serializer.serialize(key); - byte[] values = OBJECT_SERIALIZER.serialize(value); - connection.set(keys, values); - log.debug("[redisTemplate redis]放入 缓存 url:{}", key); - return 1L; - }); - } - - /** - * 查询在这个时间段内即将过期的key - * - * @param key the key - * @param time the time - * @return the list - */ - public List willExpire(final String key, final long time) { - final List keysList = new ArrayList<>(); - redisTemplate.execute((RedisCallback>) connection -> { - Set keys = redisTemplate.keys(key + "*"); - for (String key1 : keys) { - Long ttl = connection.ttl(key1.getBytes(DEFAULT_CHARSET)); - if (0 <= ttl && ttl <= 2 * time) { - keysList.add(key1); - } - } - return keysList; - }); - return keysList; - } - - - /** - * 查询在以keyPatten的所有 key - * - * @param keyPatten the key patten - * @return the set - */ - public Set keys(final String keyPatten) { - return redisTemplate.execute((RedisCallback>) connection -> redisTemplate.keys(keyPatten + "*")); - } - - /** - * 根据key获取对象 - * - * @param key the key - * @return the byte [ ] - */ - public byte[] get(final byte[] key) { - byte[] result = redisTemplate.execute((RedisCallback) connection -> connection.get(key)); - log.debug("[redisTemplate redis]取出 缓存 url:{} ", key); - return result; - } - - /** - * 根据key获取对象 - * - * @param key the key - * @return the string - */ - public Object get(final String key) { - Object resultStr = redisTemplate.execute((RedisCallback) connection -> { - RedisSerializer serializer = getRedisSerializer(); - byte[] keys = serializer.serialize(key); - byte[] values = connection.get(keys); - return OBJECT_SERIALIZER.deserialize(values); - }); - log.debug("[redisTemplate redis]取出 缓存 url:{} ", key); - return resultStr; - } - - - /** - * 根据key获取对象 - * - * @param keyPatten the key patten - * @return the keys values - */ - public Map getKeysValues(final String keyPatten) { - log.debug("[redisTemplate redis] getValues() patten={} ", keyPatten); - return redisTemplate.execute((RedisCallback>) connection -> { - RedisSerializer serializer = getRedisSerializer(); - Map maps = new HashMap<>(16); - Set keys = redisTemplate.keys(keyPatten + "*"); - if (CollectionUtils.isNotEmpty(keys)) { - for (String key : keys) { - byte[] bKeys = serializer.serialize(key); - byte[] bValues = connection.get(bKeys); - Object value = OBJECT_SERIALIZER.deserialize(bValues); - maps.put(key, value); - } - } - return maps; - }); - } - - /** - * Ops for hash hash operations. - * - * @return the hash operations - */ - public HashOperations opsForHash() { - return redisTemplate.opsForHash(); - } - - /** - * 对HashMap操作 - * - * @param key the key - * @param hashKey the hash key - * @param hashValue the hash value - */ - public void putHashValue(String key, String hashKey, Object hashValue) { - log.debug("[redisTemplate redis] putHashValue() key={},hashKey={},hashValue={} ", key, hashKey, hashValue); - opsForHash().put(key, hashKey, hashValue); - } - - /** - * 获取单个field对应的值 - * - * @param key the key - * @param hashKey the hash key - * @return the hash values - */ - public Object getHashValues(String key, String hashKey) { - log.debug("[redisTemplate redis] getHashValues() key={},hashKey={}", key, hashKey); - return opsForHash().get(key, hashKey); - } - - /** - * 根据key值删除 - * - * @param key the key - * @param hashKeys the hash keys - */ - public void delHashValues(String key, Object... hashKeys) { - log.debug("[redisTemplate redis] delHashValues() key={}", key); - opsForHash().delete(key, hashKeys); - } - - /** - * key只匹配map - * - * @param key the key - * @return the hash value - */ - public Map getHashValue(String key) { - log.debug("[redisTemplate redis] getHashValue() key={}", key); - return opsForHash().entries(key); - } - - /** - * 批量添加 - * - * @param key the key - * @param map the map - */ - public void putHashValues(String key, Map map) { - opsForHash().putAll(key, map); - } - - /** - * 集合数量 - * - * @return the long - */ - public long dbSize() { - return redisTemplate.execute(RedisServerCommands::dbSize); - } - - /** - * 清空redis存储的数据 - * - * @return the string - */ - public String flushDB() { - return redisTemplate.execute((RedisCallback) connection -> { - connection.flushDb(); - return "ok"; - }); - } - - /** - * 判断某个主键是否存在 - * - * @param key the key - * @return the boolean - */ - public boolean exists(final String key) { - return redisTemplate.execute((RedisCallback) connection -> connection.exists(key.getBytes(DEFAULT_CHARSET))); - } - - - /** - * 删除key - * - * @param keys the keys - * @return the long - */ - public long del(final String... keys) { - return redisTemplate.execute((RedisCallback) connection -> { - long result = 0; - for (String key : keys) { - result = connection.del(key.getBytes(DEFAULT_CHARSET)); - } - return result; - }); - } - - /** - * 获取 RedisSerializer - * - * @return the redis serializer - */ - protected RedisSerializer getRedisSerializer() { - return redisTemplate.getStringSerializer(); - } - - /** - * 对某个主键对应的值加一,value值必须是全数字的字符串 - * - * @param key the key - * @return the long - */ - public long incr(final String key) { - return redisTemplate.execute((RedisCallback) connection -> { - RedisSerializer redisSerializer = getRedisSerializer(); - return connection.incr(redisSerializer.serialize(key)); - }); - } - - /** - * redis List 引擎 - * - * @return the list operations - */ - public ListOperations opsForList() { - return redisTemplate.opsForList(); - } - - /** - * redis List数据结构 : 将一个或多个值 value 插入到列表 key 的表头 - * - * @param key the key - * @param value the value - * @return the long - */ - public Long leftPush(String key, Object value) { - return opsForList().leftPush(key, value); - } - - /** - * redis List数据结构 : 移除并返回列表 key 的头元素 - * - * @param key the key - * @return the string - */ - public Object leftPop(String key) { - return opsForList().leftPop(key); - } - - /** - * redis List数据结构 :将一个或多个值 value 插入到列表 key 的表尾(最右边)。 - * - * @param key the key - * @param value the value - * @return the long - */ - public Long in(String key, Object value) { - return opsForList().rightPush(key, value); - } - - /** - * redis List数据结构 : 移除并返回列表 key 的末尾元素 - * - * @param key the key - * @return the string - */ - public Object rightPop(String key) { - return opsForList().rightPop(key); - } - - - /** - * redis List数据结构 : 返回列表 key 的长度 ; 如果 key 不存在,则 key 被解释为一个空列表,返回 0 ; 如果 key 不是列表类型,返回一个错误。 - * - * @param key the key - * @return the long - */ - public Long length(String key) { - return opsForList().size(key); - } - - - /** - * redis List数据结构 : 根据参数 i 的值,移除列表中与参数 value 相等的元素 - * - * @param key the key - * @param i the - * @param value the value - */ - public void remove(String key, long i, Object value) { - opsForList().remove(key, i, value); - } - - /** - * redis List数据结构 : 将列表 key 下标为 index 的元素的值设置为 value - * - * @param key the key - * @param index the index - * @param value the value - */ - public void set(String key, long index, Object value) { - opsForList().set(key, index, value); - } - - /** - * redis List数据结构 : 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 end 指定。 - * - * @param key the key - * @param start the start - * @param end the end - * @return the list - */ - public List getList(String key, int start, int end) { - return opsForList().range(key, start, end); - } - - /** - * redis List数据结构 : 批量存储 - * - * @param key the key - * @param list the list - * @return the long - */ - public Long leftPushAll(String key, List list) { - return opsForList().leftPushAll(key, list); - } - - /** - * redis List数据结构 : 将值 value 插入到列表 key 当中,位于值 index 之前或之后,默认之后。 - * - * @param key the key - * @param index the index - * @param value the value - */ - public void insert(String key, long index, Object value) { - opsForList().set(key, index, value); - } -} +package com.central.common.redis.template; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.connection.RedisClusterNode; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisServerCommands; +import org.springframework.data.redis.core.*; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationUtils; +import org.springframework.util.Assert; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Redis Repository + * redis 基本操作 可扩展,基本够用了 + * + * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +public class RedisRepository { + /** + * Spring Redis Template + */ + private RedisTemplate redisTemplate; + + public RedisRepository(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + /** + * 获取链接工厂 + */ + public RedisConnectionFactory getConnectionFactory() { + return this.redisTemplate.getConnectionFactory(); + } + + /** + * 获取 RedisTemplate对象 + */ + public RedisTemplate getRedisTemplate() { + return redisTemplate; + } + + /** + * 清空DB + * + * @param node redis 节点 + */ + public void flushDB(RedisClusterNode node) { + this.redisTemplate.opsForCluster().flushDb(node); + } + + /** + * 添加到带有 过期时间的 缓存 + * + * @param key redis主键 + * @param value 值 + * @param time 过期时间(单位秒) + */ + public void setExpire(final byte[] key, final byte[] value, final long time) { + redisTemplate.execute((RedisCallback) connection -> { + connection.setEx(key, time, value); + return 1L; + }); + } + + /** + * 添加到带有 过期时间的 缓存 + * + * @param key redis主键 + * @param value 值 + * @param time 过期时间 + * @param timeUnit 过期时间单位 + */ + public void setExpire(final String key, final Object value, final long time, final TimeUnit timeUnit) { + redisTemplate.opsForValue().set(key, value, time, timeUnit); + } + public void setExpire(final String key, final Object value, final long time) { + this.setExpire(key, value, time, TimeUnit.SECONDS); + } + public void setExpire(final String key, final Object value, final long time, final TimeUnit timeUnit, RedisSerializer valueSerializer) { + byte[] rawKey = rawKey(key); + byte[] rawValue = rawValue(value, valueSerializer); + + redisTemplate.execute(new RedisCallback() { + @Override + public Object doInRedis(RedisConnection connection) throws DataAccessException { + potentiallyUsePsetEx(connection); + return null; + } + public void potentiallyUsePsetEx(RedisConnection connection) { + if (!TimeUnit.MILLISECONDS.equals(timeUnit) || !failsafeInvokePsetEx(connection)) { + connection.setEx(rawKey, TimeoutUtils.toSeconds(time, timeUnit), rawValue); + } + } + private boolean failsafeInvokePsetEx(RedisConnection connection) { + boolean failed = false; + try { + connection.pSetEx(rawKey, time, rawValue); + } catch (UnsupportedOperationException e) { + failed = true; + } + return !failed; + } + }, true); + } + + /** + * 一次性添加数组到 过期时间的 缓存,不用多次连接,节省开销 + * + * @param keys redis主键数组 + * @param values 值数组 + * @param time 过期时间(单位秒) + */ + public void setExpire(final String[] keys, final Object[] values, final long time) { + for (int i = 0; i < keys.length; i++) { + redisTemplate.opsForValue().set(keys[i], values[i], time, TimeUnit.SECONDS); + } + } + + + /** + * 一次性添加数组到 过期时间的 缓存,不用多次连接,节省开销 + * + * @param keys the keys + * @param values the values + */ + public void set(final String[] keys, final Object[] values) { + for (int i = 0; i < keys.length; i++) { + redisTemplate.opsForValue().set(keys[i], values[i]); + } + } + + + /** + * 添加到缓存 + * + * @param key the key + * @param value the value + */ + public void set(final String key, final Object value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 查询在以keyPatten的所有 key + * + * @param keyPatten the key patten + * @return the set + */ + public Set keys(final String keyPatten) { + return redisTemplate.keys(keyPatten + "*"); + } + + /** + * 根据key获取对象 + * + * @param key the key + * @return the byte [ ] + */ + public byte[] get(final byte[] key) { + return redisTemplate.execute((RedisCallback) connection -> connection.get(key)); + } + + /** + * 根据key获取对象 + * + * @param key the key + * @return the string + */ + public Object get(final String key) { + return redisTemplate.opsForValue().get(key); + } + + /** + *获取原来key键对应的值并重新赋新值。 + * @param key + * @param value + * @return + */ + public String getAndSet(final String key,String value) { + String result = null; + if (StringUtils.isEmpty(key)){ + log.error("非法入参"); + return null; + } + try { + Object object =redisTemplate.opsForValue().getAndSet(key, value); + if (object !=null){ + result = object.toString(); + } + }catch (Exception e){ + log.error("redisTemplate操作异常",e); + } + return result; + } + /** + * 根据key获取对象 + * + * @param key the key + * @param valueSerializer 序列化 + * @return the string + */ + public Object get(final String key, RedisSerializer valueSerializer) { + byte[] rawKey = rawKey(key); + return redisTemplate.execute(connection -> deserializeValue(connection.get(rawKey), valueSerializer), true); + } + + + /** + * Ops for hash hash operations. + * + * @return the hash operations + */ + public HashOperations opsForHash() { + return redisTemplate.opsForHash(); + } + + /** + * 对HashMap操作 + * + * @param key the key + * @param hashKey the hash key + * @param hashValue the hash value + */ + public void putHashValue(String key, String hashKey, Object hashValue) { + opsForHash().put(key, hashKey, hashValue); + } + + /** + * 获取单个field对应的值 + * + * @param key the key + * @param hashKey the hash key + * @return the hash values + */ + public Object getHashValues(String key, String hashKey) { + return opsForHash().get(key, hashKey); + } + + /** + * 根据key值删除 + * + * @param key the key + * @param hashKeys the hash keys + */ + public void delHashValues(String key, Object... hashKeys) { + opsForHash().delete(key, hashKeys); + } + + /** + * key只匹配map + * + * @param key the key + * @return the hash value + */ + public Map getHashValue(String key) { + return opsForHash().entries(key); + } + + /** + * 批量添加 + * + * @param key the key + * @param map the map + */ + public void putHashValues(String key, Map map) { + opsForHash().putAll(key, map); + } + + /** + * 集合数量 + * + * @return the long + */ + public long dbSize() { + return redisTemplate.execute(RedisServerCommands::dbSize); + } + + /** + * 清空redis存储的数据 + * + * @return the string + */ + public String flushDB() { + return redisTemplate.execute((RedisCallback) connection -> { + connection.flushDb(); + return "ok"; + }); + } + + /** + * 判断某个主键是否存在 + * + * @param key the key + * @return the boolean + */ + public boolean exists(final String key) { + return redisTemplate.hasKey(key); + } + + + /** + * 删除key + * + * @param keys the keys + * @return the long + */ + public boolean del(final String... keys) { + boolean result = false; + for (String key : keys) { + result = redisTemplate.delete(key); + } + return result; + } + + /** + * 对某个主键对应的值加一,value值必须是全数字的字符串 + * + * @param key the key + * @return the long + */ + public long incr(final String key) { + return redisTemplate.opsForValue().increment(key); + } + + /** + * redis List 引擎 + * + * @return the list operations + */ + public ListOperations opsForList() { + return redisTemplate.opsForList(); + } + + /** + * redis List数据结构 : 将一个或多个值 value 插入到列表 key 的表头 + * + * @param key the key + * @param value the value + * @return the long + */ + public Long leftPush(String key, Object value) { + return opsForList().leftPush(key, value); + } + + /** + * redis List数据结构 : 移除并返回列表 key 的头元素 + * + * @param key the key + * @return the string + */ + public Object leftPop(String key) { + return opsForList().leftPop(key); + } + + /** + * redis List数据结构 :将一个或多个值 value 插入到列表 key 的表尾(最右边)。 + * + * @param key the key + * @param value the value + * @return the long + */ + public Long in(String key, Object value) { + return opsForList().rightPush(key, value); + } + + /** + * redis List数据结构 : 移除并返回列表 key 的末尾元素 + * + * @param key the key + * @return the string + */ + public Object rightPop(String key) { + return opsForList().rightPop(key); + } + + + /** + * redis List数据结构 : 返回列表 key 的长度 ; 如果 key 不存在,则 key 被解释为一个空列表,返回 0 ; 如果 key 不是列表类型,返回一个错误。 + * + * @param key the key + * @return the long + */ + public Long length(String key) { + return opsForList().size(key); + } + + + /** + * redis List数据结构 : 根据参数 i 的值,移除列表中与参数 value 相等的元素 + * + * @param key the key + * @param i the + * @param value the value + */ + public void remove(String key, long i, Object value) { + opsForList().remove(key, i, value); + } + + /** + * redis List数据结构 : 将列表 key 下标为 index 的元素的值设置为 value + * + * @param key the key + * @param index the index + * @param value the value + */ + public void set(String key, long index, Object value) { + opsForList().set(key, index, value); + } + + /** + * redis List数据结构 : 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 end 指定。 + * + * @param key the key + * @param start the start + * @param end the end + * @return the list + */ + public List getList(String key, int start, int end) { + return opsForList().range(key, start, end); + } + + /** + * redis List数据结构 : 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 end 指定。 + * + * @param key the key + * @param start the start + * @param end the end + * @param valueSerializer 序列化 + * @return the list + */ + public List getList(String key, int start, int end, RedisSerializer valueSerializer) { + byte[] rawKey = rawKey(key); + return redisTemplate.execute(connection -> deserializeValues(connection.lRange(rawKey, start, end), valueSerializer), true); + } + + /** + * redis List数据结构 : 批量存储 + * + * @param key the key + * @param list the list + * @return the long + */ + public Long leftPushAll(String key, List list) { + return opsForList().leftPushAll(key, list); + } + + /** + * redis List数据结构 : 将值 value 插入到列表 key 当中,位于值 index 之前或之后,默认之后。 + * + * @param key the key + * @param index the index + * @param value the value + */ + public void insert(String key, long index, Object value) { + opsForList().set(key, index, value); + } + + private byte[] rawKey(Object key) { + Assert.notNull(key, "non null key required"); + + if (key instanceof byte[]) { + return (byte[]) key; + } + RedisSerializer redisSerializer = (RedisSerializer)redisTemplate.getKeySerializer(); + return redisSerializer.serialize(key); + } + private byte[] rawValue(Object value, RedisSerializer valueSerializer) { + if (value instanceof byte[]) { + return (byte[]) value; + } + + return valueSerializer.serialize(value); + } + + private List deserializeValues(List rawValues, RedisSerializer valueSerializer) { + if (valueSerializer == null) { + return rawValues; + } + return SerializationUtils.deserialize(rawValues, valueSerializer); + } + + private Object deserializeValue(byte[] value, RedisSerializer valueSerializer) { + if (valueSerializer == null) { + return value; + } + return valueSerializer.deserialize(value); + } +} diff --git a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/util/RedisObjectSerializer.java b/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/util/RedisObjectSerializer.java deleted file mode 100644 index 3fe888cc..00000000 --- a/zlt-commons/zlt-redis-spring-boot-starter/src/main/java/com/central/common/redis/util/RedisObjectSerializer.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.central.common.redis.util; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.serializer.support.DeserializingConverter; -import org.springframework.core.serializer.support.SerializingConverter; -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.data.redis.serializer.SerializationException; - -/** - * 此时定义的序列化操作表示可以序列化所有类的对象,当然,这个对象所在的类一定要实现序列化接口 - * - * @author zlt - */ -public class RedisObjectSerializer implements RedisSerializer { - // 做一个空数组,不是null - private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - // 为了方便进行对象与字节数组的转换,所以应该首先准备出两个转换器 - private Converter serializingConverter = new SerializingConverter(); - private Converter deserializingConverter = new DeserializingConverter(); - - @Override - public byte[] serialize(Object obj) { - // 这个时候没有要序列化的对象出现,所以返回的字节数组应该就是一个空数组 - if (obj == null) { - return EMPTY_BYTE_ARRAY; - } - // 将对象变为字节数组 - return this.serializingConverter.convert(obj); - } - - @Override - public Object deserialize(byte[] data) { - // 此时没有对象的内容信息 - if (data == null || data.length == 0) { - return null; - } - return this.deserializingConverter.convert(data); - } - -} \ No newline at end of file diff --git a/zlt-commons/zlt-redis-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-redis-spring-boot-starter/src/main/resources/META-INF/spring.factories index 924601e9..c0dc55ef 100644 --- a/zlt-commons/zlt-redis-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/zlt-commons/zlt-redis-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1,4 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.central.common.redis.RedisAutoConfigure,\ -com.central.common.redis.lock.RedisDistributedLock,\ +com.central.common.redis.lock.RedissonDistributedLock,\ com.central.common.redis.template.RedisRepository \ No newline at end of file diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/FeignAutoConfigure.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/FeignAutoConfigure.java deleted file mode 100644 index afa6bbbd..00000000 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/FeignAutoConfigure.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.central.common.ribbon; - -import feign.Logger; -import org.springframework.context.annotation.Bean; - -/** - * Feign统一配置 - * - * @author zlt - * @date 2018/9/18 14:04 - */ -public class FeignAutoConfigure { - - /** - * Feign 日志级别 - */ - @Bean - Logger.Level feignLoggerLevel() { - return Logger.Level.FULL; - } -} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/LbIsolationAutoConfigure.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/LbIsolationAutoConfigure.java deleted file mode 100644 index b6943875..00000000 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/LbIsolationAutoConfigure.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.central.common.ribbon; - -import com.alibaba.cloud.nacos.ribbon.NacosServer; -import com.central.common.constant.ConfigConstants; -import com.central.common.ribbon.rule.VersionIsolationRule; -import com.netflix.loadbalancer.IRule; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration; -import org.springframework.context.annotation.Bean; - -/** - * 自定义负载均衡配置 - * - * @author zlt - * @date 2019/9/3 - *

- * Blog: https://blog.csdn.net/zlt2000 - * Github: https://github.com/zlt2000 - */ -@ConditionalOnProperty(value = ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED, havingValue = "true") -@AutoConfigureBefore(RibbonClientConfiguration.class) -public class LbIsolationAutoConfigure { - @Bean - @ConditionalOnClass(NacosServer.class) - @ConditionalOnMissingBean - public IRule versionIsolationRule() { - return new VersionIsolationRule(); - } -} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RibbonAutoConfigure.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RibbonAutoConfigure.java deleted file mode 100644 index df4983c8..00000000 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RibbonAutoConfigure.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.central.common.ribbon; - -import com.central.common.ribbon.config.RestTemplateProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.netflix.ribbon.DefaultPropertiesFactory; -import org.springframework.context.annotation.Bean; - -/** - * Ribbon扩展配置类 - * - * @author zlt - * @date 2018/11/17 9:24 - */ -@EnableConfigurationProperties(RestTemplateProperties.class) -public class RibbonAutoConfigure { - @Bean - public DefaultPropertiesFactory defaultPropertiesFactory() { - return new DefaultPropertiesFactory(); - } -} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/filter/LbIsolationFilter.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/filter/LbIsolationFilter.java deleted file mode 100644 index 22c5e4ff..00000000 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/filter/LbIsolationFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.central.common.ribbon.filter; - -import cn.hutool.core.util.StrUtil; -import com.central.common.constant.CommonConstant; -import com.central.common.constant.ConfigConstants; -import com.central.common.context.LbIsolationContextHolder; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 负载均衡隔离规则过滤器 - * - * @author zlt - * @date 2019/9/15 - */ -@ConditionalOnClass(Filter.class) -public class LbIsolationFilter extends OncePerRequestFilter { - @Value("${" + ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED + ":false}") - private boolean enableIsolation; - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - return !enableIsolation; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { - try { - String version = request.getHeader(CommonConstant.Z_L_T_VERSION); - if(StrUtil.isNotEmpty(version)){ - LbIsolationContextHolder.setVersion(version); - } - - filterChain.doFilter(request, response); - } finally { - LbIsolationContextHolder.clear(); - } - } -} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/rule/VersionIsolationRule.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/rule/VersionIsolationRule.java deleted file mode 100644 index 5f2e4fdb..00000000 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/rule/VersionIsolationRule.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.central.common.ribbon.rule; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.RandomUtil; -import cn.hutool.core.util.StrUtil; -import com.alibaba.cloud.nacos.ribbon.NacosServer; -import com.central.common.constant.CommonConstant; -import com.central.common.context.LbIsolationContextHolder; -import com.netflix.loadbalancer.*; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * 根据实例版本号隔离规则 - * 详细用法:https://mp.weixin.qq.com/s/9XQ-SIbYsov3KBx9TGFN0g - * - * 实例获取规则顺序如下(不满足则走下一个规则): - * 1. 相同版本号的实例 - * 2. 无版本号的实例 - * 3. 所有实例中轮询 - * - * @author zlt - * @date 2019/9/3 - *

- * Blog: https://blog.csdn.net/zlt2000 - * Github: https://github.com/zlt2000 - */ -public class VersionIsolationRule extends RoundRobinRule { - private final static String KEY_DEFAULT = "default"; - /** - * 优先根据版本号取实例 - */ - @Override - public Server choose(ILoadBalancer lb, Object key) { - if (lb == null) { - return null; - } - String version; - if (key != null && !KEY_DEFAULT.equals(key)) { - version = key.toString(); - } else { - version = LbIsolationContextHolder.getVersion(); - } - - List targetList = null; - List upList = lb.getReachableServers(); - if (StrUtil.isNotEmpty(version)) { - //取指定版本号的实例 - targetList = upList.stream().filter( - server -> version.equals( - ((NacosServer) server).getMetadata().get(CommonConstant.METADATA_VERSION) - ) - ).collect(Collectors.toList()); - } - - if (CollUtil.isEmpty(targetList)) { - //只取无版本号的实例 - targetList = upList.stream().filter( - server -> { - String metadataVersion = ((NacosServer) server).getMetadata().get(CommonConstant.METADATA_VERSION); - return StrUtil.isEmpty(metadataVersion); - } - ).collect(Collectors.toList()); - } - - if (CollUtil.isNotEmpty(targetList)) { - return getServer(targetList); - } - return super.choose(lb, key); - } - - /** - * 随机取一个实例 - */ - private Server getServer(List upList) { - int nextInt = RandomUtil.randomInt(upList.size()); - return upList.get(nextInt); - } -} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/org/springframework/cloud/netflix/ribbon/DefaultPropertiesFactory.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/org/springframework/cloud/netflix/ribbon/DefaultPropertiesFactory.java deleted file mode 100644 index 8dd490a1..00000000 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/org/springframework/cloud/netflix/ribbon/DefaultPropertiesFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.springframework.cloud.netflix.ribbon; - -import com.netflix.loadbalancer.*; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.util.StringUtils; - -import java.util.HashMap; -import java.util.Map; - -import static org.springframework.cloud.netflix.ribbon.SpringClientFactory.NAMESPACE; - -/** - * 扩展 spring cloud ribbon的PropertiesFactory - * 使其能够支持 配置全局的ribbon.NFLoadBalancerRuleClassName=package.YourRule - * 然后各个微服务还可以根据自身情况做个性化定制。如:SERVICE_ID.ribbon.NFLoadBalancerRuleClassName=package.YourRule - * - * @author zlt - * @date 2018/11/17 9:29 - */ -public class DefaultPropertiesFactory extends PropertiesFactory { - - @Autowired - private Environment environment; - - private Map classToProperty = new HashMap<>(5); - - public DefaultPropertiesFactory() { - super(); - classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName"); - classToProperty.put(IPing.class, "NFLoadBalancerPingClassName"); - classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName"); - classToProperty.put(ServerList.class, "NIWSServerListClassName"); - classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName"); - } - - /** - * 重写 支持 ribbon.NFLoadBalancerRuleClassName=package.YourRule 全局配置的方式 - */ - @Override - public String getClassName(Class clazz, String name) { - String className = super.getClassName(clazz, name); - // 读取全局配置 - if(!StringUtils.hasText(className) && this.classToProperty.containsKey(clazz)){ - String classNameProperty = this.classToProperty.get(clazz); - className = environment.getProperty(NAMESPACE + "." + classNameProperty); - } - return className; - } -} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 16f3fb91..00000000 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,6 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.central.common.ribbon.RibbonAutoConfigure,\ -com.central.common.ribbon.FeignAutoConfigure,\ -com.central.common.ribbon.RestTemplateAutoConfigure,\ -com.central.common.ribbon.filter.LbIsolationFilter,\ -com.central.common.ribbon.LbIsolationAutoConfigure diff --git a/zlt-commons/zlt-sentinel-spring-boot-starter/pom.xml b/zlt-commons/zlt-sentinel-spring-boot-starter/pom.xml index 34b849d1..ebe27115 100644 --- a/zlt-commons/zlt-sentinel-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-sentinel-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 4.0.0 jar @@ -40,5 +40,11 @@ spring-boot-configuration-processor true + + + org.springframework + spring-webflux + true + diff --git a/zlt-commons/zlt-sentinel-spring-boot-starter/src/main/java/com/central/sentinel/config/SentinelAutoConfigure.java b/zlt-commons/zlt-sentinel-spring-boot-starter/src/main/java/com/central/sentinel/config/SentinelAutoConfigure.java index 1ff5bc5e..864d8535 100644 --- a/zlt-commons/zlt-sentinel-spring-boot-starter/src/main/java/com/central/sentinel/config/SentinelAutoConfigure.java +++ b/zlt-commons/zlt-sentinel-spring-boot-starter/src/main/java/com/central/sentinel/config/SentinelAutoConfigure.java @@ -1,34 +1,58 @@ package com.central.sentinel.config; import cn.hutool.json.JSONUtil; -import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler; -import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager; -import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.adapter.spring.webflux.callback.BlockRequestHandler; +import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.central.common.model.Result; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.server.ServerResponse; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; /** * Sentinel配置类 * * @author zlt * @date 2019/1/22 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public class SentinelAutoConfigure { - public SentinelAutoConfigure() { - WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler()); + /** + * 限流、熔断统一处理类 + */ + @Configuration + @ConditionalOnClass(HttpServletRequest.class) + public static class WebmvcHandler { + @Bean + public BlockExceptionHandler webmvcBlockExceptionHandler() { + return (request, response, e) -> { + response.setStatus(429); + Result result = Result.failed(e.getMessage()); + response.getWriter().print(JSONUtil.toJsonStr(result)); + }; + } } + /** * 限流、熔断统一处理类 */ - public class CustomUrlBlockHandler implements UrlBlockHandler { - @Override - public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException { - Result result = Result.failed("flow-limiting"); - httpServletResponse.getWriter().print(JSONUtil.toJsonStr(result)); + @Configuration + @ConditionalOnClass(ServerResponse.class) + public static class WebfluxHandler { + @Bean + public BlockRequestHandler webfluxBlockExceptionHandler() { + return (exchange, t) -> + ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS) + .contentType(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue(Result.failed(t.getMessage()))); } } } diff --git a/zlt-commons/zlt-swagger2-spring-boot-starter/pom.xml b/zlt-commons/zlt-swagger2-spring-boot-starter/pom.xml index 8136ed25..9e6471bb 100644 --- a/zlt-commons/zlt-swagger2-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-swagger2-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.zlt zlt-commons - 3.5.0 + 5.4.0 4.0.0 jar @@ -17,12 +17,8 @@ spring-boot-starter - io.springfox - springfox-swagger-ui - - - io.springfox - springfox-swagger2 + com.github.xiaoymin + knife4j-micro-spring-boot-starter org.springframework.boot diff --git a/zlt-commons/zlt-swagger2-spring-boot-starter/src/main/java/com/central/common/swagger2/Swagger2Configuration.java b/zlt-commons/zlt-swagger2-spring-boot-starter/src/main/java/com/central/common/swagger2/Swagger2Configuration.java deleted file mode 100644 index 2903d4c4..00000000 --- a/zlt-commons/zlt-swagger2-spring-boot-starter/src/main/java/com/central/common/swagger2/Swagger2Configuration.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.central.common.swagger2; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Import; -import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration; - -/** - * - * @author zlt - * @date 2018/11/18 9:20 - */ -@ConditionalOnProperty(name = "zlt.swagger.enabled", matchIfMissing = true) -@Import({ - Swagger2DocumentationConfiguration.class -}) -public class Swagger2Configuration { -} diff --git a/zlt-commons/zlt-swagger2-spring-boot-starter/src/main/java/com/central/common/swagger2/SwaggerAutoConfiguration.java b/zlt-commons/zlt-swagger2-spring-boot-starter/src/main/java/com/central/common/swagger2/SwaggerAutoConfiguration.java index 09e20d36..0919f96d 100644 --- a/zlt-commons/zlt-swagger2-spring-boot-starter/src/main/java/com/central/common/swagger2/SwaggerAutoConfiguration.java +++ b/zlt-commons/zlt-swagger2-spring-boot-starter/src/main/java/com/central/common/swagger2/SwaggerAutoConfiguration.java @@ -1,16 +1,18 @@ package com.central.common.swagger2; +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Lists; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.ParameterBuilder; import springfox.documentation.builders.PathSelectors; @@ -25,6 +27,7 @@ import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.ArrayList; import java.util.LinkedList; @@ -36,10 +39,14 @@ /** * @author zlt * @date 2018/11/18 9:22 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ -@Import( { - Swagger2Configuration.class -}) +@Configuration +@EnableSwagger2 +@EnableKnife4j +@Import(BeanValidatorPluginsConfiguration.class) public class SwaggerAutoConfiguration implements BeanFactoryAware { private static final String AUTH_KEY = "Authorization"; diff --git a/zlt-commons/zlt-zookeeper-spring-boot-starter/pom.xml b/zlt-commons/zlt-zookeeper-spring-boot-starter/pom.xml new file mode 100644 index 00000000..776ac56a --- /dev/null +++ b/zlt-commons/zlt-zookeeper-spring-boot-starter/pom.xml @@ -0,0 +1,31 @@ + + + + com.zlt + zlt-commons + 5.4.0 + + 4.0.0 + jar + zlt-zookeeper-spring-boot-starter + zookeeper通用组件 + + + com.zlt + zlt-common-core + + + + org.apache.curator + curator-recipes + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + diff --git a/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/ZookeeperAutoConfiguration.java b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/ZookeeperAutoConfiguration.java new file mode 100644 index 00000000..9744726a --- /dev/null +++ b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/ZookeeperAutoConfiguration.java @@ -0,0 +1,40 @@ +package com.central.common.zookeeper; + +import com.central.common.zookeeper.properties.ZookeeperProperty; +import org.apache.curator.RetryPolicy; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; + +/** + * zookeeper 配置类 + * + * @author zlt + * @version 1.0 + * @date 2021/4/3 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@EnableConfigurationProperties(ZookeeperProperty.class) +@ComponentScan +public class ZookeeperAutoConfiguration { + /** + * 初始化连接 + */ + @Bean(initMethod = "start", destroyMethod = "close") + @ConditionalOnMissingBean + public CuratorFramework curatorFramework(ZookeeperProperty property) { + RetryPolicy retryPolicy = new ExponentialBackoffRetry(property.getBaseSleepTime(), property.getMaxRetries()); + return CuratorFrameworkFactory.builder() + .connectString(property.getConnectString()) + .connectionTimeoutMs(property.getConnectionTimeout()) + .sessionTimeoutMs(property.getSessionTimeout()) + .retryPolicy(retryPolicy) + .build(); + } +} diff --git a/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/lock/ZookeeperDistributedLock.java b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/lock/ZookeeperDistributedLock.java new file mode 100644 index 00000000..28d24808 --- /dev/null +++ b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/lock/ZookeeperDistributedLock.java @@ -0,0 +1,71 @@ +package com.central.common.zookeeper.lock; + +import com.central.common.constant.CommonConstant; +import com.central.common.exception.LockException; +import com.central.common.lock.DistributedLock; +import com.central.common.lock.ZLock; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.recipes.locks.InterProcessMutex; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.concurrent.TimeUnit; + +/** + * zookeeper分布式锁实现 + * + * @author zlt + * @version 1.0 + * @date 2021/4/3 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Component +@ConditionalOnProperty(prefix = "zlt.lock", name = "lockerType", havingValue = "ZK") +public class ZookeeperDistributedLock implements DistributedLock { + @Resource + private CuratorFramework client; + + private ZLock getLock(String key) { + InterProcessMutex lock = new InterProcessMutex(client, getPath(key)); + return new ZLock(lock, this); + } + + @Override + public ZLock lock(String key, long leaseTime, TimeUnit unit, boolean isFair) throws Exception { + ZLock zLock = this.getLock(key); + InterProcessMutex ipm = (InterProcessMutex)zLock.getLock(); + ipm.acquire(); + return zLock; + } + + @Override + public ZLock tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, boolean isFair) throws Exception { + ZLock zLock = this.getLock(key); + InterProcessMutex ipm = (InterProcessMutex)zLock.getLock(); + if (ipm.acquire(waitTime, unit)) { + return zLock; + } + return null; + } + + @Override + public void unlock(Object lock) throws Exception { + if (lock != null) { + if (lock instanceof InterProcessMutex) { + InterProcessMutex ipm = (InterProcessMutex)lock; + if (ipm.isAcquiredInThisProcess()) { + ipm.release(); + } + } else { + throw new LockException("requires InterProcessMutex type"); + } + } + } + + private String getPath(String key) { + return CommonConstant.PATH_SPLIT + CommonConstant.LOCK_KEY_PREFIX + CommonConstant.PATH_SPLIT + key; + } +} diff --git a/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/properties/ZookeeperProperty.java b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/properties/ZookeeperProperty.java new file mode 100644 index 00000000..7ed7d167 --- /dev/null +++ b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/properties/ZookeeperProperty.java @@ -0,0 +1,45 @@ +package com.central.common.zookeeper.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * zookeeper配置 + * + * @author zlt + * @version 1.0 + * @date 2021/4/3 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +@Getter +@ConfigurationProperties(prefix = "zlt.zookeeper") +public class ZookeeperProperty { + /** + * zk连接集群,多个用逗号隔开 + */ + private String connectString; + + /** + * 会话超时时间(毫秒) + */ + private int sessionTimeout = 15000; + + /** + * 连接超时时间(毫秒) + */ + private int connectionTimeout = 15000; + + /** + * 初始重试等待时间(毫秒) + */ + private int baseSleepTime = 2000; + + /** + * 重试最大次数 + */ + private int maxRetries = 10; +} diff --git a/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/template/ZookeeperTemplate.java b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/template/ZookeeperTemplate.java new file mode 100644 index 00000000..397e5323 --- /dev/null +++ b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/java/com/central/common/zookeeper/template/ZookeeperTemplate.java @@ -0,0 +1,231 @@ +package com.central.common.zookeeper.template; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.central.common.constant.CommonConstant; +import lombok.SneakyThrows; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.recipes.cache.*; +import org.apache.zookeeper.CreateMode; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import java.util.List; + +/** + * zookeeper模板类 + * + * @author zlt + * @version 1.0 + * @date 2021/4/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Component +public class ZookeeperTemplate { + private final CuratorFramework client; + + public ZookeeperTemplate(CuratorFramework client) { + this.client = client; + } + + /** + * 创建空节点,默认持久节点 + * + * @param path 节点路径 + * @param node 节点名称 + * @return 完整路径 + */ + @SneakyThrows + public String createNode(String path, String node) { + return createNode(path, node, CreateMode.PERSISTENT); + } + + /** + * 创建带类型的空节点 + * @param path 节点路径 + * @param node 节点名称 + * @param createMode 类型 + * @return 完整路径 + */ + @SneakyThrows + public String createNode(String path, String node, CreateMode createMode) { + path = buildPath(path, node); + client.create() + .orSetData() + .creatingParentsIfNeeded() + .withMode(createMode) + .forPath(path); + return path; + } + + + /** + * 创建节点,默认持久节点 + * @param path 节点路径 + * @param node 节点名称 + * @param value 节点值 + * @return 完整路径 + */ + @SneakyThrows + public String createNode(String path, String node, String value) { + return createNode(path, node, value, CreateMode.PERSISTENT); + } + + /** + * 创建节点,默认持久节点 + * @param path 节点路径 + * @param node 节点名称 + * @param value 节点值 + * @param createMode 节点类型 + * @return 完整路径 + */ + @SneakyThrows + public String createNode(String path, String node, String value, CreateMode createMode) { + Assert.isTrue(StrUtil.isNotEmpty(value), "zookeeper节点值不能为空!"); + + path = buildPath(path, node); + client.create() + .orSetData() + .creatingParentsIfNeeded() + .withMode(createMode) + .forPath(path, value.getBytes()); + return path; + } + + /** + * 获取节点数据 + * @param path 路径 + * @param node 节点名称 + * @return 节点值 + */ + @SneakyThrows + public String get(String path, String node) { + path = buildPath(path, node); + byte[] bytes = client.getData().forPath(path); + if (bytes.length > 0) { + return new String(bytes); + } + return null; + } + + /** + * 更新节点数据 + * @param path 节点路径 + * @param node 节点名称 + * @param value 更新值 + * @return 完整路径 + */ + @SneakyThrows + public String update(String path, String node, String value) { + Assert.isTrue(StrUtil.isNotEmpty(value), "zookeeper节点值不能为空!"); + + path = buildPath(path, node); + client.setData().forPath(path, value.getBytes()); + return path; + } + + /** + * 删除节点,并且递归删除子节点 + * @param path 路径 + * @param node 节点名称 + */ + @SneakyThrows + public void delete(String path, String node) { + path = buildPath(path, node); + client.delete().quietly().deletingChildrenIfNeeded().forPath(path); + } + + /** + * 获取子节点 + * @param path 节点路径 + * @return 子节点集合 + */ + @SneakyThrows + public List getChildren(String path) { + if(StrUtil.isEmpty(path)) { + return null; + } + + if (!path.startsWith(CommonConstant.PATH_SPLIT)) { + path = CommonConstant.PATH_SPLIT + path; + } + return client.getChildren().forPath(path); + } + + /** + * 判断节点是否存在 + * @param path 路径 + * @param node 节点名称 + * @return 结果 + */ + public boolean exists(String path, String node) { + List list = getChildren(path); + return CollUtil.isNotEmpty(list) && list.contains(node); + } + + /** + * 对一个节点进行监听,监听事件包括指定的路径节点的增、删、改的操作 + * @param path 节点路径 + * @param listener 回调方法 + */ + public void watchNode(String path, NodeCacheListener listener) { + CuratorCacheListener curatorCacheListener = CuratorCacheListener.builder() + .forNodeCache(listener) + .build(); + CuratorCache curatorCache = CuratorCache.builder(client, path).build(); + curatorCache.listenable().addListener(curatorCacheListener); + curatorCache.start(); + } + + + /** + * 对指定的路径节点的一级子目录进行监听,不对该节点的操作进行监听,对其子目录的节点进行增、删、改的操作监听 + * @param path 节点路径 + * @param listener 回调方法 + */ + public void watchChildren(String path, PathChildrenCacheListener listener) { + CuratorCacheListener curatorCacheListener = CuratorCacheListener.builder() + .forPathChildrenCache(path, client, listener) + .build(); + CuratorCache curatorCache = CuratorCache.builder(client, path).build(); + curatorCache.listenable().addListener(curatorCacheListener); + curatorCache.start(); + } + + /** + * 将指定的路径节点作为根节点(祖先节点),对其所有的子节点操作进行监听,呈现树形目录的监听 + * @param path 节点路径 + * @param maxDepth 回调方法 + * @param listener 监听 + */ + public void watchTree(String path, int maxDepth, TreeCacheListener listener) { + CuratorCacheListener curatorCacheListener = CuratorCacheListener.builder() + .forTreeCache(client, listener) + .build(); + CuratorCache curatorCache = CuratorCache.builder(client, path).build(); + curatorCache.listenable().addListener(curatorCacheListener); + curatorCache.start(); + } + + /** + * 转换路径 + * @param path 路径 + * @param node 节点名 + */ + private String buildPath(String path, String node) { + Assert.isTrue(StrUtil.isNotEmpty(path) && StrUtil.isNotEmpty(node) + , "zookeeper路径或者节点名称不能为空!"); + + if (!path.startsWith(CommonConstant.PATH_SPLIT)) { + path = CommonConstant.PATH_SPLIT + path; + } + + if (CommonConstant.PATH_SPLIT.equals(path)) { + return path + node; + } else { + return path + CommonConstant.PATH_SPLIT + node; + } + } +} diff --git a/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..96f338c0 --- /dev/null +++ b/zlt-commons/zlt-zookeeper-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.central.common.zookeeper.ZookeeperAutoConfiguration \ No newline at end of file diff --git a/zlt-config/pom.xml b/zlt-config/pom.xml index 13e37947..f6f7c460 100644 --- a/zlt-config/pom.xml +++ b/zlt-config/pom.xml @@ -5,7 +5,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 4.0.0 jar diff --git a/zlt-config/src/main/resources/application-dev.properties b/zlt-config/src/main/resources/application-dev.properties index a8c21924..43d5ef6f 100644 --- a/zlt-config/src/main/resources/application-dev.properties +++ b/zlt-config/src/main/resources/application-dev.properties @@ -21,6 +21,12 @@ zlt.sentinel.dashboard=192.168.28.130:6999 zlt.fdfs.web-url=192.168.28.130 zlt.fdfs.trackerList=${zlt.fdfs.web-url}:22122 +##### s3配置 +zlt.s3.access-key=minioadmin +zlt.s3.accessKeySecret=minioadmin +zlt.s3.endpoint=http://192.168.28.130:9000 +zlt.s3.bucketName=test + ##### 日志链路追踪 zlt.trace.enable=true @@ -28,4 +34,7 @@ zlt.trace.enable=true zlt.ribbon.isolation.enabled=false ##### mybatis-plus打印完整sql(只适用于开发环境) -mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl \ No newline at end of file +mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl + +# 开启版本路由 +#zlt.loadbalance.isolation.enabled=true \ No newline at end of file diff --git a/zlt-config/src/main/resources/application-prod.properties b/zlt-config/src/main/resources/application-prod.properties index 933e9b66..5fe2ca3d 100644 --- a/zlt-config/src/main/resources/application-prod.properties +++ b/zlt-config/src/main/resources/application-prod.properties @@ -21,5 +21,11 @@ zlt.sentinel.dashboard=192.168.28.130:6999 zlt.fdfs.web-url=192.168.28.130 zlt.fdfs.trackerList=${zlt.fdfs.web-url}:22122 +##### s3配置 +zlt.s3.access-key=minioadmin +zlt.s3.accessKeySecret=minioadmin +zlt.s3.endpoint=http://192.168.28.130:9000 +zlt.s3.bucketName=test + ##### 日志链路追踪 zlt.trace.enable=true \ No newline at end of file diff --git a/zlt-config/src/main/resources/application-test.properties b/zlt-config/src/main/resources/application-test.properties index b46b08ee..ddf04395 100644 --- a/zlt-config/src/main/resources/application-test.properties +++ b/zlt-config/src/main/resources/application-test.properties @@ -21,5 +21,11 @@ zlt.sentinel.dashboard=192.168.28.130:6999 zlt.fdfs.web-url=192.168.28.130 zlt.fdfs.trackerList=${zlt.fdfs.web-url}:22122 +##### s3配置 +zlt.s3.access-key=minioadmin +zlt.s3.accessKeySecret=minioadmin +zlt.s3.endpoint=http://192.168.28.130:9000 +zlt.s3.bucketName=test + ##### 日志链路追踪 zlt.trace.enable=true \ No newline at end of file diff --git a/zlt-config/src/main/resources/application.properties b/zlt-config/src/main/resources/application.properties index 87d5e328..99f7c0c0 100644 --- a/zlt-config/src/main/resources/application.properties +++ b/zlt-config/src/main/resources/application.properties @@ -1,18 +1,7 @@ ########################## application级别通用配置 ########################## -##### ribbon配置 -## 从注册中心刷新servelist的时间 默认30秒,单位ms -ribbon.ServerListRefreshInterval=15000 -## 请求连接的超时时间 默认1秒,单位ms -ribbon.ConnectTimeout=30000 -## 请求处理的超时时间 默认1秒,单位ms -ribbon.ReadTimeout=30000 -## 对所有操作请求都进行重试,不配置这个MaxAutoRetries不起作用 默认false -#ribbon.OkToRetryOnAllOperations=true -## 对当前实例的重试次数 默认0 -#ribbon.MaxAutoRetries=1 -## 切换实例的重试次数 默认1 -ribbon.MaxAutoRetriesNextServer=0 - +##### loadbalancer配置 +## 切换实例的重试 +spring.cloud.loadbalancer.retry.enabled=false ##### feign配置 feign.sentinel.enabled=true @@ -94,4 +83,7 @@ spring.redis.lettuce.pool.max-wait=-1 # 连接池中的最大空闲连接 spring.redis.lettuce.pool.max-idle=8 # 连接池中的最小空闲连接 -spring.redis.lettuce.pool.min-idle=0 \ No newline at end of file +spring.redis.lettuce.pool.min-idle=0 + +##### logging配置 +logging.level.com.alibaba.nacos.client.naming=warn \ No newline at end of file diff --git a/zlt-config/src/main/resources/bootstrap.properties b/zlt-config/src/main/resources/bootstrap.properties index c0baec4b..639ab88d 100644 --- a/zlt-config/src/main/resources/bootstrap.properties +++ b/zlt-config/src/main/resources/bootstrap.properties @@ -4,9 +4,11 @@ spring.profiles.active=dev ##### nacos(注册中心和配置中心)地址 spring.cloud.nacos.server-addr=192.168.28.130:8848 +#spring.cloud.nacos.username=nacos +#spring.cloud.nacos.password=nacos spring.cloud.nacos.config.file-extension=yml -spring.cloud.nacos.config.shared-dataids=common.yml -spring.cloud.nacos.config.refreshable-dataids=common.yml +spring.cloud.nacos.config.shared-configs[0].data-id=common.yml +spring.cloud.nacos.config.shared-configs[0].refresh=true ##### spring-boot-actuator配置 management.endpoints.web.exposure.include=* diff --git a/zlt-demo/dubbo-demo/README.md b/zlt-demo/dubbo-demo/README.md new file mode 100644 index 00000000..824c4bfa --- /dev/null +++ b/zlt-demo/dubbo-demo/README.md @@ -0,0 +1,12 @@ +## Demo说明 + +Spring Cloud 集成 Dubbo 的 demo + +启动后访问:http://127.0.0.1:8091/test/abc + + + +## 集成 Spring Cloud Gateway +[Dubbo想要个网关怎么办?试试整合Spring Cloud Gateway](https://mp.weixin.qq.com/s/_idYl39i1eQejLLLlCW2sg) + + diff --git a/zlt-demo/dubbo-demo/pom.xml b/zlt-demo/dubbo-demo/pom.xml new file mode 100644 index 00000000..3f5aa128 --- /dev/null +++ b/zlt-demo/dubbo-demo/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + com.zlt + zlt-demo + 5.4.0 + + dubbo-demo + + + + org.springframework.boot + spring-boot-starter-web + + + + com.zlt + zlt-log-spring-boot-starter + + + + com.alibaba.cloud + spring-cloud-starter-dubbo + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + \ No newline at end of file diff --git a/zlt-demo/dubbo-demo/src/main/java/org/zlt/WebDubboApp.java b/zlt-demo/dubbo-demo/src/main/java/org/zlt/WebDubboApp.java new file mode 100644 index 00000000..059c4d5c --- /dev/null +++ b/zlt-demo/dubbo-demo/src/main/java/org/zlt/WebDubboApp.java @@ -0,0 +1,20 @@ +package org.zlt; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +/** + * @author zlt + * @date 2020/1/30 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@EnableDiscoveryClient +@SpringBootApplication +public class WebDubboApp { + public static void main(String[] args) { + SpringApplication.run(WebDubboApp.class, args); + } +} diff --git a/zlt-demo/dubbo-demo/src/main/java/org/zlt/controller/WebController.java b/zlt-demo/dubbo-demo/src/main/java/org/zlt/controller/WebController.java new file mode 100644 index 00000000..0a2e9a84 --- /dev/null +++ b/zlt-demo/dubbo-demo/src/main/java/org/zlt/controller/WebController.java @@ -0,0 +1,28 @@ +package org.zlt.controller; + +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboReference; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.zlt.service.RpcService; + +/** + * @author zlt + * @date 2020/6/26 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@RestController +public class WebController { + @DubboReference + private RpcService dubboService; + + @GetMapping("/test/{p}") + public String test(@PathVariable("p") String param) { + log.info("==============WebController"); + return dubboService.test(param); + } +} diff --git a/zlt-demo/dubbo-demo/src/main/java/org/zlt/service/RpcService.java b/zlt-demo/dubbo-demo/src/main/java/org/zlt/service/RpcService.java new file mode 100644 index 00000000..00e6fc3c --- /dev/null +++ b/zlt-demo/dubbo-demo/src/main/java/org/zlt/service/RpcService.java @@ -0,0 +1,12 @@ +package org.zlt.service; + +/** + * @author zlt + * @date 2020/1/30 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public interface RpcService { + String test(String param); +} diff --git a/zlt-demo/dubbo-demo/src/main/java/org/zlt/service/impl/RpcServiceImpl.java b/zlt-demo/dubbo-demo/src/main/java/org/zlt/service/impl/RpcServiceImpl.java new file mode 100644 index 00000000..cfabb23e --- /dev/null +++ b/zlt-demo/dubbo-demo/src/main/java/org/zlt/service/impl/RpcServiceImpl.java @@ -0,0 +1,22 @@ +package org.zlt.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboService; +import org.zlt.service.RpcService; + +/** + * @author zlt + * @date 2020/1/30 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@DubboService(protocol = "dubbo") +public class RpcServiceImpl implements RpcService { + @Override + public String test(String param) { + log.info("==============RpcServiceImpl"); + return "dubbo service: " + param; + } +} diff --git a/zlt-demo/dubbo-demo/src/main/resources/application.yml b/zlt-demo/dubbo-demo/src/main/resources/application.yml new file mode 100644 index 00000000..5cfc3893 --- /dev/null +++ b/zlt-demo/dubbo-demo/src/main/resources/application.yml @@ -0,0 +1,19 @@ +dubbo: + scan: + base-packages: org.zlt.service + protocols: + dubbo: + name: dubbo + port: -1 + registry: + address: spring-cloud://localhost + consumer: + timeout: 5000 + check: false + retries: 0 + cloud: + subscribed-services: + +zlt: + trace: + enable: true \ No newline at end of file diff --git a/zlt-demo/dubbo-demo/src/main/resources/bootstrap.yml b/zlt-demo/dubbo-demo/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..6484c93d --- /dev/null +++ b/zlt-demo/dubbo-demo/src/main/resources/bootstrap.yml @@ -0,0 +1,11 @@ +server: + port: 8091 + +spring: + application: + name: zlt-web-dubbo + main: + allow-bean-definition-overriding: true + cloud: + nacos: + server-addr: 192.168.28.130:8848 \ No newline at end of file diff --git a/zlt-demo/pom.xml b/zlt-demo/pom.xml index 7bc9c0c4..26595a2b 100644 --- a/zlt-demo/pom.xml +++ b/zlt-demo/pom.xml @@ -4,7 +4,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-demo pom @@ -20,5 +20,11 @@ seata-demo sso-demo + + dubbo-demo + + websocket-demo + + resources-server-demo \ No newline at end of file diff --git a/zlt-demo/resources-server-demo/README.md b/zlt-demo/resources-server-demo/README.md new file mode 100644 index 00000000..3abae7a9 --- /dev/null +++ b/zlt-demo/resources-server-demo/README.md @@ -0,0 +1,45 @@ +## 一、说明 +资源服务器 demo 样例,以最简化的代码演示如何快速集成一个带鉴权功能的服务,适用于 `无网络隔离` 架构。 + +> 关于无网络隔离架构的设计可参考文档:[无网络隔离架构](https://www.kancloud.cn/zlt2000/microservices-platform/1153640) + +  +## 二、启动以下服务 +1. zlt-uaa:统一认证中心 +2. user-center:用户服务 +3. sc-gateway:api网关 +4. resources-server-demo + +> 环境配置与启动参考文档:https://www.kancloud.cn/zlt2000/microservices-platform/919418 + +  +## 三、测试 +### 3.1. 测试接口一 +http://localhost:8093/test/notAuth +> 无需token访问 + +  +### 3.2. 测试接口二 +通过 @LoginUser 获取当前登录人 + +http://localhost:8093/test/auth?access_token=xxx +> - xxx 需替换为正确的 access_token +> - 可以通过修改 `bootstrap.yml` 文件中的 `zlt.security.ignore.httpUrls` 参数添加排除校验的url。 + +  +### 3.3. 测试接口三 +通过 LoginUserContextHolder 获取当前登录人 + +http://localhost:8093/test/auth2?access_token=xxx +> - xxx 需替换为正确的 access_token + +  +## 四、获取access_token +可使用任意授权模式获取; + +例如:密码模式授权 +- 请求方式:POST +- 请求头:Authorization:Basic d2ViQXBwOndlYkFwcA== +- 请求地址:http://localhost:9900/api-uaa/oauth/token?grant_type=password&username=admin&password=admin + +> 授权接口清单参考文档:https://www.kancloud.cn/zlt2000/microservices-platform/1158135 diff --git a/zlt-demo/resources-server-demo/pom.xml b/zlt-demo/resources-server-demo/pom.xml new file mode 100644 index 00000000..e8f420de --- /dev/null +++ b/zlt-demo/resources-server-demo/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + com.zlt + zlt-demo + 5.4.0 + + resources-server-demo + + + + com.zlt + zlt-config + + + com.zlt + zlt-common-spring-boot-starter + + + com.zlt + zlt-auth-client-spring-boot-starter + + + com.zlt + zlt-redis-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + com.baomidou + mybatis-plus-extension + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + \ No newline at end of file diff --git a/zlt-demo/resources-server-demo/src/main/java/org/zlt/ResourcesServerApp.java b/zlt-demo/resources-server-demo/src/main/java/org/zlt/ResourcesServerApp.java new file mode 100644 index 00000000..58306000 --- /dev/null +++ b/zlt-demo/resources-server-demo/src/main/java/org/zlt/ResourcesServerApp.java @@ -0,0 +1,18 @@ +package org.zlt; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author zlt + * @date 2022/6/25 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@SpringBootApplication +public class ResourcesServerApp { + public static void main(String[] args) { + SpringApplication.run(ResourcesServerApp.class, args); + } +} diff --git a/zlt-demo/resources-server-demo/src/main/java/org/zlt/config/MyResourceConfig.java b/zlt-demo/resources-server-demo/src/main/java/org/zlt/config/MyResourceConfig.java new file mode 100644 index 00000000..06582143 --- /dev/null +++ b/zlt-demo/resources-server-demo/src/main/java/org/zlt/config/MyResourceConfig.java @@ -0,0 +1,20 @@ +package org.zlt.config; + +import com.central.oauth2.common.config.DefaultResourceServerConf; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; + +/** + * security资源服务器配置 + * + * @author zlt + * @version 1.0 + * @date 2022/6/25 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Configuration +@EnableResourceServer +public class MyResourceConfig extends DefaultResourceServerConf { +} diff --git a/zlt-demo/resources-server-demo/src/main/java/org/zlt/config/WebMvcConfig.java b/zlt-demo/resources-server-demo/src/main/java/org/zlt/config/WebMvcConfig.java new file mode 100644 index 00000000..f2d06702 --- /dev/null +++ b/zlt-demo/resources-server-demo/src/main/java/org/zlt/config/WebMvcConfig.java @@ -0,0 +1,18 @@ +package org.zlt.config; + +import com.central.common.config.DefaultWebMvcConfig; +import org.springframework.context.annotation.Configuration; + +/** + * web服务器配置 + * + * @author zlt + * @version 1.0 + * @date 2022/6/25 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Configuration +public class WebMvcConfig extends DefaultWebMvcConfig { +} diff --git a/zlt-demo/resources-server-demo/src/main/java/org/zlt/controller/TestController.java b/zlt-demo/resources-server-demo/src/main/java/org/zlt/controller/TestController.java new file mode 100644 index 00000000..5ea4eb6f --- /dev/null +++ b/zlt-demo/resources-server-demo/src/main/java/org/zlt/controller/TestController.java @@ -0,0 +1,40 @@ +package org.zlt.controller; + +import com.central.common.annotation.LoginUser; +import com.central.common.context.LoginUserContextHolder; +import com.central.common.model.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author zlt + * @date 2022/6/25 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@RestController +public class TestController { + /** + * 通过 @LoginUser 获取当前登录人 + */ + @GetMapping("/test/auth") + public String auth(@LoginUser SysUser user) { + return "auth:" + user.getUsername(); + } + + /** + * 通过 LoginUserContextHolder 获取当前登录人 + */ + @GetMapping("/test/auth2") + public String auth() { + return "auth2:" + LoginUserContextHolder.getUser().getUsername(); + } + + @GetMapping("/test/notAuth") + public String notAuth() { + return "notAuth:ok"; + } +} diff --git a/zlt-demo/resources-server-demo/src/main/resources/bootstrap.yml b/zlt-demo/resources-server-demo/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..cd2bc3d3 --- /dev/null +++ b/zlt-demo/resources-server-demo/src/main/resources/bootstrap.yml @@ -0,0 +1,14 @@ +server: + port: 8093 + +spring: + application: + name: zlt-resources-server + main: + allow-bean-definition-overriding: true + +zlt: + security: + ignore: + httpUrls: > + /test/notAuth \ No newline at end of file diff --git a/zlt-demo/rocketmq-demo/pom.xml b/zlt-demo/rocketmq-demo/pom.xml index 97fbe2bb..91a15231 100644 --- a/zlt-demo/rocketmq-demo/pom.xml +++ b/zlt-demo/rocketmq-demo/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-demo - 3.5.0 + 5.4.0 rocketmq-demo pom diff --git a/zlt-demo/rocketmq-demo/rocketmq-consume/pom.xml b/zlt-demo/rocketmq-demo/rocketmq-consume/pom.xml index e3983aea..afddb73e 100644 --- a/zlt-demo/rocketmq-demo/rocketmq-consume/pom.xml +++ b/zlt-demo/rocketmq-demo/rocketmq-consume/pom.xml @@ -4,7 +4,7 @@ com.zlt rocketmq-demo - 3.5.0 + 5.4.0 rocketmq-consume rocketMQ消费者demo diff --git a/zlt-demo/rocketmq-demo/rocketmq-produce/pom.xml b/zlt-demo/rocketmq-demo/rocketmq-produce/pom.xml index bd7de467..2bccffc4 100644 --- a/zlt-demo/rocketmq-demo/rocketmq-produce/pom.xml +++ b/zlt-demo/rocketmq-demo/rocketmq-produce/pom.xml @@ -4,7 +4,7 @@ com.zlt rocketmq-demo - 3.5.0 + 5.4.0 rocketmq-produce rocketMQ生产者demo diff --git a/zlt-demo/rocketmq-demo/rocketmq-transactional/pom.xml b/zlt-demo/rocketmq-demo/rocketmq-transactional/pom.xml index 9227a608..2fd9aa7a 100644 --- a/zlt-demo/rocketmq-demo/rocketmq-transactional/pom.xml +++ b/zlt-demo/rocketmq-demo/rocketmq-transactional/pom.xml @@ -4,7 +4,7 @@ com.zlt rocketmq-demo - 3.5.0 + 5.4.0 rocketmq-transactional rocketMQ事务消息demo diff --git a/zlt-demo/rocketmq-demo/rocketmq-transactional/src/main/java/com/rocketmq/demo/listener/OrderTransactionListenerImpl.java b/zlt-demo/rocketmq-demo/rocketmq-transactional/src/main/java/com/rocketmq/demo/listener/OrderTransactionListenerImpl.java index c7ec3172..103551bc 100644 --- a/zlt-demo/rocketmq-demo/rocketmq-transactional/src/main/java/com/rocketmq/demo/listener/OrderTransactionListenerImpl.java +++ b/zlt-demo/rocketmq-demo/rocketmq-transactional/src/main/java/com/rocketmq/demo/listener/OrderTransactionListenerImpl.java @@ -1,6 +1,6 @@ package com.rocketmq.demo.listener; -import com.alibaba.fastjson.JSON; +import com.central.common.utils.JsonUtil; import com.rocketmq.demo.model.Order; import com.rocketmq.demo.service.IOrderService; import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; @@ -24,7 +24,7 @@ public class OrderTransactionListenerImpl implements RocketMQLocalTransactionLis public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object arg) { //插入订单数据 String orderJson = new String(((byte[])message.getPayload())); - Order order = JSON.parseObject(orderJson, Order.class); + Order order = JsonUtil.toObject(orderJson, Order.class); orderService.save(order); String produceError = (String)message.getHeaders().get("produceError"); diff --git a/zlt-demo/seata-demo/README.md b/zlt-demo/seata-demo/README.md index 34205968..9c596fb7 100644 --- a/zlt-demo/seata-demo/README.md +++ b/zlt-demo/seata-demo/README.md @@ -1,11 +1,70 @@ ## **详细的原理和部署细节请查看** -[Spring Cloud同步场景分布式事务怎样做?试试Seata](https://mp.weixin.qq.com/s/0yCmHzlXDC9BkbUuEt0_fQ) +[SEATA同步场景事务(v1.3)](https://www.kancloud.cn/zlt2000/microservices-platform/1280566) +  +## 测试环境 +* mysql 5.7 +* seata 1.3 +* nacos 1.3 +> **注意**:如果nacos使用低于1.3的版本不需要配置username和password;如果使用1.3以上版本必需开启 `nacos.core.auth.enabled=true` 并且配置username和password,否则读取不到seata-server +  +## 配置中心的配置如下 +**config.txt** +```properties +service.vgroupMapping.test_tx_service_group=default +store.mode=db +store.db.datasource=druid +store.db.dbType=mysql +store.db.url=jdbc:mysql://192.168.28.130:3306/seata?useUnicode=true +store.db.driverClassName=com.mysql.jdbc.Driver +store.db.user=root +store.db.password=root +store.db.minConn=5 +store.db.maxConn=30 +store.db.globalTable=global_table +store.db.branchTable=branch_table +store.db.queryLimit=100 +store.db.lockTable=lock_table +store.db.maxWait=5000 +``` +>根据自己的环境修改 url、user、password 配置值 -## 说明 +  +## seata的配置如下 +**registry.conf** +```json +registry { + # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa + type = "nacos" + nacos { + application = "seata-server" + serverAddr = "192.168.28.130:8848" + group = "SEATA_GROUP" + namespace = "" + cluster = "default" + username = "nacos" + password = "nacos" + } +} +config { + # file、nacos 、apollo、zk、consul、etcd3 + type = "nacos" + + nacos { + serverAddr = "192.168.28.130:8848" + namespace = "" + group = "SEATA_GROUP" + username = "nacos" + password = "nacos" + } +} +``` + +  +## 说明 **包括以下5个模块,分别是** * `business-service`:业务服务 diff --git a/zlt-demo/seata-demo/account-service/pom.xml b/zlt-demo/seata-demo/account-service/pom.xml index f9c9681b..5daa83ea 100644 --- a/zlt-demo/seata-demo/account-service/pom.xml +++ b/zlt-demo/seata-demo/account-service/pom.xml @@ -5,7 +5,7 @@ com.zlt seata-demo - 3.5.0 + 5.4.0 account-service diff --git a/zlt-demo/seata-demo/account-service/src/main/resources/application.yml b/zlt-demo/seata-demo/account-service/src/main/resources/application.yml index 9e1eaef2..1f7f5a96 100644 --- a/zlt-demo/seata-demo/account-service/src/main/resources/application.yml +++ b/zlt-demo/seata-demo/account-service/src/main/resources/application.yml @@ -4,8 +4,20 @@ spring: username: root password: 1q2w3e4r driver-class-name: com.mysql.cj.jdbc.Driver - cloud: - alibaba: - seata: - # seata 服务分组,要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应,默认是${spring.application.name}-fescar-service-group - tx-service-group: account-service-group \ No newline at end of file + +seata: + tx-service-group: test_tx_service_group + config: + type: nacos + nacos: + serverAddr: ${spring.cloud.nacos.server-addr} + group: SEATA_GROUP + username: nacos + password: nacos + registry: + type: nacos + nacos: + server-addr: ${spring.cloud.nacos.server-addr} + group: SEATA_GROUP + username: nacos + password: nacos \ No newline at end of file diff --git a/zlt-demo/seata-demo/account-service/src/main/resources/bootstrap.yml b/zlt-demo/seata-demo/account-service/src/main/resources/bootstrap.yml index fce0c942..1c062281 100644 --- a/zlt-demo/seata-demo/account-service/src/main/resources/bootstrap.yml +++ b/zlt-demo/seata-demo/account-service/src/main/resources/bootstrap.yml @@ -4,7 +4,10 @@ server: spring: application: name: account-service + main: + allow-bean-definition-overriding: true cloud: nacos: - discovery: - server-addr: 192.168.28.130:8848 \ No newline at end of file + server-addr: 192.168.28.130:8848 + username: nacos + password: nacos \ No newline at end of file diff --git a/zlt-demo/seata-demo/account-service/src/main/resources/registry.conf b/zlt-demo/seata-demo/account-service/src/main/resources/registry.conf deleted file mode 100644 index 96c107da..00000000 --- a/zlt-demo/seata-demo/account-service/src/main/resources/registry.conf +++ /dev/null @@ -1,26 +0,0 @@ -registry { - # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa - type = "nacos" - - nacos { - serverAddr = "192.168.28.130" - namespace = "public" - cluster = "default" - } - file { - name = "file.conf" - } -} -config { - # file、nacos 、apollo、zk、consul、etcd3 - type = "nacos" - nacos { - serverAddr = "192.168.28.130" - namespace = "public" - cluster = "default" - } - file { - name = "file.conf" - } -} - diff --git a/zlt-demo/seata-demo/business-service/pom.xml b/zlt-demo/seata-demo/business-service/pom.xml index 10c61b9d..39aff9c1 100644 --- a/zlt-demo/seata-demo/business-service/pom.xml +++ b/zlt-demo/seata-demo/business-service/pom.xml @@ -5,14 +5,14 @@ com.zlt seata-demo - 3.5.0 + 5.4.0 business-service com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter org.springframework.boot diff --git a/zlt-demo/seata-demo/business-service/src/main/resources/application.yml b/zlt-demo/seata-demo/business-service/src/main/resources/application.yml index 6c197a08..8fbb3389 100644 --- a/zlt-demo/seata-demo/business-service/src/main/resources/application.yml +++ b/zlt-demo/seata-demo/business-service/src/main/resources/application.yml @@ -1,6 +1,16 @@ -spring: - cloud: - alibaba: - seata: - # seata 服务分组,要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应,默认是${spring.application.name}-fescar-service-group - tx-service-group: business-service-group \ No newline at end of file +seata: + tx-service-group: test_tx_service_group + config: + type: nacos + nacos: + serverAddr: ${spring.cloud.nacos.server-addr} + group: SEATA_GROUP + username: nacos + password: nacos + registry: + type: nacos + nacos: + server-addr: ${spring.cloud.nacos.server-addr} + group: SEATA_GROUP + username: nacos + password: nacos \ No newline at end of file diff --git a/zlt-demo/seata-demo/business-service/src/main/resources/bootstrap.yml b/zlt-demo/seata-demo/business-service/src/main/resources/bootstrap.yml index 8196da7a..722d9145 100644 --- a/zlt-demo/seata-demo/business-service/src/main/resources/bootstrap.yml +++ b/zlt-demo/seata-demo/business-service/src/main/resources/bootstrap.yml @@ -4,7 +4,15 @@ server: spring: application: name: business-service + main: + allow-bean-definition-overriding: true cloud: nacos: - discovery: - server-addr: 192.168.28.130:8848 \ No newline at end of file + server-addr: 192.168.28.130:8848 + username: nacos + password: nacos + +ribbon: + ConnectTimeout: 100000 + ReadTimeout: 1000000 + OkToRetryOnAllOperations: false \ No newline at end of file diff --git a/zlt-demo/seata-demo/business-service/src/main/resources/registry.conf b/zlt-demo/seata-demo/business-service/src/main/resources/registry.conf deleted file mode 100644 index 96c107da..00000000 --- a/zlt-demo/seata-demo/business-service/src/main/resources/registry.conf +++ /dev/null @@ -1,26 +0,0 @@ -registry { - # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa - type = "nacos" - - nacos { - serverAddr = "192.168.28.130" - namespace = "public" - cluster = "default" - } - file { - name = "file.conf" - } -} -config { - # file、nacos 、apollo、zk、consul、etcd3 - type = "nacos" - nacos { - serverAddr = "192.168.28.130" - namespace = "public" - cluster = "default" - } - file { - name = "file.conf" - } -} - diff --git a/zlt-demo/seata-demo/order-service/pom.xml b/zlt-demo/seata-demo/order-service/pom.xml index 57d829fc..da9d834b 100644 --- a/zlt-demo/seata-demo/order-service/pom.xml +++ b/zlt-demo/seata-demo/order-service/pom.xml @@ -5,7 +5,7 @@ com.zlt seata-demo - 3.5.0 + 5.4.0 order-service @@ -17,7 +17,7 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter org.springframework.boot diff --git a/zlt-demo/seata-demo/order-service/src/main/resources/application.yml b/zlt-demo/seata-demo/order-service/src/main/resources/application.yml index 857eb806..1f7f5a96 100644 --- a/zlt-demo/seata-demo/order-service/src/main/resources/application.yml +++ b/zlt-demo/seata-demo/order-service/src/main/resources/application.yml @@ -4,8 +4,20 @@ spring: username: root password: 1q2w3e4r driver-class-name: com.mysql.cj.jdbc.Driver - cloud: - alibaba: - seata: - # seata 服务分组,要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应,默认是${spring.application.name}-fescar-service-group - tx-service-group: order-service-group \ No newline at end of file + +seata: + tx-service-group: test_tx_service_group + config: + type: nacos + nacos: + serverAddr: ${spring.cloud.nacos.server-addr} + group: SEATA_GROUP + username: nacos + password: nacos + registry: + type: nacos + nacos: + server-addr: ${spring.cloud.nacos.server-addr} + group: SEATA_GROUP + username: nacos + password: nacos \ No newline at end of file diff --git a/zlt-demo/seata-demo/order-service/src/main/resources/bootstrap.yml b/zlt-demo/seata-demo/order-service/src/main/resources/bootstrap.yml index a4fe7107..13799e00 100644 --- a/zlt-demo/seata-demo/order-service/src/main/resources/bootstrap.yml +++ b/zlt-demo/seata-demo/order-service/src/main/resources/bootstrap.yml @@ -4,7 +4,10 @@ server: spring: application: name: order-service + main: + allow-bean-definition-overriding: true cloud: nacos: - discovery: - server-addr: 192.168.28.130:8848 \ No newline at end of file + server-addr: 192.168.28.130:8848 + username: nacos + password: nacos \ No newline at end of file diff --git a/zlt-demo/seata-demo/order-service/src/main/resources/registry.conf b/zlt-demo/seata-demo/order-service/src/main/resources/registry.conf deleted file mode 100644 index 96c107da..00000000 --- a/zlt-demo/seata-demo/order-service/src/main/resources/registry.conf +++ /dev/null @@ -1,26 +0,0 @@ -registry { - # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa - type = "nacos" - - nacos { - serverAddr = "192.168.28.130" - namespace = "public" - cluster = "default" - } - file { - name = "file.conf" - } -} -config { - # file、nacos 、apollo、zk、consul、etcd3 - type = "nacos" - nacos { - serverAddr = "192.168.28.130" - namespace = "public" - cluster = "default" - } - file { - name = "file.conf" - } -} - diff --git a/zlt-demo/seata-demo/pom.xml b/zlt-demo/seata-demo/pom.xml index aa5a8b31..dcf2c700 100644 --- a/zlt-demo/seata-demo/pom.xml +++ b/zlt-demo/seata-demo/pom.xml @@ -4,11 +4,22 @@ com.zlt zlt-demo - 3.5.0 + 5.4.0 seata-demo seata分布式事务demo pom + + + + + io.seata + seata-spring-boot-starter + 1.3.0 + + + + business-service diff --git a/zlt-demo/seata-demo/seata-common-starter/pom.xml b/zlt-demo/seata-demo/seata-common-starter/pom.xml index 7a8299b5..390556fa 100644 --- a/zlt-demo/seata-demo/seata-common-starter/pom.xml +++ b/zlt-demo/seata-demo/seata-common-starter/pom.xml @@ -5,7 +5,7 @@ com.zlt seata-demo - 3.5.0 + 5.4.0 seata-common-starter diff --git a/zlt-demo/seata-demo/seata-demo.sql b/zlt-demo/seata-demo/seata-demo.sql index 8e5d80ec..cd9719ec 100644 --- a/zlt-demo/seata-demo/seata-demo.sql +++ b/zlt-demo/seata-demo/seata-demo.sql @@ -8,7 +8,7 @@ USE `seata-demo`; -- you must to init this sql for you business databese. the seata server not need it. -- 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库) -- 注意此处0.3.0+ 增加唯一索引 ux_undo_log -drop table `undo_log`; +DROP TABLE IF EXISTS `undo_log`; CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, diff --git a/zlt-demo/seata-demo/storage-service/pom.xml b/zlt-demo/seata-demo/storage-service/pom.xml index dc4882df..03675083 100644 --- a/zlt-demo/seata-demo/storage-service/pom.xml +++ b/zlt-demo/seata-demo/storage-service/pom.xml @@ -5,7 +5,7 @@ com.zlt seata-demo - 3.5.0 + 5.4.0 storage-service @@ -17,7 +17,7 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter org.springframework.boot diff --git a/zlt-demo/seata-demo/storage-service/src/main/resources/application.yml b/zlt-demo/seata-demo/storage-service/src/main/resources/application.yml index ec287685..1f7f5a96 100644 --- a/zlt-demo/seata-demo/storage-service/src/main/resources/application.yml +++ b/zlt-demo/seata-demo/storage-service/src/main/resources/application.yml @@ -4,8 +4,20 @@ spring: username: root password: 1q2w3e4r driver-class-name: com.mysql.cj.jdbc.Driver - cloud: - alibaba: - seata: - # seata 服务分组,要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应,默认是${spring.application.name}-fescar-service-group - tx-service-group: storage-service-group \ No newline at end of file + +seata: + tx-service-group: test_tx_service_group + config: + type: nacos + nacos: + serverAddr: ${spring.cloud.nacos.server-addr} + group: SEATA_GROUP + username: nacos + password: nacos + registry: + type: nacos + nacos: + server-addr: ${spring.cloud.nacos.server-addr} + group: SEATA_GROUP + username: nacos + password: nacos \ No newline at end of file diff --git a/zlt-demo/seata-demo/storage-service/src/main/resources/bootstrap.yml b/zlt-demo/seata-demo/storage-service/src/main/resources/bootstrap.yml index 5de51a8c..d1db4cde 100644 --- a/zlt-demo/seata-demo/storage-service/src/main/resources/bootstrap.yml +++ b/zlt-demo/seata-demo/storage-service/src/main/resources/bootstrap.yml @@ -4,7 +4,10 @@ server: spring: application: name: storage-service + main: + allow-bean-definition-overriding: true cloud: nacos: - discovery: - server-addr: 192.168.28.130:8848 \ No newline at end of file + server-addr: 192.168.28.130:8848 + username: nacos + password: nacos \ No newline at end of file diff --git a/zlt-demo/seata-demo/storage-service/src/main/resources/registry.conf b/zlt-demo/seata-demo/storage-service/src/main/resources/registry.conf deleted file mode 100644 index 96c107da..00000000 --- a/zlt-demo/seata-demo/storage-service/src/main/resources/registry.conf +++ /dev/null @@ -1,26 +0,0 @@ -registry { - # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa - type = "nacos" - - nacos { - serverAddr = "192.168.28.130" - namespace = "public" - cluster = "default" - } - file { - name = "file.conf" - } -} -config { - # file、nacos 、apollo、zk、consul、etcd3 - type = "nacos" - nacos { - serverAddr = "192.168.28.130" - namespace = "public" - cluster = "default" - } - file { - name = "file.conf" - } -} - diff --git a/zlt-demo/sharding-jdbc-demo/pom.xml b/zlt-demo/sharding-jdbc-demo/pom.xml index 5898abfc..88cdc985 100644 --- a/zlt-demo/sharding-jdbc-demo/pom.xml +++ b/zlt-demo/sharding-jdbc-demo/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-demo - 3.5.0 + 5.4.0 sharding-jdbc-demo sharding-jdbc分库分表demo diff --git a/zlt-demo/sharding-jdbc-demo/src/main/resources/application.yml b/zlt-demo/sharding-jdbc-demo/src/main/resources/application.yml index 836cbddc..4701cd57 100644 --- a/zlt-demo/sharding-jdbc-demo/src/main/resources/application.yml +++ b/zlt-demo/sharding-jdbc-demo/src/main/resources/application.yml @@ -6,10 +6,12 @@ spring: active: sharding-databases application: name: sharding-jdbc-demo + main: + allow-bean-definition-overriding: true zlt: datasource: - ip: 192.168.28.131 + ip: 192.168.28.130 username: root password: 1q2w3e4r diff --git a/zlt-demo/sso-demo/README.md b/zlt-demo/sso-demo/README.md index bc41b6ec..05d8ba7d 100644 --- a/zlt-demo/sso-demo/README.md +++ b/zlt-demo/sso-demo/README.md @@ -1,4 +1,4 @@ * **ss-sso**:使用springSecurity来实现自动单点登录,非前后端分离 - -* **web-sso**:前后端分离的单点登录 +* **web-sso**:前后端分离的单点登录与单点登出 +* **oidc-sso**:拥有独立用户体系的系统,使用OIDC协议的单点登录与单点登出 diff --git a/zlt-demo/sso-demo/oidc-sso/README.md b/zlt-demo/sso-demo/oidc-sso/README.md new file mode 100644 index 00000000..11116508 --- /dev/null +++ b/zlt-demo/sso-demo/oidc-sso/README.md @@ -0,0 +1,35 @@ +## **详细的原理和注意事项请查看** +- [OIDC协议单点登录](https://www.kancloud.cn/zlt2000/microservices-platform/2278851) + +- [单点登出详解](https://www.kancloud.cn/zlt2000/microservices-platform/2539642) + + + +## oauth-center数据库执行以下sql +```sql +alter table oauth_client_details add support_id_token tinyint(1) DEFAULT 1 COMMENT '是否支持id_token'; +alter table oauth_client_details add id_token_validity int(11) DEFAULT 60 COMMENT 'id_token有效期'; + +update oauth_client_details set additional_information = '{"LOGOUT_NOTIFY_URL_LIST":"http://127.0.0.1:8082/logoutNotify"}' + , web_server_redirect_uri = 'http://127.0.0.1:8082/callback.html' +where client_id = 'webApp'; +``` + +## 启动以下服务 + +1. zlt-uaa:统一认证中心 +2. user-center:用户服务 +3. sc-gateway:api网关 +4. oidc-sso:单点登录demo(webApp应用) +5. web-sso:单点登录demo(app应用) + + + +## 测试步骤 + +1. 登录app应用: + 通过地址 http://127.0.0.1:8081 先登录app应用 +2. 访问app应用(单点成功): + 在浏览器打开一个新的页签(共享session),通过地址 http://127.0.0.1:8082/ 访问webApp应用,单点登录成功显示当前登录用户名、应用id、token信息 +3. 再次切换回app应用 http://127.0.0.1:8081 页面点击登出按钮,登出app应用 +4. 回来webApp应用 http://127.0.0.1:8082 刷新页面,返回登录页面(单点登出成功) \ No newline at end of file diff --git a/zlt-demo/sso-demo/oidc-sso/pom.xml b/zlt-demo/sso-demo/oidc-sso/pom.xml new file mode 100644 index 00000000..84d2665a --- /dev/null +++ b/zlt-demo/sso-demo/oidc-sso/pom.xml @@ -0,0 +1,50 @@ + + 4.0.0 + + com.zlt + sso-demo + 5.4.0 + + oidc-sso + OIDC协议单点登录demo + + + + cn.hutool + hutool-all + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-context + + + com.zlt + zlt-common-core + + + org.springframework.security + spring-security-jwt + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + diff --git a/zlt-demo/sso-demo/oidc-sso/src/main/java/com/sso/demo/OidcSSOApplication.java b/zlt-demo/sso-demo/oidc-sso/src/main/java/com/sso/demo/OidcSSOApplication.java new file mode 100644 index 00000000..1b5b01d8 --- /dev/null +++ b/zlt-demo/sso-demo/oidc-sso/src/main/java/com/sso/demo/OidcSSOApplication.java @@ -0,0 +1,18 @@ +package com.sso.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author zlt + * @date 2020/5/22 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@SpringBootApplication +public class OidcSSOApplication { + public static void main(String[] args) { + SpringApplication.run(OidcSSOApplication.class, args); + } +} diff --git a/zlt-demo/sso-demo/oidc-sso/src/main/java/com/sso/demo/controller/ApiController.java b/zlt-demo/sso-demo/oidc-sso/src/main/java/com/sso/demo/controller/ApiController.java new file mode 100644 index 00000000..72e3ff03 --- /dev/null +++ b/zlt-demo/sso-demo/oidc-sso/src/main/java/com/sso/demo/controller/ApiController.java @@ -0,0 +1,234 @@ +package com.sso.demo.controller; + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.central.common.model.Result; +import com.central.common.utils.JsonUtil; +import com.central.common.utils.RsaUtils; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.security.jwt.Jwt; +import org.springframework.security.jwt.JwtHelper; +import org.springframework.security.jwt.crypto.sign.RsaVerifier; +import org.springframework.security.jwt.crypto.sign.SignatureVerifier; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import sun.misc.BASE64Encoder; + +import javax.servlet.http.HttpServletRequest; +import java.nio.charset.StandardCharsets; +import java.security.interfaces.RSAPublicKey; +import java.util.*; + +/** + * @author zlt + * @date 2020/5/22 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@RestController +public class ApiController { + private static final String PUBKEY_START = "-----BEGIN PUBLIC KEY-----"; + private static final String PUBKEY_END = "-----END PUBLIC KEY-----"; + + @Value("${zlt.sso.client-id:}") + private String clientId; + + @Value("${zlt.sso.client-secret:}") + private String clientSecret; + + @Value("${zlt.sso.redirect-uri:}") + private String redirectUri; + + @Value("${zlt.sso.scope:}") + private String scope; + + @Value("${zlt.sso.access-token-uri:}") + private String accessTokenUri; + + @Value("${zlt.sso.jwt-key-uri:}") + private String jwtKeyUri; + + /** + * 公钥 + */ + private static RSAPublicKey publicKey; + + /** + * 模拟用户数据库 + */ + private static final Map userDb = new HashMap<>(); + + /** + * nonce存储 + */ + private final static ThreadLocal NONCE = new ThreadLocal<>(); + + private final static Map localTokenMap = new HashMap<>(); + + @GetMapping("/token/{code}") + public Map tokenInfo(@PathVariable String code) throws Exception { + //获取token + Map tokenMap = getAccessToken(code); + String idTokenStr = (String)tokenMap.get("id_token"); + //解析id_token + JsonNode idToken = this.getIdTokenJson(idTokenStr); + //检查id_token的有效性 + checkToken(idToken); + //获取用户信息 + MyUser user = this.getUserInfo(idToken); + //判断用户信息是否存在,否则注册用户信息 + if (!userDb.containsKey(user.getId())) { + userDb.put(user.getId(), user); + } + String accessToken = (String)tokenMap.get("access_token"); + localTokenMap.put(accessToken, user); + + Map result = new HashMap<>(2); + result.put("tokenInfo", tokenMap); + result.put("userInfo", user); + return result; + } + + @GetMapping("/logoutNotify") + public void logoutNotify(HttpServletRequest request) { + String tokens = request.getParameter("tokens"); + log.info("=====logoutNotify: " + tokens); + if (StrUtil.isNotEmpty(tokens)) { + for (String accessToken : tokens.split(",")) { + localTokenMap.remove(accessToken); + } + } + } + + @GetMapping("/user") + public MyUser user(HttpServletRequest request) { + String token = request.getParameter("access_token"); + return localTokenMap.get(token); + } + + /** + * 检查 id_token 的有效性 + */ + private void checkToken(JsonNode idToken) { + //token有效期 + long expiresAt = idToken.get("exp").asLong(); + long now = System.currentTimeMillis(); + Assert.isTrue((expiresAt > now), "id_token已过期"); + + //应用id + String aud = idToken.get("aud").asText(); + Assert.isTrue(clientId.equals(aud), "非法应用"+aud); + + //随机码 + String nonce = idToken.get("nonce").asText(); + Assert.isTrue((StrUtil.isEmpty(nonce) || nonce.equals(NONCE.get())), "nonce参数无效"); + } + + /** + * 获取token + */ + public Map getAccessToken(String code) { + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + String base64Auth = this.getBase64ClientParam(); + headers.add("Authorization", "Basic " + base64Auth); + + MultiValueMap param = new LinkedMultiValueMap<>(); + param.add("code", code); + param.add("grant_type", "authorization_code"); + param.add("redirect_uri", redirectUri); + param.add("scope", scope); + param.add("nonce", this.genNonce()); + HttpEntity> request = new HttpEntity<>(param, headers); + ResponseEntity response = restTemplate.postForEntity(accessTokenUri, request , Map.class); + return response.getBody(); + } + + private String genNonce() { + String nonce = RandomUtil.randomString(6); + NONCE.set(nonce); + return nonce; + } + + /** + * 把 id_token 字符串解析为json对象 + */ + public JsonNode getIdTokenJson(String idToken) throws Exception { + RSAPublicKey publicKey = getPubKeyObj(); + return this.decodeAndVerify(idToken, publicKey); + } + + /** + * 通过 id_token 获取用户信息 + */ + public MyUser getUserInfo(JsonNode idToken) { + MyUser user = new MyUser(); + user.setId(Long.valueOf(idToken.get("sub").textValue())); + user.setName(idToken.get("name").textValue()); + user.setLoginName(idToken.get("login_name").textValue()); + user.setPicture(idToken.get("picture").textValue()); + return user; + } + + private JsonNode decodeAndVerify(String jwtToken, RSAPublicKey rsaPublicKey) { + SignatureVerifier rsaVerifier = new RsaVerifier(rsaPublicKey); + Jwt jwt = JwtHelper.decodeAndVerify(jwtToken, rsaVerifier); + return JsonUtil.parse(jwt.getClaims()); + } + + /** + * 获取公钥 + */ + public RSAPublicKey getPubKeyObj() throws Exception { + if (publicKey == null) { + publicKey = getPubKeyByRemote(); + } + return publicKey; + } + + private RSAPublicKey getPubKeyByRemote() throws Exception { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + String base64Auth = this.getBase64ClientParam(); + headers.add("Authorization", "Basic " + base64Auth); + + HttpEntity request = new HttpEntity<>(headers); + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity response = restTemplate.exchange(jwtKeyUri, HttpMethod.GET, request, Result.class); + Result result = response.getBody(); + Assert.isTrue((result.getResp_code() == 0), result.getResp_msg()); + + String publicKeyStr = result.getResp_msg(); + publicKeyStr = publicKeyStr.substring(PUBKEY_START.length(), publicKeyStr.indexOf(PUBKEY_END)); + return RsaUtils.getPublicKey(publicKeyStr); + } + + /** + * base64加密应用参数 + */ + private String getBase64ClientParam() { + byte[] authorization = (clientId + ":" + clientSecret).getBytes(StandardCharsets.UTF_8); + BASE64Encoder encoder = new BASE64Encoder(); + return encoder.encode(authorization); + } + + @Data + public static class MyUser { + private Long id; + private String name; + private String loginName; + private String picture; + } +} diff --git a/zlt-demo/sso-demo/oidc-sso/src/main/resources/bootstrap.yml b/zlt-demo/sso-demo/oidc-sso/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..bb422ead --- /dev/null +++ b/zlt-demo/sso-demo/oidc-sso/src/main/resources/bootstrap.yml @@ -0,0 +1,15 @@ +server: + port: 8082 + +spring: + application: + name: oidc-demo + +zlt: + sso: + client-id: webApp + client-secret: webApp + redirect-uri: http://127.0.0.1:8082/callback.html + access-token-uri: http://127.0.0.1:9900/api-uaa/oauth/token + jwt-key-uri: http://127.0.0.1:9900/api-uaa/tokens/key + scope: app \ No newline at end of file diff --git a/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/callback.html b/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/callback.html new file mode 100644 index 00000000..f320ba84 --- /dev/null +++ b/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/callback.html @@ -0,0 +1,35 @@ + + + + + + zlt + + + + + + + diff --git a/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/index.html b/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/index.html new file mode 100644 index 00000000..8061a116 --- /dev/null +++ b/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/index.html @@ -0,0 +1,54 @@ + + + + + + zlt + + + + +

+

用户名:

+

登录名:

+

头像:

+

应用id:

+

token:

+

+
+ + + diff --git a/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/js/jquery-3.2.1.min.js b/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/js/jquery-3.2.1.min.js new file mode 100644 index 00000000..644d35e2 --- /dev/null +++ b/zlt-demo/sso-demo/oidc-sso/src/main/resources/static/js/jquery-3.2.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S), +a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b), +null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" + +

用户名:

+

权限:[]

应用id:

token:

diff --git a/zlt-demo/sso-demo/web-sso/src/main/resources/static/js/sso.js b/zlt-demo/sso-demo/web-sso/src/main/resources/static/js/sso.js new file mode 100644 index 00000000..84b7bd8b --- /dev/null +++ b/zlt-demo/sso-demo/web-sso/src/main/resources/static/js/sso.js @@ -0,0 +1,38 @@ +const FULL_CHARTER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopgrstuvwxyz'; +//应用id +let clientId = 'app'; +//授权中心地址 +let uaaUri = 'http://127.0.0.1:9900/api-uaa/oauth/'; + +function getAuthorizeUri(state) { + return uaaUri+'authorize?client_id='+clientId+'&redirect_uri=http://127.0.0.1:8081/callback.html&response_type=code&state='+state; +} + +function getLogoutUri(accessToken) { + return uaaUri+'remove/token?redirect_uri=http://127.0.0.1:8081/index.html&access_token='+accessToken; +} + +function getUserInfo(accessToken) { + return 'http://127.0.0.1:8081/user?access_token='+accessToken; +} + +function getState() { + let state=''; + for (let i = 0; i < 6; i++) { + state += FULL_CHARTER[Math.floor(Math.random() * 52)]; + } + return state; +} + +/** + * 获取url参数 + */ +function getQueryVariable(variable) { + var query = window.location.search.substring(1); + var vars = query.split("&"); + for (var i=0;i com.zlt zlt-demo - 3.5.0 + 5.4.0 txlcn-demo txlcn分布式事务demo diff --git a/zlt-demo/txlcn-demo/txlcn-demo-common/pom.xml b/zlt-demo/txlcn-demo/txlcn-demo-common/pom.xml index 6c607a62..06594420 100644 --- a/zlt-demo/txlcn-demo/txlcn-demo-common/pom.xml +++ b/zlt-demo/txlcn-demo/txlcn-demo-common/pom.xml @@ -4,7 +4,7 @@ com.zlt txlcn-demo - 3.5.0 + 5.4.0 txlcn-demo-common demo-common @@ -40,7 +40,7 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter com.zlt diff --git a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-a/pom.xml b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-a/pom.xml index 75bf4c78..2f15e2ab 100644 --- a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-a/pom.xml +++ b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-a/pom.xml @@ -3,7 +3,7 @@ com.zlt txlcn-demo - 3.5.0 + 5.4.0 4.0.0 diff --git a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-a/src/main/resources/application.yml b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-a/src/main/resources/application.yml index cd402049..7133edbd 100644 --- a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-a/src/main/resources/application.yml +++ b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-a/src/main/resources/application.yml @@ -16,4 +16,10 @@ tx-lcn: driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://${zlt.datasource.ip}:3306/tx_logger?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull username: ${zlt.datasource.username} - password: ${zlt.datasource.password} \ No newline at end of file + password: ${zlt.datasource.password} +zlt: + loadbalance: + version: test + isolation: + enabled: true + chooser: com.central.common.lb.chooser.RandomRuleChooser diff --git a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-b/pom.xml b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-b/pom.xml index b88ac6ab..86dca534 100644 --- a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-b/pom.xml +++ b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-b/pom.xml @@ -3,7 +3,7 @@ com.zlt txlcn-demo - 3.5.0 + 5.4.0 4.0.0 txlcn-demo-spring-service-b diff --git a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-b/src/main/resources/application.yml b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-b/src/main/resources/application.yml index cd402049..68d7e81d 100644 --- a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-b/src/main/resources/application.yml +++ b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-b/src/main/resources/application.yml @@ -16,4 +16,9 @@ tx-lcn: driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://${zlt.datasource.ip}:3306/tx_logger?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull username: ${zlt.datasource.username} - password: ${zlt.datasource.password} \ No newline at end of file + password: ${zlt.datasource.password} +zlt: + loadbalance: + version: test + isolation: + enabled: true \ No newline at end of file diff --git a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-c/pom.xml b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-c/pom.xml index 6d9c49ab..9c6690e6 100644 --- a/zlt-demo/txlcn-demo/txlcn-demo-spring-service-c/pom.xml +++ b/zlt-demo/txlcn-demo/txlcn-demo-spring-service-c/pom.xml @@ -3,7 +3,7 @@ com.zlt txlcn-demo - 3.5.0 + 5.4.0 4.0.0 txlcn-demo-spring-service-c diff --git a/zlt-demo/websocket-demo/README.md b/zlt-demo/websocket-demo/README.md new file mode 100644 index 00000000..ddd83f5d --- /dev/null +++ b/zlt-demo/websocket-demo/README.md @@ -0,0 +1,39 @@ +## 代码说明 +- [SpringBoot的WebSocket接口如何实现鉴权?](https://www.kancloud.cn/zlt2000/microservices-platform/2278851) + +  +## 启动以下服务 + +1. zlt-uaa:统一认证中心 +2. user-center:用户服务 +3. sc-gateway:api网关 +4. websocket-demo + +> 环境配置与启动参考文档:https://www.kancloud.cn/zlt2000/microservices-platform/919418 + +  +## 获取access_token +可使用任意授权模式获取; + +例如:密码模式授权 +- 请求方式:POST +- 请求头:Authorization:Basic d2ViQXBwOndlYkFwcA== +- 请求地址:http://localhost:9900/api-uaa/oauth/token?grant_type=password&username=admin&password=admin + +> 授权接口清单参考文档:https://www.kancloud.cn/zlt2000/microservices-platform/1158135 + +  +## 测试步骤 +使用 `Postman` 进行测试(最新版本支持 webSocket 接口) + +点击 `New` 按钮,选择 `WebSocket Request` ; + +在 `URL` 中输入 `ws://localhost:8092/websocket/test` + +在 `Headers` 中添加:`Authorization:Bearer xxx` + +或者 + +在 `参数` 中添加:`ws://localhost:8092/websocket/test?access_token=xxx` + +> xxx 需替换为正确的 access_token \ No newline at end of file diff --git a/zlt-demo/websocket-demo/pom.xml b/zlt-demo/websocket-demo/pom.xml new file mode 100644 index 00000000..2644e330 --- /dev/null +++ b/zlt-demo/websocket-demo/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + com.zlt + zlt-demo + 5.4.0 + + websocket-demo + + + + com.zlt + zlt-config + + + com.zlt + zlt-auth-client-spring-boot-starter + + + com.zlt + zlt-redis-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-websocket + + + com.baomidou + mybatis-plus-extension + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + \ No newline at end of file diff --git a/zlt-demo/websocket-demo/src/main/java/org/zlt/WebSocketApp.java b/zlt-demo/websocket-demo/src/main/java/org/zlt/WebSocketApp.java new file mode 100644 index 00000000..bab8a72c --- /dev/null +++ b/zlt-demo/websocket-demo/src/main/java/org/zlt/WebSocketApp.java @@ -0,0 +1,18 @@ +package org.zlt; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author zlt + * @date 2022/5/8 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@SpringBootApplication +public class WebSocketApp { + public static void main(String[] args) { + SpringApplication.run(WebSocketApp.class, args); + } +} diff --git a/zlt-demo/websocket-demo/src/main/java/org/zlt/config/MyResourceConfig.java b/zlt-demo/websocket-demo/src/main/java/org/zlt/config/MyResourceConfig.java new file mode 100644 index 00000000..93a4f3ff --- /dev/null +++ b/zlt-demo/websocket-demo/src/main/java/org/zlt/config/MyResourceConfig.java @@ -0,0 +1,20 @@ +package org.zlt.config; + +import com.central.oauth2.common.config.DefaultResourceServerConf; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; + +/** + * security资源服务器配置 + * + * @author zlt + * @version 1.0 + * @date 2022/5/9 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Configuration +@EnableResourceServer +public class MyResourceConfig extends DefaultResourceServerConf { +} diff --git a/zlt-demo/websocket-demo/src/main/java/org/zlt/config/WebSocketConfig.java b/zlt-demo/websocket-demo/src/main/java/org/zlt/config/WebSocketConfig.java new file mode 100644 index 00000000..c25ef1c7 --- /dev/null +++ b/zlt-demo/websocket-demo/src/main/java/org/zlt/config/WebSocketConfig.java @@ -0,0 +1,23 @@ +package org.zlt.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * WebSocket配置 + * + * @author zlt + * @version 1.0 + * @date 2022/5/8 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Configuration +public class WebSocketConfig { + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } +} diff --git a/zlt-demo/websocket-demo/src/main/java/org/zlt/controller/TestWebSocketController.java b/zlt-demo/websocket-demo/src/main/java/org/zlt/controller/TestWebSocketController.java new file mode 100644 index 00000000..d14f94d3 --- /dev/null +++ b/zlt-demo/websocket-demo/src/main/java/org/zlt/controller/TestWebSocketController.java @@ -0,0 +1,27 @@ +package org.zlt.controller; + +import com.central.oauth2.common.config.WcAuthConfigurator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; + +/** + * @author zlt + * @date 2022/5/8 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@Component +@ServerEndpoint(value = "/websocket/test", configurator = WcAuthConfigurator.class) +public class TestWebSocketController { + @OnOpen + public void onOpen(Session session) throws IOException { + session.getBasicRemote().sendText("TestWebSocketController-ok"); + } +} diff --git a/zlt-demo/websocket-demo/src/main/resources/bootstrap.yml b/zlt-demo/websocket-demo/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..19e43b8d --- /dev/null +++ b/zlt-demo/websocket-demo/src/main/resources/bootstrap.yml @@ -0,0 +1,14 @@ +server: + port: 8092 + +spring: + application: + name: zlt-websocket + main: + allow-bean-definition-overriding: true + +zlt: + security: + ignore: + httpUrls: > + /websocket/** \ No newline at end of file diff --git a/zlt-doc/Dockerfile b/zlt-doc/Dockerfile new file mode 100644 index 00000000..c04c805e --- /dev/null +++ b/zlt-doc/Dockerfile @@ -0,0 +1,6 @@ +FROM mysql:5.7 + +LABEL MAINTAINER=Andy + +COPY sql/*.sql /docker-entrypoint-initdb.d/ +# docker run --name zlt-db -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=test -d zlt-db:mysql-8.0.25 \ No newline at end of file diff --git a/zlt-doc/sql/oauth-center.sql b/zlt-doc/sql/oauth-center.sql index 6ed53f27..bdf168fc 100644 --- a/zlt-doc/sql/oauth-center.sql +++ b/zlt-doc/sql/oauth-center.sql @@ -18,16 +18,18 @@ CREATE TABLE `oauth_client_details` ( `access_token_validity` int(11) NULL DEFAULT NULL COMMENT 'access_token有效期', `refresh_token_validity` int(11) NULL DEFAULT NULL COMMENT 'refresh_token有效期', `additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '{}' COMMENT '{}', - `autoapprove` char(4) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'true' COMMENT '是否自动授权 是-true', + `autoapprove` char(5) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'true' COMMENT '是否自动授权 是-true', `create_time` datetime(0) NULL DEFAULT NULL, `update_time` datetime(0) NULL DEFAULT NULL, `client_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '应用名称', + `support_id_token` tinyint(1) DEFAULT 1 COMMENT '是否支持id_token', + `id_token_validity` int(11) DEFAULT 60 COMMENT 'id_token有效期', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of oauth_client_details -- ---------------------------- -INSERT INTO `oauth_client_details` VALUES (1, 'webApp', NULL, '$2a$10$06msMGYRH8nrm4iVnKFNKOoddB8wOwymVhbUzw/d3ZixD7Nq8ot72', 'webApp', 'app', 'authorization_code,password,refresh_token,client_credentials', NULL, NULL, 3600, NULL, '{}', 'true', NULL, NULL, 'pc端'); -INSERT INTO `oauth_client_details` VALUES (2, 'app', NULL, '$2a$10$i3F515wEDiB4Gvj9ym9Prui0dasRttEUQ9ink4Wpgb4zEDCAlV8zO', 'app', 'app', 'authorization_code,password,refresh_token', 'http://127.0.0.1:8081/callback.html', NULL, 3600, NULL, '{}', 'true', NULL, NULL, '移动端'); -INSERT INTO `oauth_client_details` VALUES (3, 'zlt', NULL, '$2a$10$/o.wuORzVcXaezmYVzwYMuoY7qeWXBALwQmkskXD/7C6rqfCyPrna', 'zlt', 'all', 'authorization_code,password,refresh_token,client_credentials', 'http://127.0.0.1:8080/singleLogin', NULL, 3600, 28800, '{}', 'true', '2018-12-27 00:50:30', '2018-12-27 00:50:30', '第三方应用'); \ No newline at end of file +INSERT INTO `oauth_client_details` VALUES (1, 'webApp', NULL, '$2a$10$06msMGYRH8nrm4iVnKFNKOoddB8wOwymVhbUzw/d3ZixD7Nq8ot72', 'webApp', 'app', 'authorization_code,password,refresh_token,client_credentials,implicit,password_code,openId,mobile_password', 'http://127.0.0.1:8082/callback.html', NULL, 3600, NULL, '{"LOGOUT_NOTIFY_URL_LIST":"http://127.0.0.1:8082/logoutNotify"}', 'true', NULL, NULL, 'pc端', 1, 60); +INSERT INTO `oauth_client_details` VALUES (2, 'app', NULL, '$2a$10$i3F515wEDiB4Gvj9ym9Prui0dasRttEUQ9ink4Wpgb4zEDCAlV8zO', 'app', 'app', 'authorization_code,password,refresh_token', 'http://127.0.0.1:8081/callback.html', NULL, 3600, NULL, '{"LOGOUT_NOTIFY_URL_LIST":"http://127.0.0.1:8081/logoutNotify"}', 'true', NULL, NULL, '移动端', 1, 60); +INSERT INTO `oauth_client_details` VALUES (3, 'zlt', NULL, '$2a$10$/o.wuORzVcXaezmYVzwYMuoY7qeWXBALwQmkskXD/7C6rqfCyPrna', 'zlt', 'all', 'authorization_code,password,refresh_token,client_credentials', 'http://127.0.0.1:8080/singleLogin', NULL, 3600, 28800, '{}', 'true', '2018-12-27 00:50:30', '2018-12-27 00:50:30', '第三方应用', 1, 60); \ No newline at end of file diff --git a/zlt-doc/sql/user-center.sql b/zlt-doc/sql/user-center.sql index f908c667..9a48ed22 100644 --- a/zlt-doc/sql/user-center.sql +++ b/zlt-doc/sql/user-center.sql @@ -8,7 +8,7 @@ DROP TABLE IF EXISTS `sys_user`; CREATE TABLE `sys_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `password` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录密码', `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `head_img_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, @@ -123,7 +123,7 @@ INSERT INTO `sys_menu` VALUES (2, 12, '用户管理', '#!user', 'system/user.htm INSERT INTO `sys_menu` VALUES (3, 12, '角色管理', '#!role', 'system/role.html', NULL, 'layui-icon-user', 3, '2017-11-17 16:56:59', '2019-01-14 15:34:40', 1, 0, 'webApp'); INSERT INTO `sys_menu` VALUES (4, 12, '菜单管理', '#!menus', 'system/menus.html', NULL, 'layui-icon-menu-fill', 4, '2017-11-17 16:56:59', '2018-09-03 02:23:47', 1, 0, 'webApp'); INSERT INTO `sys_menu` VALUES (9, 37, '文件中心', '#!files', 'files/files.html', NULL, 'layui-icon-file', 3, '2017-11-17 16:56:59', '2019-01-17 20:18:44', 1, 0, 'webApp'); -INSERT INTO `sys_menu` VALUES (10, 37, '文档中心', '#!swagger', 'http://127.0.0.1:9900/swagger-ui.html', NULL, 'layui-icon-app', 4, '2017-11-17 16:56:59', '2019-01-17 20:18:48', 1, 0, 'webApp'); +INSERT INTO `sys_menu` VALUES (10, 37, '文档中心', '#!swagger', 'http://127.0.0.1:9900/doc.html', NULL, 'layui-icon-app', 4, '2017-11-17 16:56:59', '2019-01-17 20:18:48', 1, 0, 'webApp'); INSERT INTO `sys_menu` VALUES (11, 12, '我的信息', '#!myInfo', 'system/myInfo.html', NULL, 'layui-icon-login-qq', 10, '2017-11-17 16:56:59', '2018-09-02 06:12:24', 1, 0, 'webApp'); INSERT INTO `sys_menu` VALUES (12, -1, '认证管理', 'javascript:;', '', NULL, 'layui-icon-set', 1, '2017-11-17 16:56:59', '2018-12-13 15:02:49', 1, 0, 'webApp'); INSERT INTO `sys_menu` VALUES (35, 12, '应用管理', '#!app', 'attestation/app.html', NULL, 'layui-icon-link', 5, '2017-11-17 16:56:59', '2019-01-14 15:35:15', 1, 0, 'webApp'); diff --git a/zlt-doc/sql/xxl-job.sql b/zlt-doc/sql/xxl-job.sql index 8013eb8b..8dba4f52 100644 --- a/zlt-doc/sql/xxl-job.sql +++ b/zlt-doc/sql/xxl-job.sql @@ -211,7 +211,7 @@ DROP TABLE IF EXISTS `XXL_JOB_QRTZ_TRIGGER_GROUP`; CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_GROUP` ( `id` int(11) NOT NULL AUTO_INCREMENT, `app_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '执行器AppName', - `title` varchar(12) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '执行器名称', + `title` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '执行器名称', `order` tinyint(4) NOT NULL DEFAULT 0 COMMENT '排序', `address_type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '执行器地址类型:0=自动注册、1=手动录入', `address_list` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '执行器地址列表,多地址逗号分隔', diff --git "a/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v4.0.0.sql" "b/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v4.0.0.sql" new file mode 100644 index 00000000..438dd892 --- /dev/null +++ "b/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v4.0.0.sql" @@ -0,0 +1,3 @@ +------------更新语句 +Use `oauth-center`; +update oauth_client_details set authorized_grant_types = 'authorization_code,password,refresh_token,client_credentials,implicit,password_code,openId,mobile_password' where client_id = 'webApp' \ No newline at end of file diff --git "a/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v4.5.0.sql" "b/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v4.5.0.sql" new file mode 100644 index 00000000..f4384603 --- /dev/null +++ "b/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v4.5.0.sql" @@ -0,0 +1,7 @@ +------------更新语句 +Use `oauth-center`; +alter table oauth_client_details add support_id_token tinyint(1) DEFAULT 1 COMMENT '是否支持id_token'; +alter table oauth_client_details add id_token_validity int(11) DEFAULT 60 COMMENT 'id_token有效期'; + +Use `user-center`; +alter table `sys_user` MODIFY COLUMN `password` varchar(100) NOT NULL COMMENT '登录密码'; diff --git "a/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v5.1.0.sql" "b/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v5.1.0.sql" new file mode 100644 index 00000000..b86bae45 --- /dev/null +++ "b/zlt-doc/\347\211\210\346\234\254\345\215\207\347\272\247_sql/\346\227\247\347\211\210\346\234\254\345\215\207\347\272\247v5.1.0.sql" @@ -0,0 +1,7 @@ +------------更新语句 +Use `oauth-center`; +update oauth_client_details set additional_information = '{"LOGOUT_NOTIFY_URL_LIST":"http://127.0.0.1:8082/logoutNotify"}' +where client_id = 'webApp'; + +update oauth_client_details set additional_information = '{"LOGOUT_NOTIFY_URL_LIST":"http://127.0.0.1:8081/logoutNotify"}' +where client_id = 'app'; \ No newline at end of file diff --git a/zlt-gateway/pom.xml b/zlt-gateway/pom.xml index b59be40c..e34d5a20 100644 --- a/zlt-gateway/pom.xml +++ b/zlt-gateway/pom.xml @@ -4,13 +4,11 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-gateway pom - - zuul-gateway sc-gateway diff --git a/zlt-gateway/sc-gateway/pom.xml b/zlt-gateway/sc-gateway/pom.xml index 4f420876..8d28f24e 100644 --- a/zlt-gateway/sc-gateway/pom.xml +++ b/zlt-gateway/sc-gateway/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-gateway - 3.5.0 + 5.4.0 sc-gateway spring cloud gateway网关 @@ -16,7 +16,7 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter com.zlt @@ -30,6 +30,18 @@ com.zlt zlt-auth-client-spring-boot-starter + + + com.zlt + zlt-redis-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.cloud spring-cloud-starter-gateway @@ -57,17 +69,10 @@ - io.springfox - springfox-swagger2 - - - io.springfox - springfox-swagger-ui - - - com.zlt - zlt-redis-spring-boot-starter + com.github.xiaoymin + knife4j-spring-boot-starter + org.springframework.security spring-security-oauth2-resource-server @@ -84,6 +89,10 @@ eu.bitwalker UserAgentUtils + + org.springframework.security.oauth + spring-security-oauth2 + diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/SCGatewayApp.java b/zlt-gateway/sc-gateway/src/main/java/com/central/SCGatewayApp.java index e7d55fed..c2ecc2bd 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/SCGatewayApp.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/SCGatewayApp.java @@ -1,6 +1,6 @@ package com.central; -import com.central.common.ribbon.annotation.EnableBaseFeignInterceptor; +import com.central.common.lb.annotation.EnableBaseFeignInterceptor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @@ -10,7 +10,7 @@ * @author zlt * @date 2019/10/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @EnableFeignClients diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/CustomAuthenticationManager.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/CustomAuthenticationManager.java index b3f30a07..e2d5019a 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/CustomAuthenticationManager.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/CustomAuthenticationManager.java @@ -13,7 +13,7 @@ * @author zlt * @date 2019/10/6 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ public class CustomAuthenticationManager implements ReactiveAuthenticationManager { diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/CustomServerWebExchangeMatchers.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/CustomServerWebExchangeMatchers.java new file mode 100644 index 00000000..23e02a43 --- /dev/null +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/CustomServerWebExchangeMatchers.java @@ -0,0 +1,38 @@ +package com.central.gateway.auth; + +import com.central.oauth2.common.properties.SecurityProperties; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 自定义 ServerWebExchangeMatcher + * 解决只要请求携带access_token,排除鉴权的url依然会被拦截 + * + * @author zlt + * @version 1.0 + * @date 2022/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class CustomServerWebExchangeMatchers implements ServerWebExchangeMatcher { + private final SecurityProperties securityProperties; + + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + public CustomServerWebExchangeMatchers(SecurityProperties securityProperties) { + this.securityProperties = securityProperties; + } + + @Override + public Mono matches(ServerWebExchange exchange) { + for (String url : securityProperties.getIgnore().getUrls()) { + if (antPathMatcher.match(url, exchange.getRequest().getURI().getPath())) { + return MatchResult.notMatch(); + } + } + return MatchResult.match(); + } +} diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/JsonAccessDeniedHandler.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/JsonAccessDeniedHandler.java index 34076c8e..8a213b8f 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/JsonAccessDeniedHandler.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/JsonAccessDeniedHandler.java @@ -1,6 +1,6 @@ package com.central.gateway.auth; -import com.central.common.utils.ResponseUtil; +import com.central.common.utils.WebfluxResponseUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; @@ -14,13 +14,13 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j public class JsonAccessDeniedHandler implements ServerAccessDeniedHandler { @Override public Mono handle(ServerWebExchange exchange, AccessDeniedException e) { - return ResponseUtil.responseFailed(exchange, HttpStatus.FORBIDDEN.value(), e.getMessage()); + return WebfluxResponseUtil.responseFailed(exchange, HttpStatus.FORBIDDEN.value(), e.getMessage()); } } diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/JsonAuthenticationEntryPoint.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/JsonAuthenticationEntryPoint.java index 672580a8..52dddddc 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/JsonAuthenticationEntryPoint.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/JsonAuthenticationEntryPoint.java @@ -1,6 +1,6 @@ package com.central.gateway.auth; -import com.central.common.utils.ResponseUtil; +import com.central.common.utils.WebfluxResponseUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.core.AuthenticationException; @@ -14,13 +14,13 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j public class JsonAuthenticationEntryPoint implements ServerAuthenticationEntryPoint { @Override public Mono commence(ServerWebExchange exchange, AuthenticationException e) { - return ResponseUtil.responseFailed(exchange, HttpStatus.UNAUTHORIZED.value(), e.getMessage()); + return WebfluxResponseUtil.responseFailed(exchange, HttpStatus.UNAUTHORIZED.value(), e.getMessage()); } } diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/Oauth2AuthSuccessHandler.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/Oauth2AuthSuccessHandler.java index 11e57ec3..ea1b1e8f 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/Oauth2AuthSuccessHandler.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/Oauth2AuthSuccessHandler.java @@ -1,8 +1,10 @@ package com.central.gateway.auth; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; import com.central.common.constant.SecurityConstants; import com.central.common.model.SysUser; +import com.central.oauth2.common.util.AuthUtils; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.provider.OAuth2Authentication; @@ -19,13 +21,13 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ public class Oauth2AuthSuccessHandler implements ServerAuthenticationSuccessHandler { @Override public Mono onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) { - MultiValueMap headerValues = new LinkedMultiValueMap(4); + MultiValueMap headerValues = new LinkedMultiValueMap<>(4); Object principal = authentication.getPrincipal(); //客户端模式只返回一个clientId if (principal instanceof SysUser) { @@ -37,12 +39,13 @@ public Mono onAuthenticationSuccess(WebFilterExchange webFilterExchange, A String clientId = oauth2Authentication.getOAuth2Request().getClientId(); headerValues.add(SecurityConstants.TENANT_HEADER, clientId); headerValues.add(SecurityConstants.ROLE_HEADER, CollectionUtil.join(authentication.getAuthorities(), ",")); - + String accountType = AuthUtils.getAccountType(oauth2Authentication.getUserAuthentication()); + if (StrUtil.isNotEmpty(accountType)) { + headerValues.add(SecurityConstants.ACCOUNT_TYPE_HEADER, accountType); + } ServerWebExchange exchange = webFilterExchange.getExchange(); ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate() - .headers(h -> { - h.addAll(headerValues); - }) + .headers(h -> h.addAll(headerValues)) .build(); ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build(); diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/PermissionAuthManager.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/PermissionAuthManager.java index da4619c2..1afb01b4 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/PermissionAuthManager.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/auth/PermissionAuthManager.java @@ -1,7 +1,7 @@ package com.central.gateway.auth; import com.central.common.model.SysMenu; -import com.central.gateway.feign.MenuService; +import com.central.gateway.feign.AsynMenuService; import com.central.oauth2.common.service.impl.DefaultPermissionServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -14,7 +14,9 @@ import reactor.core.publisher.Mono; import javax.annotation.Resource; +import java.util.Collections; import java.util.List; +import java.util.concurrent.Future; /** * url权限认证 @@ -22,14 +24,14 @@ * @author zlt * @date 2019/10/6 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j @Component public class PermissionAuthManager extends DefaultPermissionServiceImpl implements ReactiveAuthorizationManager { @Resource - private MenuService menuService; + private AsynMenuService asynMenuService; @Override public Mono check(Mono authentication, AuthorizationContext authorizationContext) { @@ -43,6 +45,12 @@ public Mono check(Mono authentication, Au @Override public List findMenuByRoleCodes(String roleCodes) { - return menuService.findByRoleCodes(roleCodes); + Future> futureResult = asynMenuService.findByRoleCodes(roleCodes); + try { + return futureResult.get(); + } catch (Exception e) { + log.error("asynMenuService.findMenuByRoleCodes-error", e); + } + return Collections.emptyList(); } -} +} \ No newline at end of file diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/AsycTaskExecutorConfig.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/AsycTaskExecutorConfig.java similarity index 71% rename from zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/AsycTaskExecutorConfig.java rename to zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/AsycTaskExecutorConfig.java index 3a7c9253..49ab7e6d 100644 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/AsycTaskExecutorConfig.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/AsycTaskExecutorConfig.java @@ -1,14 +1,19 @@ -package com.central.gateway.config; - -import com.central.common.config.DefaultAsycTaskConfig; -import org.springframework.context.annotation.Configuration; - -/** - * @author zlt - * 线程池配置、启用异步 - * @Async quartz 需要使用 - */ -@Configuration -public class AsycTaskExecutorConfig extends DefaultAsycTaskConfig { - -} +package com.central.gateway.config; + +import com.central.common.config.DefaultAsycTaskConfig; +import org.springframework.context.annotation.Configuration; + +/** + * 线程池配置、启用异步 + * + * @author zlt + * @version 1.0 + * @date 2021/8/8 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Configuration +public class AsycTaskExecutorConfig extends DefaultAsycTaskConfig { + +} diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/CorsConfig.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/CorsConfig.java index ed033cdc..7e2ea488 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/CorsConfig.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/CorsConfig.java @@ -2,6 +2,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; @@ -13,20 +15,21 @@ * @author zlt * @date 2019/10/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Configuration public class CorsConfig { private static final String ALL = "*"; + @Order(Ordered.HIGHEST_PRECEDENCE) @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); // cookie跨域 config.setAllowCredentials(Boolean.TRUE); config.addAllowedMethod(ALL); - config.addAllowedOrigin(ALL); + config.addAllowedOriginPattern(ALL); config.addAllowedHeader(ALL); // 配置前端js允许访问的自定义响应头 config.addExposedHeader("setToken"); diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/DynamicRouteConfig.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/DynamicRouteConfig.java index 66330d95..5720d863 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/DynamicRouteConfig.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/DynamicRouteConfig.java @@ -14,7 +14,7 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Configuration diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/FeignConfig.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/FeignConfig.java new file mode 100644 index 00000000..9f4d343d --- /dev/null +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/FeignConfig.java @@ -0,0 +1,45 @@ +package com.central.gateway.config; + +import feign.codec.Decoder; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.support.ResponseEntityDecoder; +import org.springframework.cloud.openfeign.support.SpringDecoder; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Feign配置类 + * 配置Feign的Decoder解决在Gateway中使用Feign时报错找不到HttpMessageConverters + * + * @author zlt + * @date 2020/7/26 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Configuration +public class FeignConfig { + @Bean + public Decoder feignDecoder() { + return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter())); + } + + public ObjectFactory feignHttpMessageConverter() { + final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new GateWayMappingJackson2HttpMessageConverter()); + return () -> httpMessageConverters; + } + + public static class GateWayMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter { + GateWayMappingJackson2HttpMessageConverter(){ + List mediaTypes = new ArrayList<>(); + mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8")); + setSupportedMediaTypes(mediaTypes); + } + } +} diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/ResourceServerConfiguration.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/ResourceServerConfiguration.java index a6792804..5f8edf6a 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/ResourceServerConfiguration.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/config/ResourceServerConfiguration.java @@ -21,7 +21,7 @@ * @author zlt * @date 2019/10/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Configuration @@ -48,6 +48,7 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { oauth2Filter.setServerAuthenticationConverter(tokenAuthenticationConverter); oauth2Filter.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(entryPoint)); oauth2Filter.setAuthenticationSuccessHandler(new Oauth2AuthSuccessHandler()); + oauth2Filter.setRequiresAuthenticationMatcher(new CustomServerWebExchangeMatchers(securityProperties)); http.addFilterAt(oauth2Filter, SecurityWebFiltersOrder.AUTHENTICATION); ServerHttpSecurity.AuthorizeExchangeSpec authorizeExchange = http.authorizeExchange(); diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/error/CustomErrorWebFluxAutoConfiguration.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/error/CustomErrorWebFluxAutoConfiguration.java index d9f0b733..174d70c0 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/error/CustomErrorWebFluxAutoConfiguration.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/error/CustomErrorWebFluxAutoConfiguration.java @@ -4,8 +4,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.autoconfigure.web.ResourceProperties; import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.WebProperties; import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.reactive.error.ErrorAttributes; @@ -28,33 +28,33 @@ * @author zlt * @date 2020/3/30 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Configuration @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @ConditionalOnClass(WebFluxConfigurer.class) @AutoConfigureBefore(WebFluxAutoConfiguration.class) -@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class}) +@EnableConfigurationProperties({ServerProperties.class, WebProperties.class}) public class CustomErrorWebFluxAutoConfiguration { private final ServerProperties serverProperties; private final ApplicationContext applicationContext; - private final ResourceProperties resourceProperties; + private final WebProperties webProperties; private final List viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public CustomErrorWebFluxAutoConfiguration(ServerProperties serverProperties, - ResourceProperties resourceProperties, + WebProperties webProperties, ObjectProvider> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) { this.serverProperties = serverProperties; this.applicationContext = applicationContext; - this.resourceProperties = resourceProperties; + this.webProperties = webProperties; this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } @@ -64,7 +64,7 @@ public CustomErrorWebFluxAutoConfiguration(ServerProperties serverProperties, public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) { JsonErrorWebExceptionHandler exceptionHandler = new JsonErrorWebExceptionHandler( errorAttributes, - this.resourceProperties, + this.webProperties.getResources(), this.serverProperties.getError(), this.applicationContext); exceptionHandler.setViewResolvers(this.viewResolvers); diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/error/JsonErrorWebExceptionHandler.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/error/JsonErrorWebExceptionHandler.java index 27c64e9d..93814e37 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/error/JsonErrorWebExceptionHandler.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/error/JsonErrorWebExceptionHandler.java @@ -1,11 +1,13 @@ package com.central.gateway.error; import org.springframework.boot.autoconfigure.web.ErrorProperties; -import org.springframework.boot.autoconfigure.web.ResourceProperties; +import org.springframework.boot.autoconfigure.web.WebProperties; import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler; +import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.reactive.error.ErrorAttributes; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpStatus; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.web.reactive.function.server.*; import java.util.HashMap; @@ -17,11 +19,11 @@ * @author zlt * @date 2020/3/30 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ public class JsonErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler { - public JsonErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, + public JsonErrorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties.Resources resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) { super(errorAttributes, resourceProperties, errorProperties, applicationContext); } @@ -30,9 +32,9 @@ public JsonErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourcePro * 获取异常属性 */ @Override - protected Map getErrorAttributes(ServerRequest request, boolean includeStackTrace) { + protected Map getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) { Throwable error = super.getError(request); - return responseError(this.buildMessage(request, error)); + return responseError(request, error); } /** @@ -49,8 +51,9 @@ protected RouterFunction getRoutingFunction(ErrorAttributes erro * @param errorAttributes */ @Override - protected HttpStatus getHttpStatus(Map errorAttributes) { - return HttpStatus.INTERNAL_SERVER_ERROR; + protected int getHttpStatus(Map errorAttributes) { + Integer httpStatus = (Integer) errorAttributes.remove("httpStatus"); + return httpStatus != null ? httpStatus : HttpStatus.INTERNAL_SERVER_ERROR.value(); } /** @@ -74,14 +77,26 @@ private String buildMessage(ServerRequest request, Throwable ex) { /** * 构建返回的JSON数据格式 - * @param errorMessage 异常信息 - * @return */ - public static Map responseError(String errorMessage) { + private Map responseError(ServerRequest request, Throwable error) { + String errorMessage = buildMessage(request, error); + int httpStatus = getHttpStatus(error); Map map = new HashMap<>(); map.put("resp_code", 1); map.put("resp_msg", errorMessage); map.put("datas", null); + map.put("httpStatus", httpStatus); return map; } + + private int getHttpStatus(Throwable error) { + int httpStatus; + if (error instanceof InvalidTokenException) { + InvalidTokenException ex = (InvalidTokenException)error; + httpStatus = ex.getHttpErrorCode(); + } else { + httpStatus = HttpStatus.INTERNAL_SERVER_ERROR.value(); + } + return httpStatus; + } } diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/feign/AsynMenuService.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/feign/AsynMenuService.java new file mode 100644 index 00000000..b6577abd --- /dev/null +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/feign/AsynMenuService.java @@ -0,0 +1,35 @@ +package com.central.gateway.feign; + +import com.central.common.model.SysMenu; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.AsyncResult; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; +import java.util.concurrent.Future; + +/** + * 异步Menu服务 + * + * @author zlt + * @version 1.0 + * @date 2021/8/8 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Component +public class AsynMenuService { + @Lazy + @Resource + private MenuService menuService; + + @Async + public Future> findByRoleCodes(String roleCodes) { + List result = menuService.findByRoleCodes(roleCodes); + return new AsyncResult<>(result); + } +} diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/feign/fallback/MenuServiceFallbackFactory.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/feign/fallback/MenuServiceFallbackFactory.java index 90aaa50d..840ae68a 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/feign/fallback/MenuServiceFallbackFactory.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/feign/fallback/MenuServiceFallbackFactory.java @@ -2,7 +2,7 @@ import cn.hutool.core.collection.CollectionUtil; import com.central.gateway.feign.MenuService; -import feign.hystrix.FallbackFactory; +import org.springframework.cloud.openfeign.FallbackFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/LbIsolationFilter.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/LbIsolationFilter.java index 41c201c4..74f964d3 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/LbIsolationFilter.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/LbIsolationFilter.java @@ -6,9 +6,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; -import org.springframework.cloud.gateway.config.LoadBalancerProperties; +/*import org.springframework.cloud.gateway.config.LoadBalancerProperties; import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter; -import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;*/ import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; @@ -22,9 +22,10 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ +/* @Component @ConditionalOnProperty(name = ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED, havingValue = "true") public class LbIsolationFilter extends LoadBalancerClientFilter { @@ -45,3 +46,4 @@ protected ServiceInstance choose(ServerWebExchange exchange) { return super.choose(exchange); } } +*/ diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/RequestStatisticsFilter.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/RequestStatisticsFilter.java index b2f21e4c..ce3e88cb 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/RequestStatisticsFilter.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/RequestStatisticsFilter.java @@ -20,7 +20,7 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Component diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/TraceFilter.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/TraceFilter.java index 9f77dee9..89bf0f0b 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/TraceFilter.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/TraceFilter.java @@ -1,9 +1,7 @@ package com.central.gateway.filter; -import cn.hutool.core.util.IdUtil; -import com.central.common.constant.CommonConstant; import com.central.log.properties.TraceProperties; -import org.slf4j.MDC; +import com.central.log.trace.MDCTraceUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; @@ -19,7 +17,7 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Component @@ -31,10 +29,13 @@ public class TraceFilter implements GlobalFilter, Ordered { public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { if (traceProperties.getEnable()) { //链路追踪id - String traceId = IdUtil.fastSimpleUUID(); - MDC.put(CommonConstant.LOG_TRACE_ID, traceId); + MDCTraceUtils.addTrace(); + ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate() - .headers(h -> h.add(CommonConstant.TRACE_ID_HEADER, traceId)) + .headers(h -> { + h.add(MDCTraceUtils.TRACE_ID_HEADER, MDCTraceUtils.getTraceId()); + h.add(MDCTraceUtils.SPAN_ID_HEADER, MDCTraceUtils.getNextSpanId()); + }) .build(); ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build(); diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/VersionLbIsolationFilter.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/VersionLbIsolationFilter.java new file mode 100644 index 00000000..fab1c00a --- /dev/null +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/filter/VersionLbIsolationFilter.java @@ -0,0 +1,45 @@ +package com.central.gateway.filter; + +import com.central.common.constant.CommonConstant; +import com.central.common.constant.ConfigConstants; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.List; + +/** + * 示例 + * + * @author jarvis create by 2022/5/8 + */ +@Component +public class VersionLbIsolationFilter implements GlobalFilter, Ordered { + + @Value("${"+ ConfigConstants.CONFIG_LOADBALANCE_ISOLATION_ENABLE+":}") + private Boolean enableVersionControl; + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + if(Boolean.TRUE.equals(enableVersionControl) + && exchange.getRequest().getQueryParams().containsKey(CommonConstant.Z_L_T_VERSION)){ + String version = exchange.getRequest().getQueryParams().get(CommonConstant.Z_L_T_VERSION).get(0); + ServerHttpRequest rebuildRequest = exchange.getRequest().mutate().headers(header -> { + header.add(CommonConstant.Z_L_T_VERSION, version); + }).build(); + ServerWebExchange rebuildServerWebExchange = exchange.mutate().request(rebuildRequest).build(); + return chain.filter(rebuildServerWebExchange); + } + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } +} diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/route/NacosRouteDefinitionRepository.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/route/NacosRouteDefinitionRepository.java index 1d3b536b..155fc18c 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/route/NacosRouteDefinitionRepository.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/route/NacosRouteDefinitionRepository.java @@ -3,9 +3,9 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.cloud.nacos.NacosConfigProperties; -import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; +import com.central.common.utils.JsonUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.route.RouteDefinition; @@ -24,7 +24,7 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j @@ -87,7 +87,7 @@ public Mono delete(Mono routeId) { private List getListByStr(String content) { if (StrUtil.isNotEmpty(content)) { - return JSONObject.parseArray(content, RouteDefinition.class); + return JsonUtil.toList(content, RouteDefinition.class); } return new ArrayList<>(0); } diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerAggProperties.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerAggProperties.java index 1e4c1084..df7762d8 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerAggProperties.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerAggProperties.java @@ -14,7 +14,7 @@ * @author zlt * @date 2019/10/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Setter diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerHandler.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerHandler.java index 34cec884..f9be8322 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerHandler.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerHandler.java @@ -21,7 +21,7 @@ * @author zlt * @date 2019/10/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @RestController diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerProvider.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerProvider.java index 04acef57..5156a077 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerProvider.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/swagger/SwaggerProvider.java @@ -21,7 +21,7 @@ * @author zlt * @date 2019/10/5 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Component diff --git a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/utils/ReactiveAddrUtil.java b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/utils/ReactiveAddrUtil.java index ca11fd96..181d8ac5 100644 --- a/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/utils/ReactiveAddrUtil.java +++ b/zlt-gateway/sc-gateway/src/main/java/com/central/gateway/utils/ReactiveAddrUtil.java @@ -12,7 +12,7 @@ * @author zlt * @date 2019/10/7 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j diff --git a/zlt-gateway/sc-gateway/src/main/resources/application.yml b/zlt-gateway/sc-gateway/src/main/resources/application.yml index 28080a1d..f7f69bf3 100644 --- a/zlt-gateway/sc-gateway/src/main/resources/application.yml +++ b/zlt-gateway/sc-gateway/src/main/resources/application.yml @@ -75,7 +75,7 @@ spring: - StripPrefix=1 # sentinel: # datasource.ds1.nacos: -# server-addr: ${zlt.nacos.server-addr} +# server-addr: ${spring.cloud.nacos.server-addr} # data-id: ${spring.application.name}-sentinel-gw-flow # group-id: DEFAULT_GROUP # ruleType: gw-flow @@ -98,9 +98,9 @@ zlt: /api-uaa/css/**, /api-uaa/images/**, /api-uaa/js/**, - /api-user/users-anon/**, /login.html, - /user/login + /user/login, + /api-uaa/tokens/key auth: urlPermission: #是否开启url级别权限 diff --git a/zlt-gateway/zuul-gateway/pom.xml b/zlt-gateway/zuul-gateway/pom.xml deleted file mode 100644 index 3c35f793..00000000 --- a/zlt-gateway/zuul-gateway/pom.xml +++ /dev/null @@ -1,155 +0,0 @@ - - 4.0.0 - - com.zlt - zlt-gateway - 3.5.0 - - zuul-gateway - zuul网关 - - - - - com.zlt - zlt-config - - - com.zlt - zlt-auth-client-spring-boot-starter - - - com.zlt - zlt-ribbon-spring-boot-starter - - - com.zlt - zlt-sentinel-spring-boot-starter - - - com.zlt - zlt-redis-spring-boot-starter - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-tomcat - - - - - - org.springframework.boot - spring-boot-starter-undertow - - - - org.springframework - spring-context-support - - - org.springframework.boot - spring-boot-starter-actuator - - - - com.github.zlt2000 - swagger-butler-core - - - - com.github.xiaoymin - swagger-bootstrap-ui - - - - com.zyplayer - swagger-mg-ui - - - - org.springframework.cloud - spring-cloud-starter-netflix-zuul - - - com.baomidou - mybatis-plus-extension - - - - com.squareup.okhttp3 - okhttp - - - - io.micrometer - micrometer-registry-prometheus - - - eu.bitwalker - UserAgentUtils - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - repackage - - - - - - com.spotify - docker-maven-plugin - - ${docker.image.prefix}/${project.artifactId} - - ${project.version} - latest - - true - ${docker.baseImage} - ${docker.volumes} - - ${docker.java.opts} - - ["sh","-c","java $JAVA_OPTS ${docker.java.security.egd} -jar /${project.build.finalName}.jar"] - - - / - ${project.build.directory} - ${project.build.finalName}.jar - - - - - - ${project.artifactId} - - \ No newline at end of file diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/GatewayApp.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/GatewayApp.java deleted file mode 100644 index 7fe04b5f..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/GatewayApp.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.central; - -import com.central.common.ribbon.annotation.EnableBaseFeignInterceptor; -import com.central.common.ribbon.annotation.EnableFeignInterceptor; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.cloud.netflix.zuul.EnableZuulProxy; -import org.springframework.cloud.openfeign.EnableFeignClients; - -/** - * @author zlt - */ -@EnableZuulProxy -@EnableFeignClients -@EnableBaseFeignInterceptor -@EnableDiscoveryClient -@SpringBootApplication -public class GatewayApp { - public static void main(String[] args) { - SpringApplication.run(GatewayApp.class, args); - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/CorsConfigration.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/CorsConfigration.java deleted file mode 100644 index 2f2c0829..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/CorsConfigration.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.central.gateway.config; - -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -/** - * 跨域配置 - * - * @author zlt - * @date 2018/8/9 15:59 - */ -@Configuration -public class CorsConfigration { - - @Bean - public CorsFilter corsFilter() { - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - CorsConfiguration config = new CorsConfiguration(); - // #允许向该服务器提交请求的URI,*表示全部允许 - config.addAllowedOrigin(CorsConfiguration.ALL); - // 允许cookies跨域 - config.setAllowCredentials(true); - // #允许访问的头信息,*表示全部 - config.addAllowedHeader(CorsConfiguration.ALL); - // 允许提交请求的方法,*表示全部允许 - config.addAllowedMethod(CorsConfiguration.ALL); - source.registerCorsConfiguration("/**", config); - return new CorsFilter(source); - } - - /** - * 配置过滤器 - */ - @Bean - public FilterRegistrationBean someFilterRegistration() { - FilterRegistrationBean registration = new FilterRegistrationBean<>(); - registration.setFilter(corsFilter()); - registration.addUrlPatterns("/*"); - registration.setName("corsFilter"); - registration.setOrder(Ordered.HIGHEST_PRECEDENCE); - return registration; - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/DynamicZuulRouteConfig.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/DynamicZuulRouteConfig.java deleted file mode 100644 index 42c76e42..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/DynamicZuulRouteConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.central.gateway.config; - -import com.alibaba.cloud.nacos.NacosConfigProperties; -import com.central.gateway.route.nacos.NacosDynRouteLocator; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; -import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * 动态路由配置 - * - * @author zlt - * @date 2019/8/21 - */ -@Configuration -@ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "enabled", havingValue = "true") -public class DynamicZuulRouteConfig { - @Autowired - private ZuulProperties zuulProperties; - - @Autowired - private DispatcherServletPath dispatcherServletPath; - - /** - * Nacos实现方式 - */ - @Configuration - @ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true) - public class NacosZuulRoute { - @Autowired - private NacosConfigProperties nacosConfigProperties; - - @Autowired - private ApplicationEventPublisher publisher; - - @Bean - public NacosDynRouteLocator nacosDynRouteLocator() { - return new NacosDynRouteLocator(nacosConfigProperties, publisher, dispatcherServletPath.getPrefix(), zuulProperties); - } - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/ResourceServerConfiguration.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/ResourceServerConfiguration.java deleted file mode 100644 index 58e217f6..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/ResourceServerConfiguration.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.central.gateway.config; - -import com.central.common.config.DefaultPasswordConfig; -import com.central.oauth2.common.config.DefaultResourceServerConf; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; - -/** - * @author zlt - */ -@Configuration -@EnableResourceServer -@Import({DefaultPasswordConfig.class}) -public class ResourceServerConfiguration extends DefaultResourceServerConf { - @Override - public HttpSecurity setAuthenticate(ExpressionUrlAuthorizationConfigurer.AuthorizedUrl authorizedUrl) { - return authorizedUrl.access("@permissionService.hasPermission(request, authentication)").and(); - } -} \ No newline at end of file diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/SwaggerConfig.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/SwaggerConfig.java deleted file mode 100644 index d28318b5..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/SwaggerConfig.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.central.gateway.config; - -import com.didispace.swagger.butler.EnableSwaggerButler; -import org.springframework.context.annotation.Configuration; - -/** - * swagger 聚合文档配置 - * zuul routers 映射具体服务的/v2/api-docs swagger - * - * @author 作者 owen E-mail: 624191343@qq.com - */ -@Configuration -@EnableSwaggerButler -public class SwaggerConfig { - -} \ No newline at end of file diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/ZuulRouteEntity.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/ZuulRouteEntity.java deleted file mode 100644 index a4d9b32d..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/config/ZuulRouteEntity.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.central.gateway.config; - -import lombok.Getter; -import lombok.Setter; - -/** - * zuul路由实体 - * - * @author zlt - * @date 2019/8/21 - */ -@Setter -@Getter -public class ZuulRouteEntity { - /** - * The ID of the route (the same as its map key by default). - */ - private String id; - /** - * The path (pattern) for the route, e.g. /foo/**. - */ - private String path; - /** - * The service ID (if any) to map to this route. You can specify a - * physical URL or a service, but not both. - */ - private String serviceId; - /** - * A full physical URL to map to the route. An alternative is to use a - * service ID and service discovery to find the physical address. - */ - private String url; - /** - * Flag to determine whether the prefix for this route (the path, minus - * pattern patcher) should be stripped before forwarding. - */ - private boolean stripPrefix = true; - /** - * Flag to indicate that this route should be retryable (if supported). - * Generally retry requires a service ID and ribbon. - */ - private Boolean retryable; - - private String apiName; - - private boolean enabled = true; - - private boolean customSensitiveHeaders = true; -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/feign/MenuService.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/feign/MenuService.java deleted file mode 100644 index 032ed131..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/feign/MenuService.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.central.gateway.feign; - -import com.central.common.constant.ServiceNameConstants; -import com.central.common.model.SysMenu; -import com.central.gateway.feign.fallback.MenuServiceFallbackFactory; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; - -import java.util.List; - -/** - * @author zlt - */ -@FeignClient(name = ServiceNameConstants.USER_SERVICE, fallbackFactory = MenuServiceFallbackFactory.class, decode404 = true) -public interface MenuService { - /** - * 角色菜单列表 - * @param roleCodes - */ - @GetMapping(value = "/menus/{roleCodes}") - List findByRoleCodes(@PathVariable("roleCodes") String roleCodes); -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/feign/fallback/MenuServiceFallbackFactory.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/feign/fallback/MenuServiceFallbackFactory.java deleted file mode 100644 index 7cba08ab..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/feign/fallback/MenuServiceFallbackFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.central.gateway.feign.fallback; - -import cn.hutool.core.collection.CollectionUtil; -import com.central.gateway.feign.MenuService; -import feign.hystrix.FallbackFactory; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * menuService降级工场 - * - * @author zlt - * @date 2019/1/18 - */ -@Slf4j -@Component -public class MenuServiceFallbackFactory implements FallbackFactory { - @Override - public MenuService create(Throwable throwable) { - return roleIds -> { - log.error("调用findByRoleCodes异常:{}", roleIds, throwable); - return CollectionUtil.newArrayList(); - }; - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/RequestStatisticsFilter.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/RequestStatisticsFilter.java deleted file mode 100644 index 72c60b45..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/RequestStatisticsFilter.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.central.gateway.filter.pre; - -import cn.hutool.core.util.StrUtil; -import com.central.common.utils.AddrUtil; -import com.central.log.monitor.PointUtil; -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; -import eu.bitwalker.useragentutils.UserAgent; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; - -/** - * 请求统计分析埋点过滤器 - * - * @author zlt - * @date 2019/5/6 - */ -@Slf4j -@Component -public class RequestStatisticsFilter extends ZuulFilter { - @Override - public String filterType() { - return FilterConstants.PRE_TYPE; - } - - @Override - public int filterOrder() { - return 0; - } - - @Override - public boolean shouldFilter() { - return true; - } - - @Override - public Object run() { - RequestContext ctx = RequestContext.getCurrentContext(); - HttpServletRequest req = ctx.getRequest(); - UserAgent userAgent = UserAgent.parseUserAgentString(req.getHeader("User-Agent")); - - //埋点 - PointUtil.debug("0", "request-statistics", - "ip=" + AddrUtil.getRemoteAddr(req) - + "&browser=" + getBrowser(userAgent.getBrowser().getName()) - + "&operatingSystem=" + getOperatingSystem(userAgent.getOperatingSystem().getName())); - - return null; - } - - private String getBrowser(String browser) { - if (StrUtil.isNotEmpty(browser)) { - if (browser.contains("CHROME")) { - return "CHROME"; - } else if (browser.contains("FIREFOX")) { - return "FIREFOX"; - } else if (browser.contains("SAFARI")) { - return "SAFARI"; - } else if (browser.contains("EDGE")) { - return "EDGE"; - } - } - return browser; - } - - private String getOperatingSystem(String operatingSystem) { - if (StrUtil.isNotEmpty(operatingSystem)) { - if (operatingSystem.contains("MAC_OS_X")) { - return "MAC_OS_X"; - } else if (operatingSystem.contains("ANDROID")) { - return "ANDROID"; - } - } - return operatingSystem; - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/TraceFilter.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/TraceFilter.java deleted file mode 100644 index 2739492e..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/TraceFilter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.central.gateway.filter.pre; - -import cn.hutool.core.util.IdUtil; -import com.central.common.constant.CommonConstant; -import com.central.log.properties.TraceProperties; -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; -import org.slf4j.MDC; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; -import org.springframework.stereotype.Component; - -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.FORM_BODY_WRAPPER_FILTER_ORDER; - -/** - * 生成日志链路追踪id,并传入header中 - * - * @author zlt - * @date 2019/8/13 - */ -@Component -public class TraceFilter extends ZuulFilter { - @Autowired - private TraceProperties traceProperties; - - @Override - public String filterType() { - return FilterConstants.PRE_TYPE; - } - - @Override - public int filterOrder() { - return FORM_BODY_WRAPPER_FILTER_ORDER - 1; - } - - @Override - public boolean shouldFilter() { - //根据配置控制是否开启过滤器 - return traceProperties.getEnable(); - } - - @Override - public Object run() { - //链路追踪id - String traceId = IdUtil.fastSimpleUUID(); - MDC.put(CommonConstant.LOG_TRACE_ID, traceId); - RequestContext ctx = RequestContext.getCurrentContext(); - ctx.addZuulRequestHeader(CommonConstant.TRACE_ID_HEADER, traceId); - return null; - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/UserInfoHeaderFilter.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/UserInfoHeaderFilter.java deleted file mode 100644 index 731a907d..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/UserInfoHeaderFilter.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.central.gateway.filter.pre; - -import cn.hutool.core.collection.CollectionUtil; -import com.central.common.constant.SecurityConstants; -import com.central.common.model.SysUser; -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; -import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.stereotype.Component; - -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.FORM_BODY_WRAPPER_FILTER_ORDER; - -/** - * 将认证用户的相关信息放入header中, 后端服务可以直接读取使用 - * - * @author zlt - * @date 2018/11/20 - */ -@Component -public class UserInfoHeaderFilter extends ZuulFilter { - @Override - public String filterType() { - return FilterConstants.PRE_TYPE; - } - - @Override - public int filterOrder() { - return FORM_BODY_WRAPPER_FILTER_ORDER - 1; - } - - @Override - public boolean shouldFilter() { - return true; - } - - @Override - public Object run() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken)) { - Object principal = authentication.getPrincipal(); - RequestContext ctx = RequestContext.getCurrentContext(); - //客户端模式只返回一个clientId - if (principal instanceof SysUser) { - SysUser user = (SysUser)authentication.getPrincipal(); - ctx.addZuulRequestHeader(SecurityConstants.USER_ID_HEADER, String.valueOf(user.getId())); - ctx.addZuulRequestHeader(SecurityConstants.USER_HEADER, user.getUsername()); - } - OAuth2Authentication oauth2Authentication = (OAuth2Authentication)authentication; - String clientId = oauth2Authentication.getOAuth2Request().getClientId(); - ctx.addZuulRequestHeader(SecurityConstants.TENANT_HEADER, clientId); - ctx.addZuulRequestHeader(SecurityConstants.ROLE_HEADER, CollectionUtil.join(authentication.getAuthorities(), ",")); - } - return null; - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/route/AbstractDynRouteLocator.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/route/AbstractDynRouteLocator.java deleted file mode 100644 index c1dbcee7..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/route/AbstractDynRouteLocator.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.central.gateway.route; - -import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator; -import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator; -import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; -import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute; -import org.springframework.util.StringUtils; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * 扩展 {@link SimpleRouteLocator} 实现动态路由 - * - * @author zlt - * @date 2019/8/21 - */ -public abstract class AbstractDynRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator { - private ZuulProperties properties; - - public AbstractDynRouteLocator(String servletPath, ZuulProperties properties) { - super(servletPath, properties); - this.properties = properties; - } - - /** - * 刷新路由 - */ - @Override - public void refresh() { - doRefresh(); - } - - @Override - protected Map locateRoutes() { - LinkedHashMap routesMap = new LinkedHashMap<>(); - // 从application.properties中加载静态路由信息 - routesMap.putAll(super.locateRoutes()); - // 从数据源中加载动态路由信息 - routesMap.putAll(loadDynamicRoute()); - // 优化一下配置 - LinkedHashMap values = new LinkedHashMap<>(); - for (Map.Entry entry : routesMap.entrySet()) { - String path = entry.getKey(); - // Prepend with slash if not already present. - if (!path.startsWith("/")) { - path = "/" + path; - } - if (StringUtils.hasText(this.properties.getPrefix())) { - path = this.properties.getPrefix() + path; - if (!path.startsWith("/")) { - path = "/" + path; - } - } - values.put(path, entry.getValue()); - } - return values; - } - - /** - * 加载路由配置,由子类去实现 - */ - public abstract Map loadDynamicRoute(); -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/route/nacos/NacosDynRouteLocator.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/route/nacos/NacosDynRouteLocator.java deleted file mode 100644 index fb723a7d..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/route/nacos/NacosDynRouteLocator.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.central.gateway.route.nacos; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.util.StrUtil; -import com.alibaba.cloud.nacos.NacosConfigProperties; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.nacos.api.config.listener.Listener; -import com.alibaba.nacos.api.exception.NacosException; -import com.central.gateway.config.ZuulRouteEntity; -import com.central.gateway.route.AbstractDynRouteLocator; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent; -import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; -import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute; -import org.springframework.context.ApplicationEventPublisher; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; - -/** - * Nacos动态路由实现类 - * - * @author zlt - * @date 2019/8/22 - */ -@Slf4j -public class NacosDynRouteLocator extends AbstractDynRouteLocator { - private static final String ZUUL_DATA_ID = "zuul-routes"; - private static final String ZUUL_GROUP_ID = "ZUUL_GATEWAY"; - - private NacosConfigProperties nacosConfigProperties; - - private ApplicationEventPublisher publisher; - - private NacosDynRouteLocator locator; - - @Setter - private List zuulRouteEntities; - - public NacosDynRouteLocator(NacosConfigProperties nacosConfigProperties, ApplicationEventPublisher publisher, - String servletPath, ZuulProperties properties) { - super(servletPath, properties); - this.nacosConfigProperties = nacosConfigProperties; - this.publisher = publisher; - this.locator = this; - addListener(); - } - - /** - * 添加Nacos监听 - */ - private void addListener() { - try { - nacosConfigProperties.configServiceInstance().addListener(ZUUL_DATA_ID, ZUUL_GROUP_ID, new Listener() { - @Override - public Executor getExecutor() { - return null; - } - - @Override - public void receiveConfigInfo(String configInfo) { - //赋值路由信息 - locator.setZuulRouteEntities(getListByStr(configInfo)); - RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(locator); - publisher.publishEvent(routesRefreshedEvent); - } - }); - } catch (NacosException e) { - log.error("nacos-addListener-error", e); - } - } - - @Override - public Map loadDynamicRoute() { - Map routes = new LinkedHashMap<>(); - if (zuulRouteEntities == null) { - zuulRouteEntities = getNacosConfig(); - } - for (ZuulRouteEntity result : zuulRouteEntities) { - if (StrUtil.isBlank(result.getPath()) || !result.isEnabled()) { - continue; - } - ZuulRoute zuulRoute = new ZuulRoute(); - BeanUtil.copyProperties(result, zuulRoute); - routes.put(zuulRoute.getPath(), zuulRoute); - } - return routes; - } - - /** - * 查询zuul的路由配置 - */ - private List getNacosConfig() { - try { - String content = nacosConfigProperties.configServiceInstance().getConfig(ZUUL_DATA_ID, ZUUL_GROUP_ID,5000); - return getListByStr(content); - } catch (NacosException e) { - log.error("listenerNacos-error", e); - } - return new ArrayList<>(0); - } - - public List getListByStr(String content) { - if (StrUtil.isNotEmpty(content)) { - return JSONObject.parseArray(content, ZuulRouteEntity.class); - } - return new ArrayList<>(0); - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/service/impl/PermissionServiceImpl.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/service/impl/PermissionServiceImpl.java deleted file mode 100644 index 422e6e1f..00000000 --- a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/service/impl/PermissionServiceImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.central.gateway.service.impl; - -import com.central.common.model.SysMenu; -import com.central.gateway.feign.MenuService; -import com.central.oauth2.common.service.impl.DefaultPermissionServiceImpl; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import java.util.List; - -/** - * 请求权限判断service - * - * @author zlt - * @date 2018/10/28 - */ -@Slf4j -@Service("permissionService") -public class PermissionServiceImpl extends DefaultPermissionServiceImpl { - @Resource - private MenuService menuService; - - @Override - public List findMenuByRoleCodes(String roleCodes) { - return menuService.findByRoleCodes(roleCodes); - } - - public boolean hasPermission(HttpServletRequest request, Authentication authentication) { - return hasPermission(authentication, request.getMethod(), request.getRequestURI()); - } -} diff --git a/zlt-gateway/zuul-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/zlt-gateway/zuul-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json deleted file mode 100644 index efd00b85..00000000 --- a/zlt-gateway/zuul-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "properties": [ - { - "name": "zlt.gateway.dynamicRoute.enabled", - "description": "是否开启动态路由.", - "type": "java.lang.Boolean", - "defaultValue": false - }, - { - "name": "zlt.gateway.dynamicRoute.dataType", - "description": "动态路由的数据类型.", - "type": "java.lang.String", - "defaultValue": "nacos" - } - ] -} \ No newline at end of file diff --git a/zlt-gateway/zuul-gateway/src/main/resources/application.yml b/zlt-gateway/zuul-gateway/src/main/resources/application.yml deleted file mode 100644 index e2a06e3d..00000000 --- a/zlt-gateway/zuul-gateway/src/main/resources/application.yml +++ /dev/null @@ -1,169 +0,0 @@ -#spring: - # sentinel动态配置规则 -# cloud: -# sentinel: -# zuul: -# enabled: true -# order: -# pre: 2000 -# post: 500 -# error: -100 -# filter: -# enabled: false -# datasource: - # 限流 -# ds1: -# nacos: -# server-addr: ${zlt.nacos.server-addr} -# dataId: ${spring.application.name}-sentinel-gw-flow -# groupId: DEFAULT_GROUP -# rule-type: gw-flow - # api分组 -# ds2: -# nacos: -# server-addr: ${zlt.nacos.server-addr} -# dataId: ${spring.application.name}-sentinel-gw-api-group -# groupId: DEFAULT_GROUP -# rule-type: gw-api-group - -zlt: - oauth2: - token: - store: - type: redis - security: - ignore: - # 忽略认证的地址 - httpUrls: > - /api-uaa/oauth/**, - /api-uaa/validata/**, - /api-uaa/css/**, - /api-uaa/images/**, - /api-uaa/js/**, - /api-user/users-anon/**, - /login.html, - /user/login - auth: - urlPermission: - #是否开启url级别权限 - enable: false - #配置只进行登录认证,不进行url权限认证的api - ignoreUrls: > - /api-user/menus/current, - /api-user/users/current, - /api-log/requestStat - #白名单 - includeClientIds: - - webApp - renew: - #是否开启token自动续签(目前只有redis实现) - enable: true - #白名单 - includeClientIds: - - webApp - gateway: - #网关动态路由 - dynamicRoute: - enabled: true - - -zuul: - ribbon-isolation-strategy: thread - ribbon: - eager-load: - enabled: true - threadPool: - useSeparateThreadPools: true - threadPoolKeyPrefix: api-gateway - #关闭重试 - retryable: false - ignored-headers: Access-Control-Allow-Credentials,Access-Control-Allow-Origin,Access-Control-Allow-Methods - add-host-header: true - routes: - auth: - path: /api-uaa/** - service-id: uaa-server - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - auth-login-page: - path: /login.html - service-id: uaa-server - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - auth-login-process: - path: /user/login - service-id: uaa-server - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - auth-login-token: - path: /oauth/token - service-id: uaa-server - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - auth-login-authorize: - path: /oauth/authorize - service-id: uaa-server - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - auth-check-process: - path: /oauth/check_token - service-id: uaa-server - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - user: - path: /api-user/** - service-id: user-center - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - file: - path: /api-file/** - service-id: file-center - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - log: - path: /api-log/** - service-id: log-center - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - generator: - path: /api-generator/** - service-id: code-generator - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - search: - path: /api-search/** - service-id: search-center - strip-prefix: true - sensitive-headers: '*' - custom-sensitive-headers: true - ssl-hostname-validation-enabled: false - -hystrix: - threadpool: - default: - coreSize: 100 - maximumSize: 2000 - allowMaximumSizeToDivergeFromCoreSize: true - maxQueueSize: -1 - command: - default: - execution: - isolation: - thread: - timeoutInMilliseconds: 60000 - -swagger: - butler: - auto-generate-from-zuul-routes: true - generate-routes: user,file - apiDocsPath: v2/api-docs \ No newline at end of file diff --git a/zlt-gateway/zuul-gateway/src/main/resources/bootstrap.yml b/zlt-gateway/zuul-gateway/src/main/resources/bootstrap.yml deleted file mode 100644 index b64a9d42..00000000 --- a/zlt-gateway/zuul-gateway/src/main/resources/bootstrap.yml +++ /dev/null @@ -1,9 +0,0 @@ -server: - port: 9900 - undertow: - buffer-size: 1024 - direct-buffers: true - -spring: - application: - name: api-gateway \ No newline at end of file diff --git a/zlt-gateway/zuul-gateway/src/main/resources/pubkey.txt b/zlt-gateway/zuul-gateway/src/main/resources/pubkey.txt deleted file mode 100644 index 0c1d7c8d..00000000 --- a/zlt-gateway/zuul-gateway/src/main/resources/pubkey.txt +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjmgfejBXsPYynAIPczHA -eEvTDfAVaNKQudyI7VszdezbHDM1CStCIgwiMmLd7QYf1SrrmQoqxhcSRbhjE3ej -RF5qzhtx3kmepdpMrQptcsLjRkixaxCc4E2k6Us5707gGwbhoaTrRit5F2MnAdLY -C1TS3WwnO/hQfqUcAglbK8yrJ4AwAv0DAoIUSWnWqzuniV1SYbdV57uswxUssoWy -sEfPz+nv1ZLRs6Wz4eQ5Myqx2+CjWc9F8iXa2PV8Rmjms3dVbWcLUpCP18Dfzp8l -n8vF9LfYB7UaLSpfJe6FFF6+vCg4JHfo12djTUgwGjauMF3e9mmjU83KIoQS66lp -AQIDAQAB ------END PUBLIC KEY----- \ No newline at end of file diff --git a/zlt-job/job-admin/pom.xml b/zlt-job/job-admin/pom.xml index 15803174..ae520d5a 100644 --- a/zlt-job/job-admin/pom.xml +++ b/zlt-job/job-admin/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-job - 3.5.0 + 5.4.0 job-admin jar diff --git a/zlt-job/job-core/pom.xml b/zlt-job/job-core/pom.xml index 421c53ea..e97b0bf2 100644 --- a/zlt-job/job-core/pom.xml +++ b/zlt-job/job-core/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-job - 3.5.0 + 5.4.0 job-core jar diff --git a/zlt-job/job-core/src/main/java/com/xxl/job/core/log/XxlJobFileAppender.java b/zlt-job/job-core/src/main/java/com/xxl/job/core/log/XxlJobFileAppender.java index ff89001a..e86bc7b9 100644 --- a/zlt-job/job-core/src/main/java/com/xxl/job/core/log/XxlJobFileAppender.java +++ b/zlt-job/job-core/src/main/java/com/xxl/job/core/log/XxlJobFileAppender.java @@ -200,14 +200,12 @@ public static String readLines(File logFile){ BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(logFile), "utf-8")); - if (reader != null) { - StringBuilder sb = new StringBuilder(); - String line = null; - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); - } - return sb.toString(); + StringBuilder sb = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); } + return sb.toString(); } catch (IOException e) { logger.error(e.getMessage(), e); } finally { diff --git a/zlt-job/job-core/src/main/java/com/xxl/job/core/thread/TriggerCallbackThread.java b/zlt-job/job-core/src/main/java/com/xxl/job/core/thread/TriggerCallbackThread.java index 9fba156d..225a9e91 100644 --- a/zlt-job/job-core/src/main/java/com/xxl/job/core/thread/TriggerCallbackThread.java +++ b/zlt-job/job-core/src/main/java/com/xxl/job/core/thread/TriggerCallbackThread.java @@ -215,7 +215,7 @@ private void retryFailCallbackFile(){ } // retry callback, 100 lines per page - if (failCallbackParamList!=null && failCallbackParamList.size()>0) { + if (failCallbackParamList.size()>0) { int pagesize = 100; List pageData = new ArrayList<>(); for (int i = 0; i < failCallbackParamList.size(); i++) { diff --git a/zlt-job/job-executor-samples/pom.xml b/zlt-job/job-executor-samples/pom.xml index d6eeae3a..b0a91ce9 100644 --- a/zlt-job/job-executor-samples/pom.xml +++ b/zlt-job/job-executor-samples/pom.xml @@ -6,7 +6,7 @@ com.zlt zlt-job - 3.5.0 + 5.4.0 job-executor-samples jar diff --git a/zlt-job/pom.xml b/zlt-job/pom.xml index 061dbdae..06202f4c 100644 --- a/zlt-job/pom.xml +++ b/zlt-job/pom.xml @@ -4,7 +4,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-job pom diff --git a/zlt-monitor/log-center/pom.xml b/zlt-monitor/log-center/pom.xml index 68d64bf7..8f5ae3fb 100644 --- a/zlt-monitor/log-center/pom.xml +++ b/zlt-monitor/log-center/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-monitor - 3.5.0 + 5.4.0 log-center @@ -19,7 +19,7 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter @@ -47,6 +47,11 @@ io.micrometer micrometer-registry-prometheus + + + org.hibernate + hibernate-validator + diff --git a/zlt-monitor/log-center/src/main/java/com/central/log/controller/AuditLogController.java b/zlt-monitor/log-center/src/main/java/com/central/log/controller/AuditLogController.java index 85a46e80..1b03ce25 100644 --- a/zlt-monitor/log-center/src/main/java/com/central/log/controller/AuditLogController.java +++ b/zlt-monitor/log-center/src/main/java/com/central/log/controller/AuditLogController.java @@ -1,9 +1,9 @@ package com.central.log.controller; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; import com.central.search.client.service.IQueryService; import com.central.search.model.SearchDto; +import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; @@ -16,7 +16,7 @@ * @author zlt * @date 2020/2/4 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @RestController @@ -34,7 +34,7 @@ public AuditLogController(IQueryService queryService) { @ApiImplicitParam(name = "queryStr", value = "搜索关键字", dataType = "String") }) @GetMapping(value = "/auditLog") - public PageResult getPage(SearchDto searchDto) { + public PageResult getPage(SearchDto searchDto) { searchDto.setIsHighlighter(true); searchDto.setSortCol("timestamp"); return queryService.strQuery("audit-log-*", searchDto); diff --git a/zlt-monitor/log-center/src/main/java/com/central/log/controller/SlowQueryLogController.java b/zlt-monitor/log-center/src/main/java/com/central/log/controller/SlowQueryLogController.java index edaf6455..99d5a3f6 100644 --- a/zlt-monitor/log-center/src/main/java/com/central/log/controller/SlowQueryLogController.java +++ b/zlt-monitor/log-center/src/main/java/com/central/log/controller/SlowQueryLogController.java @@ -1,9 +1,9 @@ package com.central.log.controller; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; import com.central.search.client.service.IQueryService; import com.central.search.model.SearchDto; +import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; @@ -28,7 +28,7 @@ public class SlowQueryLogController { @ApiImplicitParam(name = "queryStr", value = "搜索关键字", dataType = "String") }) @GetMapping(value = "/slowQueryLog") - public PageResult getPage(SearchDto searchDto) { + public PageResult getPage(SearchDto searchDto) { searchDto.setIsHighlighter(true); searchDto.setSortCol("timestamp"); return queryService.strQuery("mysql-slowlog-*", searchDto); diff --git a/zlt-monitor/log-center/src/main/java/com/central/log/controller/SysLogController.java b/zlt-monitor/log-center/src/main/java/com/central/log/controller/SysLogController.java index 3893ac7f..1a483ee2 100644 --- a/zlt-monitor/log-center/src/main/java/com/central/log/controller/SysLogController.java +++ b/zlt-monitor/log-center/src/main/java/com/central/log/controller/SysLogController.java @@ -1,16 +1,20 @@ package com.central.log.controller; -import com.alibaba.fastjson.JSONObject; import com.central.common.model.PageResult; +import com.central.log.model.TraceLog; +import com.central.log.service.TraceLogService; import com.central.search.client.service.IQueryService; import com.central.search.model.SearchDto; +import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.Resource; +import java.util.*; + /** * 系统日志 * @@ -18,19 +22,48 @@ */ @RestController public class SysLogController { - @Autowired + /** + * 系统日志索引名 + */ + private static final String SYS_LOG_INDEXNAME = "sys-log-*"; + + @Resource private IQueryService queryService; + @Resource + private TraceLogService traceLogService; + @ApiOperation(value = "系统日志全文搜索列表") @ApiImplicitParams({ @ApiImplicitParam(name = "page", value = "分页起始位置", required = true, dataType = "Integer"), @ApiImplicitParam(name = "limit", value = "分页结束位置", required = true, dataType = "Integer"), - @ApiImplicitParam(name = "queryStr", value = "搜索关键字", dataType = "String") + @ApiImplicitParam(name = "queryStr", value = "搜索关键字", dataType = "String"), + @ApiImplicitParam(name = "sortCol", value = "排序字段", dataType = "String"), + @ApiImplicitParam(name = "sortOrder", value = "排序顺序", dataType = "String"), + @ApiImplicitParam(name = "isHighlighter", value = "是否显示高亮", dataType = "String") }) @GetMapping(value = "/sysLog") - public PageResult getPage(SearchDto searchDto) { - searchDto.setIsHighlighter(true); - searchDto.setSortCol("timestamp"); - return queryService.strQuery("sys-log-*", searchDto); + public PageResult sysLog(SearchDto searchDto) { + return queryService.strQuery(SYS_LOG_INDEXNAME, searchDto); + } + + @ApiOperation(value = "系统日志链路列表") + @ApiImplicitParams({ + @ApiImplicitParam(name = "queryStr", value = "搜索关键字", dataType = "String"), + @ApiImplicitParam(name = "sortCol", value = "排序字段", dataType = "String"), + @ApiImplicitParam(name = "sortOrder", value = "排序顺序", dataType = "String"), + @ApiImplicitParam(name = "isHighlighter", value = "是否显示高亮", dataType = "String") + }) + @GetMapping(value = "/traceLog") + public PageResult traceLog(SearchDto searchDto) { + PageResult pageResult = queryService.strQuery(SYS_LOG_INDEXNAME, searchDto); + List jsonNodeList = pageResult.getData(); + List logList; + if (jsonNodeList != null) { + logList = traceLogService.transTraceLog(jsonNodeList); + } else { + logList = new ArrayList<>(0); + } + return PageResult.builder().data(logList).code(0).count((long) logList.size()).build(); } } diff --git a/zlt-monitor/log-center/src/main/java/com/central/log/model/TraceLog.java b/zlt-monitor/log-center/src/main/java/com/central/log/model/TraceLog.java new file mode 100644 index 00000000..ee508ed3 --- /dev/null +++ b/zlt-monitor/log-center/src/main/java/com/central/log/model/TraceLog.java @@ -0,0 +1,24 @@ +package com.central.log.model; + +import lombok.Getter; +import lombok.Setter; + +/** + * 日志链路对象 + * + * @author zlt + * @version 1.0 + * @date 2022/1/27 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +@Getter +public class TraceLog { + private String spanId; + private String parentId; + private String appName; + private String serverIp; + private String serverPort; +} diff --git a/zlt-monitor/log-center/src/main/java/com/central/log/service/TraceLogService.java b/zlt-monitor/log-center/src/main/java/com/central/log/service/TraceLogService.java new file mode 100644 index 00000000..f7c094d1 --- /dev/null +++ b/zlt-monitor/log-center/src/main/java/com/central/log/service/TraceLogService.java @@ -0,0 +1,61 @@ +package com.central.log.service; + +import cn.hutool.core.util.StrUtil; +import com.central.common.utils.JsonUtil; +import com.central.log.model.TraceLog; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 日志链路service + * + * @author zlt + * @version 1.0 + * @date 2022/1/27 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Service +public class TraceLogService { + public List transTraceLog(List jsonNodeList) { + List logList = new ArrayList<>(); + Set logSet = new HashSet<>(); + jsonNodeList.forEach(e -> { + TraceLog log = JsonUtil.toObject(e, TraceLog.class); + String spanId = log.getSpanId(); + if (StrUtil.isNotEmpty(spanId)) { + if (spanId.length() == 1) { + log.setParentId("-1"); + } else { + log.setParentId(spanId.substring(0, spanId.length() - 2)); + } + if (checkLog(genLogKey(log), logSet)) { + logList.add(log); + } + } + }); + return logList; + } + + /** + * 通过集合来去重 + */ + private boolean checkLog(String logKey, Set logSet) { + if (logSet.contains(logKey)) { + return false; + } else { + logSet.add(logKey); + return true; + } + } + + private String genLogKey(TraceLog log) { + return StrUtil.format("{}_{}_{}_{}", log.getAppName(), log.getSpanId(), log.getServerIp(), log.getServerPort()); + } +} diff --git a/zlt-monitor/pom.xml b/zlt-monitor/pom.xml index 27e5d7d9..b98b8f49 100644 --- a/zlt-monitor/pom.xml +++ b/zlt-monitor/pom.xml @@ -3,7 +3,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-monitor pom diff --git a/zlt-monitor/sc-admin/pom.xml b/zlt-monitor/sc-admin/pom.xml index aaa551c2..707ed0b3 100644 --- a/zlt-monitor/sc-admin/pom.xml +++ b/zlt-monitor/sc-admin/pom.xml @@ -4,7 +4,7 @@ com.zlt zlt-monitor - 3.5.0 + 5.4.0 sc-admin diff --git a/zlt-register/nacos/LICENSE b/zlt-register/nacos/LICENSE index 37261723..5be5ed21 100644 --- a/zlt-register/nacos/LICENSE +++ b/zlt-register/nacos/LICENSE @@ -245,24 +245,6 @@ The source code of slf4j can be found at https://github.com/qos-ch/slf4j. OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------- -This product has a bundle fastjson, which is available under the ASL2 License. -The source code of fastjson can be found at https://github.com/alibaba/fastjson. - - Copyright 1999-2016 Alibaba Group Holding Ltd. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ------ This product has a bundle javassist, which is available under the ASL2 License. The source code of javassist can be found at https://github.com/jboss-javassist/javassist. @@ -301,7 +283,7 @@ The source code of jna can be found at https://github.com/java-native-access/jna This product has a bundle guava, which is available under the ASL2 License. The source code of guava can be found at https://github.com/google/guava. - Copyright (C) 2007 The Guava authors + Copyright (C) 2007 The Guava authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/zlt-register/nacos/bin/shutdown.sh b/zlt-register/nacos/bin/shutdown.sh index 3df4a03e..e3e14fed 100644 --- a/zlt-register/nacos/bin/shutdown.sh +++ b/zlt-register/nacos/bin/shutdown.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Copyright 1999-2018 Alibaba Group Holding Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/zlt-register/nacos/bin/startup.cmd b/zlt-register/nacos/bin/startup.cmd index 8e79e6dd..c9910877 100644 --- a/zlt-register/nacos/bin/startup.cmd +++ b/zlt-register/nacos/bin/startup.cmd @@ -21,54 +21,75 @@ rem added double quotation marks to avoid the issue caused by the folder names c rem removed the last 5 chars(which means \bin\) to get the base DIR. set BASE_DIR="%BASE_DIR:~0,-5%" -set DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/" -set CUSTOM_SEARCH_LOCATIONS=%DEFAULT_SEARCH_LOCATIONS%,file:%BASE_DIR%/conf/ +set CUSTOM_SEARCH_LOCATIONS=file:%BASE_DIR%/conf/ -set MODE="standalone" +set MODE="cluster" set FUNCTION_MODE="all" set SERVER=nacos-server set MODE_INDEX=-1 set FUNCTION_MODE_INDEX=-1 set SERVER_INDEX=-1 +set EMBEDDED_STORAGE_INDEX=-1 +set EMBEDDED_STORAGE="" set i=0 for %%a in (%*) do ( - if "%%a" == "-m" ( set /a MODE_INDEX=!i!+1 ) - if "%%a" == "-f" ( set /a FUNCTION_MODE_INDEX=!i!+1 ) - if "%%a" == "-s" ( set /a SERVER_INDEX=!i!+1 ) - set /a i+=1 + if "%%a" == "-m" ( set /a MODE_INDEX=!i!+1 ) + if "%%a" == "-f" ( set /a FUNCTION_MODE_INDEX=!i!+1 ) + if "%%a" == "-s" ( set /a SERVER_INDEX=!i!+1 ) + if "%%a" == "-p" ( set /a EMBEDDED_STORAGE_INDEX=!i!+1 ) + set /a i+=1 ) set i=0 for %%a in (%*) do ( - if %MODE_INDEX% == !i! ( set MODE="%%a" ) - if %FUNCTION_MODE_INDEX% == !i! ( set FUNCTION_MODE="%%a" ) - if %SERVER_INDEX% == !i! (set SERVER="%%a") - set /a i+=1 + if %MODE_INDEX% == !i! ( set MODE="%%a" ) + if %FUNCTION_MODE_INDEX% == !i! ( set FUNCTION_MODE="%%a" ) + if %SERVER_INDEX% == !i! (set SERVER="%%a") + if %EMBEDDED_STORAGE_INDEX% == !i! (set EMBEDDED_STORAGE="%%a") + set /a i+=1 ) +rem if nacos startup mode is standalone if %MODE% == "standalone" ( - set "JAVA_OPT=%JAVA_OPT% -Xms512m -Xmx512m -Xmn256m" - set "JAVA_OPT=%JAVA_OPT% -Dnacos.standalone=true" -) else ( - set "JAVA_OPT=%JAVA_OPT% -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m" - set "JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=%BASE_DIR%\logs\java_heapdump.hprof" - set "JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages" + echo "nacos is starting with standalone" + set "NACOS_OPTS=-Dnacos.standalone=true" + set "NACOS_JVM_OPTS=-Xms512m -Xmx512m -Xmn256m" ) +rem if nacos startup mode is cluster +if %MODE% == "cluster" ( + echo "nacos is starting with cluster" + if %EMBEDDED_STORAGE% == "embedded" ( + set "NACOS_OPTS=-DembeddedStorage=true" + ) + + set "NACOS_JVM_OPTS=-server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=%BASE_DIR%\logs\java_heapdump.hprof -XX:-UseLargePages" +) + +rem set nacos's functionMode if %FUNCTION_MODE% == "config" ( - set "JAVA_OPT=%JAVA_OPT% -Dnacos.functionMode=config" + set "NACOS_OPTS=%NACOS_OPTS% -Dnacos.functionMode=config" ) + if %FUNCTION_MODE% == "naming" ( - set "JAVA_OPT=%JAVA_OPT% -Dnacos.functionMode=naming" + set "NACOS_OPTS=%NACOS_OPTS% -Dnacos.functionMode=naming" ) -set "JAVA_OPT=%JAVA_OPT% -Dloader.path=%BASE_DIR%/plugins/health,%BASE_DIR%/plugins/cmdb,%BASE_DIR%/plugins/mysql" +rem set nacos options +set "NACOS_OPTS=%NACOS_OPTS% -Dloader.path=%BASE_DIR%/plugins/health,%BASE_DIR%/plugins/cmdb" +set "NACOS_OPTS=%NACOS_OPTS% -Dnacos.home=%BASE_DIR%" +set "NACOS_OPTS=%NACOS_OPTS% -jar %BASE_DIR%\target\%SERVER%.jar" + +rem set nacos spring config location +set "NACOS_CONFIG_OPTS=--spring.config.additional-location=%CUSTOM_SEARCH_LOCATIONS%" + +rem set nacos log4j file location +set "NACOS_LOG4J_OPTS=--logging.config=%BASE_DIR%/conf/nacos-logback.xml" + -set "JAVA_OPT=%JAVA_OPT% -Dnacos.home=%BASE_DIR%" -set "JAVA_OPT=%JAVA_OPT% -jar %BASE_DIR%\target\%SERVER%.jar" -set "JAVA_OPT=%JAVA_OPT% --spring.config.location=%CUSTOM_SEARCH_LOCATIONS%" -set "JAVA_OPT=%JAVA_OPT% --logging.config=%BASE_DIR%/conf/nacos-logback.xml" +set COMMAND="%JAVA%" %NACOS_JVM_OPTS% %NACOS_OPTS% %NACOS_CONFIG_OPTS% %NACOS_LOG4J_OPTS% nacos.nacos %* -call "%JAVA%" %JAVA_OPT% nacos.nacos %* +rem start nacos command +%COMMAND% diff --git a/zlt-register/nacos/bin/startup.sh b/zlt-register/nacos/bin/startup.sh index 6da373f3..f16c5e6c 100644 --- a/zlt-register/nacos/bin/startup.sh +++ b/zlt-register/nacos/bin/startup.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Copyright 1999-2018 Alibaba Group Holding Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); @@ -54,7 +54,9 @@ fi export SERVER="nacos-server" export MODE="cluster" export FUNCTION_MODE="all" -while getopts ":m:f:s:" opt +export MEMBER_LIST="" +export EMBEDDED_STORAGE="" +while getopts ":m:f:s:c:p:" opt do case $opt in m) @@ -63,6 +65,10 @@ do FUNCTION_MODE=$OPTARG;; s) SERVER=$OPTARG;; + c) + MEMBER_LIST=$OPTARG;; + p) + EMBEDDED_STORAGE=$OPTARG;; ?) echo "Unknown parameter" exit 1;; @@ -72,8 +78,7 @@ done export JAVA_HOME export JAVA="$JAVA_HOME/bin/java" export BASE_DIR=`cd $(dirname $0)/..; pwd` -export DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/" -export CUSTOM_SEARCH_LOCATIONS=${DEFAULT_SEARCH_LOCATIONS},file:${BASE_DIR}/conf/ +export CUSTOM_SEARCH_LOCATIONS=file:${BASE_DIR}/conf/ #=========================================================================================== # JVM Configuration @@ -82,6 +87,9 @@ if [[ "${MODE}" == "standalone" ]]; then JAVA_OPT="${JAVA_OPT} -Xms512m -Xmx512m -Xmn256m" JAVA_OPT="${JAVA_OPT} -Dnacos.standalone=true" else + if [[ "${EMBEDDED_STORAGE}" == "embedded" ]]; then + JAVA_OPT="${JAVA_OPT} -DembeddedStorage=true" + fi JAVA_OPT="${JAVA_OPT} -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m" JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${BASE_DIR}/logs/java_heapdump.hprof" JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages" @@ -94,6 +102,7 @@ elif [[ "${FUNCTION_MODE}" == "naming" ]]; then JAVA_OPT="${JAVA_OPT} -Dnacos.functionMode=naming" fi +JAVA_OPT="${JAVA_OPT} -Dnacos.member.list=${MEMBER_LIST}" JAVA_MAJOR_VERSION=$($JAVA -version 2>&1 | sed -E -n 's/.* version "([0-9]*).*$/\1/p') if [[ "$JAVA_MAJOR_VERSION" -ge "9" ]] ; then @@ -103,11 +112,11 @@ else JAVA_OPT="${JAVA_OPT} -Xloggc:${BASE_DIR}/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" fi -JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb,${BASE_DIR}/plugins/mysql" +JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb" JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}" JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/${SERVER}.jar" JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}" -JAVA_OPT="${JAVA_OPT} --spring.config.location=${CUSTOM_SEARCH_LOCATIONS}" +JAVA_OPT="${JAVA_OPT} --spring.config.additional-location=${CUSTOM_SEARCH_LOCATIONS}" JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/nacos-logback.xml" JAVA_OPT="${JAVA_OPT} --server.max-http-header-size=524288" diff --git a/zlt-register/nacos/conf/1.4.0-ipv6_support-update.sql b/zlt-register/nacos/conf/1.4.0-ipv6_support-update.sql new file mode 100644 index 00000000..f480147a --- /dev/null +++ b/zlt-register/nacos/conf/1.4.0-ipv6_support-update.sql @@ -0,0 +1,27 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +ALTER TABLE `config_info_tag` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; + +ALTER TABLE `his_config_info` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL AFTER `src_user`; + +ALTER TABLE `config_info` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; + +ALTER TABLE `config_info_beta` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; \ No newline at end of file diff --git a/zlt-register/nacos/conf/application.properties b/zlt-register/nacos/conf/application.properties index c9f762e6..3f84c38f 100644 --- a/zlt-register/nacos/conf/application.properties +++ b/zlt-register/nacos/conf/application.properties @@ -1,3 +1,19 @@ +# +# Copyright 1999-2018 Alibaba Group Holding Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + #*************** Spring Boot Related Configurations ***************# ### Default web context path: server.servlet.contextPath=/nacos @@ -12,19 +28,23 @@ server.port=8848 # nacos.inetutils.ip-address= - #*************** Config Module Related Configurations ***************# -### If user MySQL as datasource: +### If use MySQL as datasource: # spring.datasource.platform=mysql ### Count of DB: # db.num=1 ### Connect URL of DB: -# db.url.0=jdbc:mysql://1.1.1.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true -# db.user=user -# db.password=password +# db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC +# db.user.0=nacos +# db.password.0=nacos +### Connection pool configuration: hikariCP +db.pool.config.connectionTimeout=30000 +db.pool.config.validationTimeout=10000 +db.pool.config.maximumPoolSize=20 +db.pool.config.minimumIdle=2 #*************** Naming Module Related Configurations ***************# ### Data dispatch task execution period in milliseconds: @@ -42,6 +62,22 @@ server.port=8848 ### If enable the instance auto expiration, kind like of health check of instance: # nacos.naming.expireInstance=true +nacos.naming.empty-service.auto-clean=true +nacos.naming.empty-service.clean.initial-delay-ms=50000 +nacos.naming.empty-service.clean.period-time-ms=30000 + +### Add in 2.0.0 +### The interval to clean empty service +# nacos.naming.clean.empty-service.interval=60000 + +### The expired time to clean empty service +# nacos.naming.clean.empty-service.expired-time=60000 + +### The interval to clean expired metadata +# nacos.naming.clean.expired-metadata.interval=5000 + +### The expired time to clean metadata +# nacos.naming.clean.expired-metadata.expired-time=60000 #*************** CMDB Module Related Configurations ***************# ### The interval to dump external CMDB in seconds: @@ -79,7 +115,7 @@ management.metrics.export.influx.enabled=false server.tomcat.accesslog.enabled=true ### The access log pattern: -server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i +server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i ### The directory of access log: server.tomcat.basedir= @@ -90,7 +126,7 @@ server.tomcat.basedir= #spring.security.enabled=false ### The ignore urls of auth, is deprecated in 1.2.0: -nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** +nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** ### The auth system to use, currently only 'nacos' is supported: nacos.core.auth.system.type=nacos @@ -105,9 +141,61 @@ nacos.core.auth.default.token.expire.seconds=18000 nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 ### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. -nacos.core.auth.caching.enabled=false +nacos.core.auth.caching.enabled=true + +### Since 1.4.1, Turn on/off white auth for user-agent: nacos-server, only for upgrade from old version. +nacos.core.auth.enable.userAgentAuthWhite=true +### Since 1.4.1, worked when nacos.core.auth.enabled=true and nacos.core.auth.enable.userAgentAuthWhite=false. +### The two properties is the white list for auth and used by identity the request from other server. +nacos.core.auth.server.identity.key= +nacos.core.auth.server.identity.value= #*************** Istio Related Configurations ***************# ### If turn on the MCP server: nacos.istio.mcp.server.enabled=false + + + +###*************** Add from 1.3.0 ***************### + + +#*************** Core Related Configurations ***************# + +### set the WorkerID manually +# nacos.core.snowflake.worker-id= + +### Member-MetaData +# nacos.core.member.meta.site= +# nacos.core.member.meta.adweight= +# nacos.core.member.meta.weight= + +### MemberLookup +### Addressing pattern category, If set, the priority is highest +# nacos.core.member.lookup.type=[file,address-server] +## Set the cluster list with a configuration file or command-line argument +# nacos.member.list=192.168.16.101:8847?raft_port=8807,192.168.16.101?raft_port=8808,192.168.16.101:8849?raft_port=8809 +## for AddressServerMemberLookup +# Maximum number of retries to query the address server upon initialization +# nacos.core.address-server.retry=5 +## Server domain name address of [address-server] mode +# address.server.domain=jmenv.tbsite.net +## Server port of [address-server] mode +# address.server.port=8080 +## Request address of [address-server] mode +# address.server.url=/nacos/serverlist + +#*************** JRaft Related Configurations ***************# + +### Sets the Raft cluster election timeout, default value is 5 second +# nacos.core.protocol.raft.data.election_timeout_ms=5000 +### Sets the amount of time the Raft snapshot will execute periodically, default is 30 minute +# nacos.core.protocol.raft.data.snapshot_interval_secs=30 +### raft internal worker threads +# nacos.core.protocol.raft.data.core_thread_num=8 +### Number of threads required for raft business request processing +# nacos.core.protocol.raft.data.cli_service_thread_num=4 +### raft linear read strategy. Safe linear reads are used by default, that is, the Leader tenure is confirmed by heartbeat +# nacos.core.protocol.raft.data.read_index_type=ReadOnlySafe +### rpc request timeout, default 5 seconds +# nacos.core.protocol.raft.data.rpc_request_timeout_ms=5000 diff --git a/zlt-register/nacos/conf/application.properties.example b/zlt-register/nacos/conf/application.properties.example index fd873753..8e171829 100644 --- a/zlt-register/nacos/conf/application.properties.example +++ b/zlt-register/nacos/conf/application.properties.example @@ -1,12 +1,177 @@ -# spring +# +# Copyright 1999-2018 Alibaba Group Holding Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# -server.contextPath=/nacos +#*************** Spring Boot Related Configurations ***************# +### Default web context path: server.servlet.contextPath=/nacos +### Default web server port: server.port=8848 +#*************** Network Related Configurations ***************# +### If prefer hostname over ip for Nacos server addresses in cluster.conf: +# nacos.inetutils.prefer-hostname-over-ip=false -db.num=2 -db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC -db.url.1=jdbc:mysql://11.163.152.9:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC -db.user=nacos_devtest -db.password=nacos \ No newline at end of file +### Specify local server's IP: +# nacos.inetutils.ip-address= + + +#*************** Config Module Related Configurations ***************# +### If use MySQL as datasource: +# spring.datasource.platform=mysql + +### Count of DB: +# db.num=1 + +### Connect URL of DB: +# db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC +# db.user.0=nacos +# db.password.0=nacos + + +#*************** Naming Module Related Configurations ***************# +### Data dispatch task execution period in milliseconds: +# nacos.naming.distro.taskDispatchPeriod=200 + +### Data count of batch sync task: +# nacos.naming.distro.batchSyncKeyCount=1000 + +### Retry delay in milliseconds if sync task failed: +# nacos.naming.distro.syncRetryDelay=5000 + +### If enable data warmup. If set to false, the server would accept request without local data preparation: +# nacos.naming.data.warmup=true + +### If enable the instance auto expiration, kind like of health check of instance: +# nacos.naming.expireInstance=true + +nacos.naming.empty-service.auto-clean=true +nacos.naming.empty-service.clean.initial-delay-ms=50000 +nacos.naming.empty-service.clean.period-time-ms=30000 + + +#*************** CMDB Module Related Configurations ***************# +### The interval to dump external CMDB in seconds: +# nacos.cmdb.dumpTaskInterval=3600 + +### The interval of polling data change event in seconds: +# nacos.cmdb.eventTaskInterval=10 + +### The interval of loading labels in seconds: +# nacos.cmdb.labelTaskInterval=300 + +### If turn on data loading task: +# nacos.cmdb.loadDataAtStart=false + + +#*************** Metrics Related Configurations ***************# +### Metrics for prometheus +#management.endpoints.web.exposure.include=* + +### Metrics for elastic search +management.metrics.export.elastic.enabled=false +#management.metrics.export.elastic.host=http://localhost:9200 + +### Metrics for influx +management.metrics.export.influx.enabled=false +#management.metrics.export.influx.db=springboot +#management.metrics.export.influx.uri=http://localhost:8086 +#management.metrics.export.influx.auto-create-db=true +#management.metrics.export.influx.consistency=one +#management.metrics.export.influx.compressed=true + + +#*************** Access Log Related Configurations ***************# +### If turn on the access log: +server.tomcat.accesslog.enabled=true + +### The access log pattern: +server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i + +### The directory of access log: +server.tomcat.basedir= + + +#*************** Access Control Related Configurations ***************# +### If enable spring security, this option is deprecated in 1.2.0: +#spring.security.enabled=false + +### The ignore urls of auth, is deprecated in 1.2.0: +nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** + +### The auth system to use, currently only 'nacos' is supported: +nacos.core.auth.system.type=nacos + +### If turn on auth system: +nacos.core.auth.enabled=false + +### The token expiration in seconds: +nacos.core.auth.default.token.expire.seconds=18000 + +### The default token: +nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 + +### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. +nacos.core.auth.caching.enabled=true + + +#*************** Istio Related Configurations ***************# +### If turn on the MCP server: +nacos.istio.mcp.server.enabled=false + + + +###*************** Add from 1.3.0 ***************### + + +#*************** Core Related Configurations ***************# + +### set the WorkerID manually +# nacos.core.snowflake.worker-id= + +### Member-MetaData +# nacos.core.member.meta.site= +# nacos.core.member.meta.adweight= +# nacos.core.member.meta.weight= + +### MemberLookup +### Addressing pattern category, If set, the priority is highest +# nacos.core.member.lookup.type=[file,address-server] +## Set the cluster list with a configuration file or command-line argument +# nacos.member.list=192.168.16.101:8847?raft_port=8807,192.168.16.101?raft_port=8808,192.168.16.101:8849?raft_port=8809 +## for AddressServerMemberLookup +# Maximum number of retries to query the address server upon initialization +# nacos.core.address-server.retry=5 +## Server domain name address of [address-server] mode +# address.server.domain=jmenv.tbsite.net +## Server port of [address-server] mode +# address.server.port=8080 +## Request address of [address-server] mode +# address.server.url=/nacos/serverlist + +#*************** JRaft Related Configurations ***************# + +### Sets the Raft cluster election timeout, default value is 5 second +# nacos.core.protocol.raft.data.election_timeout_ms=5000 +### Sets the amount of time the Raft snapshot will execute periodically, default is 30 minute +# nacos.core.protocol.raft.data.snapshot_interval_secs=30 +### raft internal worker threads +# nacos.core.protocol.raft.data.core_thread_num=8 +### Number of threads required for raft business request processing +# nacos.core.protocol.raft.data.cli_service_thread_num=4 +### raft linear read strategy. Safe linear reads are used by default, that is, the Leader tenure is confirmed by heartbeat +# nacos.core.protocol.raft.data.read_index_type=ReadOnlySafe +### rpc request timeout, default 5 seconds +# nacos.core.protocol.raft.data.rpc_request_timeout_ms=5000 diff --git a/zlt-register/nacos/conf/cluster.conf.example b/zlt-register/nacos/conf/cluster.conf.example index 38eed31a..0e3a7214 100644 --- a/zlt-register/nacos/conf/cluster.conf.example +++ b/zlt-register/nacos/conf/cluster.conf.example @@ -1,5 +1,21 @@ +# +# Copyright 1999-2018 Alibaba Group Holding Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + #it is ip #example -10.10.109.214 -11.16.128.34 -11.16.128.36 \ No newline at end of file +192.168.16.101:8847 +192.168.16.102 +192.168.16.103 diff --git a/zlt-register/nacos/conf/nacos-logback.xml b/zlt-register/nacos/conf/nacos-logback.xml index ce473f39..15f7623a 100644 --- a/zlt-register/nacos/conf/nacos-logback.xml +++ b/zlt-register/nacos/conf/nacos-logback.xml @@ -1,4 +1,20 @@ + + @@ -11,7 +27,7 @@ ${nacos.home}/logs/cmdb-main.log.%d{yyyy-MM-dd}.%i 2GB - 7 + 7 7GB true @@ -35,7 +51,7 @@ ${LOG_HOME}/naming-server.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 7GB true @@ -59,7 +75,7 @@ ${LOG_HOME}/naming-raft.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 3GB true @@ -84,7 +100,7 @@ ${LOG_HOME}/naming-distro.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 3GB true @@ -108,7 +124,7 @@ ${LOG_HOME}/naming-event.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 3GB true @@ -132,7 +148,7 @@ ${LOG_HOME}/naming-push.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 3GB true @@ -148,7 +164,7 @@ ${LOG_HOME}/naming-rt.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 3GB true @@ -165,7 +181,7 @@ ${LOG_HOME}/naming-performance.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 3GB true @@ -183,7 +199,7 @@ ${LOG_HOME}/config-dump.log.%d{yyyy-MM-dd}.%i 2GB - 7 + 7 7GB true @@ -199,7 +215,7 @@ ${LOG_HOME}/config-pull.log.%d{yyyy-MM-dd}.%i 20MB - 7 + 7 128MB true @@ -215,7 +231,7 @@ ${LOG_HOME}/config-fatal.log.%d{yyyy-MM-dd}.%i 20MB - 7 + 7 128MB true @@ -231,7 +247,7 @@ ${LOG_HOME}/config-memory.log.%d{yyyy-MM-dd}.%i 20MB - 7 + 7 128MB true @@ -247,7 +263,7 @@ ${LOG_HOME}/config-pull-check.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 3GB true @@ -264,7 +280,7 @@ ${LOG_HOME}/config-client-request.log.%d{yyyy-MM-dd}.%i 2GB - 7 + 7 7GB true @@ -281,7 +297,7 @@ ${LOG_HOME}/config-trace.log.%d{yyyy-MM-dd}.%i 2GB - 7 + 7 7GB true @@ -298,7 +314,7 @@ ${LOG_HOME}/config-notify.log.%d{yyyy-MM-dd}.%i 1GB - 7 + 7 3GB true @@ -315,7 +331,7 @@ ${LOG_HOME}/config-server.log.%d{yyyy-MM-dd}.%i 50MB - 7 + 7 512MB true @@ -332,7 +348,7 @@ ${LOG_HOME}/nacos.log.%d{yyyy-MM-dd}.%i 50MB - 7 + 7 512MB true @@ -349,7 +365,7 @@ ${LOG_HOME}/nacos-address.log.%d{yyyy-MM-dd}.%i 2GB - 7 + 7 7GB true @@ -366,7 +382,7 @@ ${LOG_HOME}/istio-main.log.%d{yyyy-MM-dd}.%i 2GB - 7 + 7 7GB true @@ -383,7 +399,58 @@ ${LOG_HOME}/core-auth.log.%d{yyyy-MM-dd}.%i 2GB - 7 + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/protocol-raft.log + true + + ${LOG_HOME}/protocol-raft.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/protocol-distro.log + true + + ${LOG_HOME}/protocol-distro.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/nacos-cluster.log + true + + ${LOG_HOME}/nacos-cluster.log.%d{yyyy-MM-dd}.%i + 2GB + 7 7GB true @@ -393,6 +460,129 @@ + + ${LOG_HOME}/alipay-jraft.log + true + + ${LOG_HOME}/alipay-jraft.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + + + ${LOG_HOME}/tps-control.log + true + + ${LOG_HOME}/tps-control.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/tps-control-digest.log + true + + ${LOG_HOME}/tps-control-digest.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/tps-control-detail.log + true + + ${LOG_HOME}/tps-control-detail.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + + ${LOG_HOME}/remote.log + true + + ${LOG_HOME}/remote.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/remote-digest.log + true + + ${LOG_HOME}/remote-digest.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/remote-push.log + true + + ${LOG_HOME}/remote-push.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + @@ -402,7 +592,37 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -483,6 +703,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -510,7 +750,22 @@ - + + + + + + + + + + + + + + + + diff --git a/zlt-register/nacos/conf/nacos-mysql.sql b/zlt-register/nacos/conf/nacos-mysql.sql index 5e88447a..551d6598 100644 --- a/zlt-register/nacos/conf/nacos-mysql.sql +++ b/zlt-register/nacos/conf/nacos-mysql.sql @@ -1,3 +1,19 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info */ @@ -11,7 +27,7 @@ CREATE TABLE `config_info` ( `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', - `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', `app_name` varchar(128) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', `c_desc` varchar(256) DEFAULT NULL, @@ -56,7 +72,7 @@ CREATE TABLE `config_info_beta` ( `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', - `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) @@ -78,7 +94,7 @@ CREATE TABLE `config_info_tag` ( `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', - `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag'; @@ -134,7 +150,7 @@ CREATE TABLE `his_config_info` ( `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `src_user` text, - `src_ip` varchar(20) DEFAULT NULL, + `src_ip` varchar(50) DEFAULT NULL, `op_type` char(10) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`nid`), @@ -192,7 +208,7 @@ CREATE TABLE `roles` ( CREATE TABLE `permissions` ( `role` varchar(50) NOT NULL, - `resource` varchar(512) NOT NULL, + `resource` varchar(255) NOT NULL, `action` varchar(8) NOT NULL, UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE ); diff --git a/zlt-register/nacos/conf/schema.sql b/zlt-register/nacos/conf/schema.sql index f0a19931..dfa8d754 100644 --- a/zlt-register/nacos/conf/schema.sql +++ b/zlt-register/nacos/conf/schema.sql @@ -1,3 +1,19 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + CREATE SCHEMA nacos AUTHORIZATION nacos; CREATE TABLE config_info ( @@ -11,7 +27,7 @@ CREATE TABLE config_info ( gmt_create timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', src_user varchar(128) DEFAULT NULL, - src_ip varchar(20) DEFAULT NULL, + src_ip varchar(50) DEFAULT NULL, c_desc varchar(256) DEFAULT NULL, c_use varchar(64) DEFAULT NULL, effect varchar(64) DEFAULT NULL, @@ -36,7 +52,7 @@ CREATE TABLE his_config_info ( gmt_create timestamp NOT NULL DEFAULT '2010-05-05 00:00:00.000', gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00.000', src_user varchar(128), - src_ip varchar(20) DEFAULT NULL, + src_ip varchar(50) DEFAULT NULL, op_type char(10) DEFAULT NULL, constraint hisconfiginfo_nid_key PRIMARY KEY (nid)); @@ -57,7 +73,7 @@ CREATE TABLE config_info_beta ( gmt_create timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', src_user varchar(128), - src_ip varchar(20) DEFAULT NULL, + src_ip varchar(50) DEFAULT NULL, constraint configinfobeta_id_key PRIMARY KEY (id), constraint uk_configinfobeta_datagrouptenant UNIQUE (data_id,group_id,tenant_id)); @@ -73,7 +89,7 @@ CREATE TABLE config_info_tag ( gmt_create timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', src_user varchar(128), - src_ip varchar(20) DEFAULT NULL, + src_ip varchar(50) DEFAULT NULL, constraint configinfotag_id_key PRIMARY KEY (id), constraint uk_configinfotag_datagrouptenanttag UNIQUE (data_id,group_id,tenant_id,tag_id)); @@ -194,3 +210,19 @@ CREATE TABLE permissions ( INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE); INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN'); + + +/******************************************/ +/* ipv6 support */ +/******************************************/ +ALTER TABLE `config_info_tag` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; + +ALTER TABLE `his_config_info` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL AFTER `src_user`; + +ALTER TABLE `config_info` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; + +ALTER TABLE `config_info_beta` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; \ No newline at end of file diff --git a/zlt-register/nacos/target/nacos-server.jar b/zlt-register/nacos/target/nacos-server.jar index 60234d47..dfb57b71 100644 Binary files a/zlt-register/nacos/target/nacos-server.jar and b/zlt-register/nacos/target/nacos-server.jar differ diff --git a/zlt-transaction/pom.xml b/zlt-transaction/pom.xml index 6b2573fc..fad9d2d4 100644 --- a/zlt-transaction/pom.xml +++ b/zlt-transaction/pom.xml @@ -4,7 +4,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-transaction 事务 diff --git a/zlt-transaction/txlcn-tm/pom.xml b/zlt-transaction/txlcn-tm/pom.xml index 5c3c4303..6f36da1f 100644 --- a/zlt-transaction/txlcn-tm/pom.xml +++ b/zlt-transaction/txlcn-tm/pom.xml @@ -3,7 +3,7 @@ com.zlt zlt-transaction - 3.5.0 + 5.4.0 4.0.0 txlcn-tm diff --git a/zlt-uaa/Dockerfile b/zlt-uaa/Dockerfile new file mode 100644 index 00000000..fe3dac09 --- /dev/null +++ b/zlt-uaa/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:8u292 + +LABEL MAINTAINER=Andy +WORKDIR /apps +ADD target/zlt-uaa.jar /apps/ +CMD ["java","-jar","zlt-uaa.jar"] +# docker run --name zlt-uaa \ +# -e spring_cloud_nacos_server_addr=10.0.0.12:8848 \ +# -e zlt_datasource_ip=10.0.0.12 \ +# -e zlt_datasource_username=root \ +# -e zlt_datasource_password=redhat \ +# -e spring_redis_host=10.0.0.12 \ +# -d zlt-uaa:4.5 \ No newline at end of file diff --git a/zlt-uaa/pom.xml b/zlt-uaa/pom.xml index 97a660f4..10c2c963 100644 --- a/zlt-uaa/pom.xml +++ b/zlt-uaa/pom.xml @@ -4,7 +4,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-uaa 认证中心 @@ -27,7 +27,7 @@ com.zlt - zlt-ribbon-spring-boot-starter + zlt-loadbalancer-spring-boot-starter com.zlt @@ -88,7 +88,17 @@ io.micrometer micrometer-registry-prometheus - + + + org.hibernate + hibernate-validator + + + junit + junit + test + + diff --git a/zlt-uaa/src/main/java/com/central/UaaServerApp.java b/zlt-uaa/src/main/java/com/central/UaaServerApp.java index e58bbca1..b3ac5a29 100644 --- a/zlt-uaa/src/main/java/com/central/UaaServerApp.java +++ b/zlt-uaa/src/main/java/com/central/UaaServerApp.java @@ -1,11 +1,10 @@ package com.central; -import com.central.common.ribbon.annotation.EnableFeignInterceptor; +import com.central.common.lb.annotation.EnableFeignInterceptor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; /** * @author zlt @@ -13,10 +12,12 @@ @EnableFeignClients @EnableFeignInterceptor @EnableDiscoveryClient -@EnableRedisHttpSession +//@EnableRedisHttpSession @SpringBootApplication public class UaaServerApp { public static void main(String[] args) { - SpringApplication.run(UaaServerApp.class, args); + SpringApplication application = new SpringApplication(UaaServerApp.class); + application.setEnvironmentPrefix("zlt-uaa"); + application.run(args); } } diff --git a/zlt-uaa/src/main/java/com/central/oauth/config/AuthorizationServerConfig.java b/zlt-uaa/src/main/java/com/central/oauth/config/AuthorizationServerConfig.java index 55f3b87c..e7d68db9 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/config/AuthorizationServerConfig.java +++ b/zlt-uaa/src/main/java/com/central/oauth/config/AuthorizationServerConfig.java @@ -1,31 +1,49 @@ package com.central.oauth.config; +import cn.hutool.core.util.StrUtil; +import com.central.common.constant.SecurityConstants; +import com.central.common.model.SysUser; +import com.central.oauth.model.Client; +import com.central.oauth.service.IClientService; import com.central.oauth.service.impl.RedisClientDetailsService; +import com.central.oauth.service.impl.UserDetailServiceFactory; +import com.central.oauth.utils.OidcIdTokenBuilder; +import com.central.oauth2.common.constants.IdTokenClaimNames; +import com.central.oauth2.common.properties.TokenStoreProperties; +import com.central.oauth2.common.util.AuthUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.cloud.bootstrap.encrypt.KeyProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.TokenGranter; import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices; import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; import org.springframework.security.oauth2.provider.token.TokenEnhancer; -import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import javax.annotation.Resource; -import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; /** * OAuth2 授权服务器配置 * * @author zlt * @date 2018/10/24 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Configuration @EnableAuthorizationServer @@ -38,17 +56,11 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap private AuthenticationManager authenticationManager; @Resource - private UserDetailsService userDetailsService; + private UserDetailServiceFactory userDetailsServiceFactory; @Autowired private TokenStore tokenStore; - @Autowired(required = false) - private JwtAccessTokenConverter jwtAccessTokenConverter; - - @Autowired(required = false) - private TokenEnhancer tokenEnhancer; - @Autowired private WebResponseExceptionTranslator webResponseExceptionTranslator; @@ -58,27 +70,21 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap @Autowired private RandomValueAuthorizationCodeServices authorizationCodeServices; + @Autowired + private TokenGranter tokenGranter; + /** * 配置身份认证器,配置认证方式,TokenStore,TokenGranter,OAuth2RequestFactory * @param endpoints */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { - if (jwtAccessTokenConverter != null) { - if (tokenEnhancer != null) { - TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); - tokenEnhancerChain.setTokenEnhancers( - Arrays.asList(tokenEnhancer, jwtAccessTokenConverter)); - endpoints.tokenEnhancer(tokenEnhancerChain); - } else { - endpoints.accessTokenConverter(jwtAccessTokenConverter); - } - } endpoints.tokenStore(tokenStore) .authenticationManager(authenticationManager) - .userDetailsService(userDetailsService) + //.userDetailsService(userDetailsServiceFactory.getService(SecurityConstants.DEF_ACCOUNT_TYPE)) .authorizationCodeServices(authorizationCodeServices) - .exceptionTranslator(webResponseExceptionTranslator); + .exceptionTranslator(webResponseExceptionTranslator) + .tokenGranter(tokenGranter); } /** @@ -105,4 +111,68 @@ public void configure(AuthorizationServerSecurityConfigurer security) { //让/oauth/token支持client_id以及client_secret作登录认证 .allowFormAuthenticationForClients(); } + + @Bean + @Order(1) + public TokenEnhancer tokenEnhancer(@Autowired(required = false) KeyProperties keyProperties + , IClientService clientService + , TokenStoreProperties tokenStoreProperties) { + return (accessToken, authentication) -> { + Set responseTypes = authentication.getOAuth2Request().getResponseTypes(); + Map additionalInfo = new HashMap<>(3); + String accountType = AuthUtils.getAccountType(authentication.getUserAuthentication()); + if (StrUtil.isNotEmpty(accountType)) { + additionalInfo.put(SecurityConstants.ACCOUNT_TYPE_PARAM_NAME, accountType); + } + + if (responseTypes.contains(SecurityConstants.ID_TOKEN) + || "authJwt".equals(tokenStoreProperties.getType())) { + Object principal = authentication.getPrincipal(); + //增加id参数 + if (principal instanceof SysUser) { + SysUser user = (SysUser)principal; + if (responseTypes.contains(SecurityConstants.ID_TOKEN)) { + //生成id_token + setIdToken(additionalInfo, authentication, keyProperties, clientService, user); + } + if ("authJwt".equals(tokenStoreProperties.getType())) { + additionalInfo.put("id", user.getId()); + } + } + } + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); + return accessToken; + }; + } + + /** + * 生成id_token + * @param additionalInfo 存储token附加信息对象 + * @param authentication 授权对象 + * @param keyProperties 密钥 + * @param clientService 应用service + */ + private void setIdToken(Map additionalInfo, OAuth2Authentication authentication + , KeyProperties keyProperties, IClientService clientService, SysUser user) { + String clientId = authentication.getOAuth2Request().getClientId(); + Client client = clientService.loadClientByClientId(clientId); + if (client.getSupportIdToken()) { + String nonce = authentication.getOAuth2Request().getRequestParameters().get(IdTokenClaimNames.NONCE); + long now = System.currentTimeMillis(); + long expiresAt = System.currentTimeMillis() + client.getIdTokenValiditySeconds() * 1000; + String idToken = OidcIdTokenBuilder.builder(keyProperties) + .issuer(SecurityConstants.ISS) + .issuedAt(now) + .expiresAt(expiresAt) + .subject(String.valueOf(user.getId())) + .name(user.getNickname()) + .loginName(user.getUsername()) + .picture(user.getHeadImgUrl()) + .audience(clientId) + .nonce(nonce) + .build(); + + additionalInfo.put(SecurityConstants.ID_TOKEN, idToken); + } + } } diff --git a/zlt-uaa/src/main/java/com/central/oauth/config/RedisListenerConfig.java b/zlt-uaa/src/main/java/com/central/oauth/config/RedisListenerConfig.java new file mode 100644 index 00000000..68f98f8f --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/config/RedisListenerConfig.java @@ -0,0 +1,24 @@ +package com.central.oauth.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +/** + * + * redis过期key监听器配置类 + * @author zlt + * + */ +@Configuration +public class RedisListenerConfig { + @Bean + @Primary + public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(factory); + return container; + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/config/SecurityConfig.java b/zlt-uaa/src/main/java/com/central/oauth/config/SecurityConfig.java index 85f06694..359c8475 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/config/SecurityConfig.java +++ b/zlt-uaa/src/main/java/com/central/oauth/config/SecurityConfig.java @@ -1,35 +1,46 @@ package com.central.oauth.config; import com.central.common.constant.SecurityConstants; +import com.central.common.properties.TenantProperties; import com.central.oauth.filter.LoginProcessSetTenantFilter; import com.central.oauth.handler.OauthLogoutSuccessHandler; +import com.central.oauth.password.PasswordAuthenticationProvider; +import com.central.oauth.service.impl.UserDetailServiceFactory; +import com.central.oauth.tenant.TenantAuthenticationSecurityConfig; +import com.central.oauth.tenant.TenantUsernamePasswordAuthenticationFilter; import com.central.oauth.mobile.MobileAuthenticationSecurityConfig; import com.central.oauth.openid.OpenIdAuthenticationSecurityConfig; import com.central.common.config.DefaultPasswordConfig; +import com.central.oauth2.common.token.CustomWebAuthenticationDetails; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; /** * spring security配置 * 在WebSecurityConfigurerAdapter不拦截oauth要开放的资源 * * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Configuration @Import(DefaultPasswordConfig.class) @@ -37,23 +48,21 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AuthenticationSuccessHandler authenticationSuccessHandler; - @Autowired - private AuthenticationFailureHandler authenticationFailureHandler; @Autowired(required = false) private AuthenticationEntryPoint authenticationEntryPoint; @Resource - private UserDetailsService userDetailsService; + private UserDetailServiceFactory userDetailsServiceFactory; @Autowired private PasswordEncoder passwordEncoder; @Resource - private LogoutHandler oauthLogoutHandler; + private LogoutHandler logoutHandler; - @Autowired - private ValidateCodeSecurityConfig validateCodeSecurityConfig; + @Resource + private LogoutSuccessHandler logoutSuccessHandler; @Autowired private OpenIdAuthenticationSecurityConfig openIdAuthenticationSecurityConfig; @@ -61,6 +70,18 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MobileAuthenticationSecurityConfig mobileAuthenticationSecurityConfig; + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private TenantAuthenticationSecurityConfig tenantAuthenticationSecurityConfig; + + @Autowired + private TenantProperties tenantProperties; + + @Autowired + private AuthenticationDetailsSource authenticationDetailsSource; + /** * 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户 * @return 认证管理对象 @@ -71,27 +92,30 @@ public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } + @Bean + public TenantUsernamePasswordAuthenticationFilter tenantAuthenticationFilter(AuthenticationManager authenticationManager) { + TenantUsernamePasswordAuthenticationFilter filter = new TenantUsernamePasswordAuthenticationFilter(); + filter.setAuthenticationManager(authenticationManager); + filter.setFilterProcessesUrl(SecurityConstants.OAUTH_LOGIN_PRO_URL); + filter.setAuthenticationSuccessHandler(authenticationSuccessHandler); + filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(SecurityConstants.LOGIN_FAILURE_PAGE)); + filter.setAuthenticationDetailsSource(authenticationDetailsSource); + return filter; + } + @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() //授权服务器关闭basic认证 .permitAll() - .and() - .formLogin() - .loginPage(SecurityConstants.LOGIN_PAGE) - .loginProcessingUrl(SecurityConstants.OAUTH_LOGIN_PRO_URL) - .successHandler(authenticationSuccessHandler) - .failureHandler(authenticationFailureHandler) .and() .logout() .logoutUrl(SecurityConstants.LOGOUT_URL) - .logoutSuccessHandler(new OauthLogoutSuccessHandler()) - .addLogoutHandler(oauthLogoutHandler) + .logoutSuccessHandler(logoutSuccessHandler) + .addLogoutHandler(logoutHandler) .clearAuthentication(true) .and() - .apply(validateCodeSecurityConfig) - .and() .apply(openIdAuthenticationSecurityConfig) .and() .apply(mobileAuthenticationSecurityConfig) @@ -101,6 +125,22 @@ protected void configure(HttpSecurity http) throws Exception { // 解决不允许显示在iframe的问题 .headers().frameOptions().disable().cacheControl(); + if (tenantProperties.getEnable()) { + //解决不同租户单点登录时角色没变化 + http.formLogin() + .loginPage(SecurityConstants.LOGIN_PAGE) + .and() + .addFilterAt(tenantAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class) + .apply(tenantAuthenticationSecurityConfig); + } else { + http.formLogin() + .loginPage(SecurityConstants.LOGIN_PAGE) + .loginProcessingUrl(SecurityConstants.OAUTH_LOGIN_PRO_URL) + .successHandler(authenticationSuccessHandler) + .authenticationDetailsSource(authenticationDetailsSource); + } + + // 基于密码 等模式可以无session,不支持授权码模式 if (authenticationEntryPoint != null) { http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint); @@ -114,8 +154,14 @@ protected void configure(HttpSecurity http) throws Exception { /** * 全局用户信息 */ - @Autowired - public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); + @Override + public void configure(AuthenticationManagerBuilder auth) { + PasswordAuthenticationProvider provider = new PasswordAuthenticationProvider(); + provider.setPasswordEncoder(passwordEncoder); + provider.setUserDetailsServiceFactory(userDetailsServiceFactory); + auth.authenticationProvider(provider); } + /*public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); + }*/ } diff --git a/zlt-uaa/src/main/java/com/central/oauth/config/SecurityHandlerConfig.java b/zlt-uaa/src/main/java/com/central/oauth/config/SecurityHandlerConfig.java index 17629925..21466313 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/config/SecurityHandlerConfig.java +++ b/zlt-uaa/src/main/java/com/central/oauth/config/SecurityHandlerConfig.java @@ -1,24 +1,22 @@ package com.central.oauth.config; -import com.central.common.utils.ResponseUtil; +import com.central.oauth.exception.ValidateCodeException; import com.central.oauth.handler.OauthLogoutHandler; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.central.oauth.handler.OauthLogoutSuccessHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.exceptions.*; import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator; import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; -import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -32,46 +30,33 @@ @Slf4j @Configuration public class SecurityHandlerConfig { - @Resource - private ObjectMapper objectMapper; - - /** - * 登陆失败,返回401 - */ @Bean - public AuthenticationFailureHandler loginFailureHandler() { - return (request, response, exception) -> { - String msg; - if (exception instanceof BadCredentialsException) { - msg = "密码错误"; - } else { - msg = exception.getMessage(); - } - ResponseUtil.responseWriter(objectMapper, response, msg, HttpStatus.UNAUTHORIZED.value()); - }; + public LogoutHandler logoutHandler() { + return new OauthLogoutHandler(); } @Bean - public OauthLogoutHandler oauthLogoutHandler() { - return new OauthLogoutHandler(); + public LogoutSuccessHandler logoutSuccessHandler() { + return new OauthLogoutSuccessHandler(); } @Bean public WebResponseExceptionTranslator webResponseExceptionTranslator() { return new DefaultWebResponseExceptionTranslator() { - public static final String BAD_MSG = "坏的凭证"; + private static final String BAD_MSG = "坏的凭证"; + private static final String BAD_MSG_EN = "Bad credentials"; @Override public ResponseEntity translate(Exception e) throws Exception { OAuth2Exception oAuth2Exception; - if (e.getMessage() != null && e.getMessage().equals(BAD_MSG)) { + if (e.getMessage() != null + && (BAD_MSG.equals(e.getMessage()) || BAD_MSG_EN.equals(e.getMessage()))) { oAuth2Exception = new InvalidGrantException("用户名或密码错误", e); - } else if (e instanceof InternalAuthenticationServiceException) { - oAuth2Exception = new InvalidGrantException(e.getMessage(), e); - } else if (e instanceof RedirectMismatchException) { - oAuth2Exception = new InvalidGrantException(e.getMessage(), e); - } else if (e instanceof InvalidScopeException) { + } else if (e instanceof InternalAuthenticationServiceException + || e instanceof ValidateCodeException) { oAuth2Exception = new InvalidGrantException(e.getMessage(), e); + } else if (e instanceof OAuth2Exception) { + oAuth2Exception = (OAuth2Exception)e; } else { oAuth2Exception = new UnsupportedResponseTypeException("服务内部错误", e); } diff --git a/zlt-uaa/src/main/java/com/central/oauth/config/TokenGranterConfig.java b/zlt-uaa/src/main/java/com/central/oauth/config/TokenGranterConfig.java new file mode 100644 index 00000000..067ca3c7 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/config/TokenGranterConfig.java @@ -0,0 +1,179 @@ +package com.central.oauth.config; + +import com.central.oauth.granter.MobilePwdGranter; +import com.central.oauth.granter.OpenIdGranter; +import com.central.oauth.granter.PwdImgCodeGranter; +import com.central.oauth.service.IValidateCodeService; +import com.central.oauth.service.impl.CustomTokenServices; +import com.central.oauth.service.impl.UserDetailServiceFactory; +import com.central.oauth.service.impl.UserDetailsByNameServiceFactoryWrapper; +import com.central.oauth2.common.properties.SecurityProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter; +import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices; +import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices; +import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter; +import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; +import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; +import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory; +import org.springframework.security.oauth2.provider.token.*; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * token授权模式配置类 + * + * @author zlt + * @date 2020/7/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Configuration +public class TokenGranterConfig { + @Autowired + private ClientDetailsService clientDetailsService; + + @Resource + private UserDetailServiceFactory userDetailsServiceFactory; + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private TokenStore tokenStore; + + @Autowired(required = false) + private List tokenEnhancer; + + @Autowired + private IValidateCodeService validateCodeService; + + @Autowired + private RandomValueAuthorizationCodeServices authorizationCodeServices; + + private boolean reuseRefreshToken = true; + + private AuthorizationServerTokenServices tokenServices; + + private TokenGranter tokenGranter; + + @Resource + private SecurityProperties securityProperties; + + /** + * 授权模式 + */ + @Bean + @ConditionalOnMissingBean + public TokenGranter tokenGranter(DefaultTokenServices tokenServices) { + if (tokenGranter == null) { + tokenGranter = new TokenGranter() { + private CompositeTokenGranter delegate; + + @Override + public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) { + if (delegate == null) { + delegate = new CompositeTokenGranter(getAllTokenGranters(tokenServices)); + } + return delegate.grant(grantType, tokenRequest); + } + }; + } + return tokenGranter; + } + + /** + * 所有授权模式:默认的5种模式 + 自定义的模式 + */ + protected List getAllTokenGranters(DefaultTokenServices tokenServices) { + AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices(); + OAuth2RequestFactory requestFactory = requestFactory(); + //获取默认的授权模式 + List tokenGranters = getDefaultTokenGranters(tokenServices, authorizationCodeServices, requestFactory); + if (authenticationManager != null) { + // 添加密码加图形验证码模式 + tokenGranters.add(new PwdImgCodeGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory, validateCodeService)); + // 添加openId模式 + tokenGranters.add(new OpenIdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); + // 添加手机号加密码授权模式 + tokenGranters.add(new MobilePwdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); + } + return tokenGranters; + } + + /** + * 默认的授权模式 + */ + private List getDefaultTokenGranters(AuthorizationServerTokenServices tokenServices + , AuthorizationCodeServices authorizationCodeServices, OAuth2RequestFactory requestFactory) { + List tokenGranters = new ArrayList<>(); + // 添加授权码模式 + tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory)); + // 添加刷新令牌的模式 + tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory)); + // 添加隐士授权模式 + tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory)); + // 添加客户端模式 + tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory)); + if (authenticationManager != null) { + // 添加密码模式 + tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); + } + return tokenGranters; + } + + private AuthorizationCodeServices authorizationCodeServices() { + if (authorizationCodeServices == null) { + authorizationCodeServices = new InMemoryAuthorizationCodeServices(); + } + return authorizationCodeServices; + } + + private OAuth2RequestFactory requestFactory() { + return new DefaultOAuth2RequestFactory(clientDetailsService); + } + + @Bean + @ConditionalOnMissingBean + protected DefaultTokenServices createDefaultTokenServices() { + DefaultTokenServices tokenServices = new CustomTokenServices(securityProperties.getAuth()); + tokenServices.setTokenStore(tokenStore); + tokenServices.setSupportRefreshToken(true); + tokenServices.setReuseRefreshToken(reuseRefreshToken); + tokenServices.setClientDetailsService(clientDetailsService); + tokenServices.setTokenEnhancer(tokenEnhancer()); + addUserDetailsService(tokenServices); + return tokenServices; + } + + private TokenEnhancer tokenEnhancer() { + if (tokenEnhancer != null) { + TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); + tokenEnhancerChain.setTokenEnhancers(tokenEnhancer); + return tokenEnhancerChain; + } + return null; + } + + private void addUserDetailsService(DefaultTokenServices tokenServices) { + if (this.userDetailsServiceFactory != null) { + PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); + provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceFactoryWrapper<>(this.userDetailsServiceFactory)); + tokenServices.setAuthenticationManager(new ProviderManager(Collections.singletonList(provider))); + } + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/config/ValidateCodeSecurityConfig.java b/zlt-uaa/src/main/java/com/central/oauth/config/ValidateCodeSecurityConfig.java deleted file mode 100644 index edc1ce4f..00000000 --- a/zlt-uaa/src/main/java/com/central/oauth/config/ValidateCodeSecurityConfig.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * - */ -package com.central.oauth.config; - -import org.springframework.security.config.annotation.SecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.web.DefaultSecurityFilterChain; -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import javax.servlet.Filter; - -/** - * 校验码相关安全配置 - * - * @author zlt - */ -@Component("validateCodeSecurityConfig") -public class ValidateCodeSecurityConfig extends SecurityConfigurerAdapter { - @Resource - private Filter validateCodeFilter; - - @Override - public void configure(HttpSecurity http) { - http.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class); - } -} diff --git a/zlt-uaa/src/main/java/com/central/oauth/controller/ClientController.java b/zlt-uaa/src/main/java/com/central/oauth/controller/ClientController.java index 0f22e64d..4dd8097a 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/controller/ClientController.java +++ b/zlt-uaa/src/main/java/com/central/oauth/controller/ClientController.java @@ -18,6 +18,9 @@ * 应用相关接口 * * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Api(tags = "应用") @RestController @@ -53,7 +56,7 @@ public void delete(@PathVariable Long id) { @PostMapping("/saveOrUpdate") @ApiOperation(value = "保存或者修改应用") - public Result saveOrUpdate(@RequestBody ClientDto clientDto) { + public Result saveOrUpdate(@RequestBody ClientDto clientDto) throws Exception { return clientService.saveClient(clientDto); } } diff --git a/zlt-uaa/src/main/java/com/central/oauth/controller/OAuth2Controller.java b/zlt-uaa/src/main/java/com/central/oauth/controller/OAuth2Controller.java deleted file mode 100644 index be3880ac..00000000 --- a/zlt-uaa/src/main/java/com/central/oauth/controller/OAuth2Controller.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.central.oauth.controller; - -import com.central.common.constant.SecurityConstants; -import com.central.common.utils.ResponseUtil; -import com.central.common.context.TenantContextHolder; -import com.central.oauth2.common.token.MobileAuthenticationToken; -import com.central.oauth2.common.token.OpenIdAuthenticationToken; -import com.central.oauth2.common.util.AuthUtils; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.MapUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.security.authentication.*; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException; -import org.springframework.security.oauth2.provider.*; -import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * OAuth2相关操作 - * - * @author zlt - */ -@Api(tags = "OAuth2相关操作") -@Slf4j -@RestController -public class OAuth2Controller { - @Resource - private ObjectMapper objectMapper; - - @Autowired - private PasswordEncoder passwordEncoder; - - @Resource - private AuthorizationServerTokenServices authorizationServerTokenServices; - - @Autowired - private AuthenticationManager authenticationManager; - - @Autowired - private ClientDetailsService clientDetailsService; - - @ApiOperation(value = "用户名密码获取token") - @PostMapping(SecurityConstants.PASSWORD_LOGIN_PRO_URL) - public void getUserTokenInfo( - @ApiParam(required = true, name = "username", value = "账号") String username, - @ApiParam(required = true, name = "password", value = "密码") String password, - HttpServletRequest request, HttpServletResponse response) throws IOException { - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password); - writerToken(request, response, token, "用户名或密码错误"); - } - - @ApiOperation(value = "openId获取token") - @PostMapping(SecurityConstants.OPENID_TOKEN_URL) - public void getTokenByOpenId( - @ApiParam(required = true, name = "openId", value = "openId") String openId, - HttpServletRequest request, HttpServletResponse response) throws IOException { - OpenIdAuthenticationToken token = new OpenIdAuthenticationToken(openId); - writerToken(request, response, token, "openId错误"); - } - - @ApiOperation(value = "mobile获取token") - @PostMapping(SecurityConstants.MOBILE_TOKEN_URL) - public void getTokenByMobile( - @ApiParam(required = true, name = "mobile", value = "mobile") String mobile, - @ApiParam(required = true, name = "password", value = "密码") String password, - HttpServletRequest request, HttpServletResponse response) throws IOException { - MobileAuthenticationToken token = new MobileAuthenticationToken(mobile, password); - writerToken(request, response, token, "手机号或密码错误"); - } - - private void writerToken(HttpServletRequest request, HttpServletResponse response, AbstractAuthenticationToken token - , String badCredenbtialsMsg) throws IOException { - try { - final String[] clientInfos = AuthUtils.extractClient(request); - String clientId = clientInfos[0]; - String clientSecret = clientInfos[1]; - - ClientDetails clientDetails = getClient(clientId, clientSecret); - //保存租户id - TenantContextHolder.setTenant(clientId); - TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), "customer"); - OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails); - Authentication authentication = authenticationManager.authenticate(token); - SecurityContextHolder.getContext().setAuthentication(authentication); - OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); - OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication); - oAuth2Authentication.setAuthenticated(true); - TenantContextHolder.clear(); - ResponseUtil.responseSucceed(objectMapper, response, oAuth2AccessToken); - } catch (BadCredentialsException | InternalAuthenticationServiceException e) { - exceptionHandler(response, badCredenbtialsMsg); - } catch (Exception e) { - exceptionHandler(response, e); - } - } - - private void exceptionHandler(HttpServletResponse response, Exception e) throws IOException { - log.error("exceptionHandler-error:", e); - exceptionHandler(response, e.getMessage()); - } - - private void exceptionHandler(HttpServletResponse response, String msg) throws IOException { - response.setStatus(HttpStatus.UNAUTHORIZED.value()); - ResponseUtil.responseFailed(objectMapper, response, msg); - } - - private ClientDetails getClient(String clientId, String clientSecret) { - ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); - - if (clientDetails == null) { - throw new UnapprovedClientAuthenticationException("clientId对应的信息不存在"); - } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) { - throw new UnapprovedClientAuthenticationException("clientSecret不匹配"); - } - return clientDetails; - } -} diff --git a/zlt-uaa/src/main/java/com/central/oauth/controller/TokensController.java b/zlt-uaa/src/main/java/com/central/oauth/controller/TokensController.java index 53611a4c..088d44f3 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/controller/TokensController.java +++ b/zlt-uaa/src/main/java/com/central/oauth/controller/TokensController.java @@ -1,15 +1,29 @@ package com.central.oauth.controller; -import com.central.common.annotation.LoginClient; +import com.central.common.constant.SecurityConstants; import com.central.common.model.PageResult; +import com.central.common.model.Result; import com.central.oauth.model.TokenVo; import com.central.oauth.service.ITokensService; +import com.central.oauth2.common.util.AuthUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; import java.util.Map; +import java.util.stream.Collectors; /** * token管理接口 @@ -17,15 +31,43 @@ * @author zlt */ @Api(tags = "Token管理") +@Slf4j @RestController @RequestMapping("/tokens") public class TokensController { - @Autowired + @Resource private ITokensService tokensService; + @Resource + private ClientDetailsService clientDetailsService; + + @Resource + private PasswordEncoder passwordEncoder; + @GetMapping("") @ApiOperation(value = "token列表") public PageResult list(@RequestParam Map params, String tenantId) { return tokensService.listTokens(params, tenantId); } + + @GetMapping("/key") + @ApiOperation(value = "获取jwt密钥") + public Result key(HttpServletRequest request) { + try { + String[] clientArr = AuthUtils.extractClient(request); + ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientArr[0]); + if (clientDetails == null || !passwordEncoder.matches(clientArr[1], clientDetails.getClientSecret())) { + throw new BadCredentialsException("应用ID或密码错误"); + } + } catch (AuthenticationException ae) { + return Result.failed(ae.getMessage()); + } + org.springframework.core.io.Resource res = new ClassPathResource(SecurityConstants.RSA_PUBLIC_KEY); + try (BufferedReader br = new BufferedReader(new InputStreamReader(res.getInputStream()))) { + return Result.succeed(br.lines().collect(Collectors.joining("\n"))); + } catch (IOException ioe) { + log.error("key error", ioe); + return Result.failed(ioe.getMessage()); + } + } } diff --git a/zlt-uaa/src/main/java/com/central/oauth/filter/LoginProcessSetTenantFilter.java b/zlt-uaa/src/main/java/com/central/oauth/filter/LoginProcessSetTenantFilter.java index 7030122c..da6dad12 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/filter/LoginProcessSetTenantFilter.java +++ b/zlt-uaa/src/main/java/com/central/oauth/filter/LoginProcessSetTenantFilter.java @@ -21,7 +21,7 @@ * @author zlt * @date 2020/3/29 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ public class LoginProcessSetTenantFilter extends OncePerRequestFilter { diff --git a/zlt-uaa/src/main/java/com/central/oauth/filter/OauthTokenAspect.java b/zlt-uaa/src/main/java/com/central/oauth/filter/OauthTokenAspect.java index e20b8f8f..7e101e6f 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/filter/OauthTokenAspect.java +++ b/zlt-uaa/src/main/java/com/central/oauth/filter/OauthTokenAspect.java @@ -27,7 +27,7 @@ * @author zlt * @date 2020/3/29 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j @@ -46,10 +46,13 @@ public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throw String clientId = getClientId(principal); Map parameters = (Map) args[1]; String grantType = parameters.get(OAuth2Utils.GRANT_TYPE); + if (!parameters.containsKey(SecurityConstants.ACCOUNT_TYPE_PARAM_NAME)) { + parameters.put(SecurityConstants.ACCOUNT_TYPE_PARAM_NAME, SecurityConstants.DEF_ACCOUNT_TYPE); + } //保存租户id TenantContextHolder.setTenant(clientId); - Object proceed = joinPoint.proceed(); + Object proceed = joinPoint.proceed(args); if (SecurityConstants.AUTHORIZATION_CODE.equals(grantType)) { /* 如果使用 @EnableOAuth2Sso 注解不能修改返回格式,否则授权码模式可以统一改 @@ -63,11 +66,6 @@ public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throw .status(HttpStatus.OK) .body(Result.succeed(body)); } - } catch (Exception e) { - log.error("授权错误", e); - return ResponseEntity - .status(HttpStatus.BAD_REQUEST) - .body(Result.failed(e.getMessage())); } finally { TenantContextHolder.clear(); } diff --git a/zlt-uaa/src/main/java/com/central/oauth/filter/ValidateCodeFilter.java b/zlt-uaa/src/main/java/com/central/oauth/filter/ValidateCodeFilter.java deleted file mode 100644 index e6729ea3..00000000 --- a/zlt-uaa/src/main/java/com/central/oauth/filter/ValidateCodeFilter.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.central.oauth.filter; - -import com.central.common.constant.SecurityConstants; -import com.central.oauth.exception.ValidateCodeException; -import com.central.oauth.service.IValidateCodeService; -import com.central.oauth2.common.properties.SecurityProperties; -import com.central.oauth2.common.util.AuthUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.stereotype.Component; -import org.springframework.util.AntPathMatcher; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author zlt - * @date 2018/11/21 - */ -@Slf4j -@Component("validateCodeFilter") -public class ValidateCodeFilter extends OncePerRequestFilter { - @Autowired - private IValidateCodeService validateCodeService; - - @Autowired - private SecurityProperties securityProperties; - - /** - * 验证码校验失败处理器 - */ - @Autowired - private AuthenticationFailureHandler authenticationFailureHandler; - - /** - * 验证请求url与配置的url是否匹配的工具类 - */ - private AntPathMatcher pathMatcher = new AntPathMatcher(); - - /** - * 返回true代表不执行过滤器,false代表执行 - */ - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - //登录提交的时候验证验证码 - if (pathMatcher.match(SecurityConstants.PASSWORD_LOGIN_PRO_URL, request.getRequestURI())) { - //判断是否有不验证验证码的client - if (securityProperties.getCode().getIgnoreClientCode().length > 0) { - try { - final String[] clientInfos = AuthUtils.extractClient(request); - String clientId = clientInfos[0]; - for (String client : securityProperties.getCode().getIgnoreClientCode()) { - if (client.equals(clientId)) { - return true; - } - } - } catch (Exception e) { - log.error("解析client信息失败", e); - } - } - return false; - } - return true; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - try { - validateCodeService.validate(request); - } catch (ValidateCodeException e) { - authenticationFailureHandler.onAuthenticationFailure(request, response, e); - return; - } - chain.doFilter(request, response); - } -} \ No newline at end of file diff --git a/zlt-uaa/src/main/java/com/central/oauth/granter/MobilePwdGranter.java b/zlt-uaa/src/main/java/com/central/oauth/granter/MobilePwdGranter.java new file mode 100644 index 00000000..1f774e0d --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/granter/MobilePwdGranter.java @@ -0,0 +1,53 @@ +package com.central.oauth.granter; + +import com.central.oauth2.common.token.MobileAuthenticationToken; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * openId授权模式 + * + * @author zlt + * @date 2020/7/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class MobilePwdGranter extends AbstractTokenGranter { + private static final String GRANT_TYPE = "mobile_password"; + + private final AuthenticationManager authenticationManager; + + public MobilePwdGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices + , ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { + super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); + this.authenticationManager = authenticationManager; + } + + @Override + protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { + Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); + String mobile = parameters.get("mobile"); + String password = parameters.get("password"); + // Protect from downstream leaks of password + parameters.remove("password"); + + Authentication userAuth = new MobileAuthenticationToken(mobile, password); + ((AbstractAuthenticationToken) userAuth).setDetails(parameters); + userAuth = authenticationManager.authenticate(userAuth); + if (userAuth == null || !userAuth.isAuthenticated()) { + throw new InvalidGrantException("Could not authenticate mobile: " + mobile); + } + + OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); + return new OAuth2Authentication(storedOAuth2Request, userAuth); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/granter/OpenIdGranter.java b/zlt-uaa/src/main/java/com/central/oauth/granter/OpenIdGranter.java new file mode 100644 index 00000000..fc95bce4 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/granter/OpenIdGranter.java @@ -0,0 +1,49 @@ +package com.central.oauth.granter; + +import com.central.oauth2.common.token.OpenIdAuthenticationToken; +import org.springframework.security.authentication.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * openId授权模式 + * + * @author zlt + * @date 2020/7/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class OpenIdGranter extends AbstractTokenGranter { + private static final String GRANT_TYPE = "openId"; + + private final AuthenticationManager authenticationManager; + + public OpenIdGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices + , ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { + super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); + this.authenticationManager = authenticationManager; + } + + @Override + protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { + Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); + String openId = parameters.get("openId"); + + Authentication userAuth = new OpenIdAuthenticationToken(openId); + ((AbstractAuthenticationToken) userAuth).setDetails(parameters); + userAuth = authenticationManager.authenticate(userAuth); + if (userAuth == null || !userAuth.isAuthenticated()) { + throw new InvalidGrantException("Could not authenticate openId: " + openId); + } + + OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); + return new OAuth2Authentication(storedOAuth2Request, userAuth); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/granter/PwdImgCodeGranter.java b/zlt-uaa/src/main/java/com/central/oauth/granter/PwdImgCodeGranter.java new file mode 100644 index 00000000..53d2b0d6 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/granter/PwdImgCodeGranter.java @@ -0,0 +1,42 @@ +package com.central.oauth.granter; + +import com.central.oauth.service.IValidateCodeService; +import org.springframework.security.authentication.*; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * password添加图像验证码授权模式 + * + * @author zlt + * @date 2020/7/11 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class PwdImgCodeGranter extends ResourceOwnerPasswordTokenGranter { + private static final String GRANT_TYPE = "password_code"; + + private final IValidateCodeService validateCodeService; + + public PwdImgCodeGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices + , ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, IValidateCodeService validateCodeService) { + super(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); + this.validateCodeService = validateCodeService; + } + + @Override + protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { + Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); + String deviceId = parameters.get("deviceId"); + String validCode = parameters.get("validCode"); + //校验图形验证码 + validateCodeService.validate(deviceId, validCode); + + return super.getOAuth2Authentication(client, tokenRequest); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/handler/OauthLogoutHandler.java b/zlt-uaa/src/main/java/com/central/oauth/handler/OauthLogoutHandler.java index 41083f12..b3a3d49f 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/handler/OauthLogoutHandler.java +++ b/zlt-uaa/src/main/java/com/central/oauth/handler/OauthLogoutHandler.java @@ -1,28 +1,38 @@ package com.central.oauth.handler; import cn.hutool.core.util.StrUtil; +import com.central.oauth.utils.UsernameHolder; +import com.central.oauth2.common.properties.SecurityProperties; import com.central.oauth2.common.util.AuthUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.util.Assert; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author zlt * @date 2018/10/17 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Slf4j public class OauthLogoutHandler implements LogoutHandler { - @Autowired + @Resource private TokenStore tokenStore; + @Resource + private SecurityProperties securityProperties; + @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { Assert.notNull(tokenStore, "tokenStore must be set"); @@ -31,6 +41,11 @@ public void logout(HttpServletRequest request, HttpServletResponse response, Aut token = AuthUtils.extractToken(request); } if(StrUtil.isNotEmpty(token)){ + if (securityProperties.getAuth().getUnifiedLogout()) { + OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(token); + UsernameHolder.setContext(oAuth2Authentication.getName()); + } + OAuth2AccessToken existingAccessToken = tokenStore.readAccessToken(token); OAuth2RefreshToken refreshToken; if (existingAccessToken != null) { diff --git a/zlt-uaa/src/main/java/com/central/oauth/handler/OauthLogoutSuccessHandler.java b/zlt-uaa/src/main/java/com/central/oauth/handler/OauthLogoutSuccessHandler.java index ba2b7afc..78dae8e0 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/handler/OauthLogoutSuccessHandler.java +++ b/zlt-uaa/src/main/java/com/central/oauth/handler/OauthLogoutSuccessHandler.java @@ -1,8 +1,10 @@ package com.central.oauth.handler; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSON; import com.central.common.model.Result; +import com.central.common.utils.JsonUtil; +import com.central.oauth.service.impl.UnifiedLogoutService; +import com.central.oauth2.common.properties.SecurityProperties; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -11,6 +13,7 @@ import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -20,15 +23,25 @@ * @author zlt * @date 2020/3/10 *

- * Blog: https://blog.csdn.net/zlt2000 + * Blog: https://zlt2000.gitee.io * Github: https://github.com/zlt2000 */ @Slf4j public class OauthLogoutSuccessHandler implements LogoutSuccessHandler { - private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + + @Resource + private UnifiedLogoutService unifiedLogoutService; + + @Resource + private SecurityProperties securityProperties; @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { + if (securityProperties.getAuth().getUnifiedLogout()) { + unifiedLogoutService.allLogout(); + } + String redirectUri = request.getParameter("redirect_uri"); if (StrUtil.isNotEmpty(redirectUri)) { //重定向指定的地址 @@ -36,9 +49,9 @@ public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse resp } else { response.setStatus(HttpStatus.OK.value()); response.setCharacterEncoding("UTF-8"); - response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); PrintWriter writer = response.getWriter(); - String jsonStr = JSON.toJSONString(Result.succeed("登出成功")); + String jsonStr = JsonUtil.toJSONString(Result.succeed("登出成功")); writer.write(jsonStr); writer.flush(); } diff --git a/zlt-uaa/src/main/java/com/central/oauth/listener/RedisKeyExpirationListener.java b/zlt-uaa/src/main/java/com/central/oauth/listener/RedisKeyExpirationListener.java new file mode 100644 index 00000000..eb4e1db7 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/listener/RedisKeyExpirationListener.java @@ -0,0 +1,112 @@ +package com.central.oauth.listener; + +import com.central.common.constant.SecurityConstants; +import com.central.common.redis.template.RedisRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy; +import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy; +import org.springframework.stereotype.Component; + +/** + * + * redis过期key监听器 + * @author zlt + * + */ +@Component +@Slf4j +public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { + @Autowired + private RedisRepository redisRepository; + private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy(); + private final RedisConnectionFactory connectionFactory; + + public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer, RedisConnectionFactory connectionFactory) { + super(listenerContainer); + this.connectionFactory = connectionFactory; + } + + @Override + public void onMessage(Message message, byte[] pattern) { + if (message == null) { + log.debug("message不能为空"); + return; + } + //获取失效的的key + String expiredKey = message.toString(); + if (StringUtils.isEmpty(expiredKey)) { + log.debug("expiredKey不能为空"); + return; + } + String accesskey = expiredKey.substring(0, expiredKey.indexOf(":") + 1); + if (!"access:".equals(accesskey)) { + log.debug("非需要监听key,跳过"); + return; + } + String accessValue = expiredKey.substring(expiredKey.indexOf(":") + 1); + // 分布式集群部署下防止一个过期被多个服务重复消费 + String qc = "qc:" + accessValue; + String oldLock = redisRepository.getAndSet(qc, "1"); + if (StringUtils.isNotEmpty(oldLock) && "1".equals(oldLock)) { + log.debug("其他节点已经处理了该数据,跳过"); + return; + } + byte[] accessBakKey = serializeKey(SecurityConstants.ACCESS_BAK + accessValue); + byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + accessValue); + RedisConnection conn = getConnection(); + try { + byte[] access = conn.get(accessBakKey); + byte[] auth = conn.get(authKey); + OAuth2Authentication authentication = deserializeAuthentication(auth); + if (authentication != null) { + byte[] unameKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(authentication)); + byte[] clientId = serializeKey(SecurityConstants.REDIS_CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId()); + conn.openPipeline(); + conn.lRem(unameKey, 1, access); + conn.lRem(clientId, 1, access); + conn.closePipeline(); + } + } catch (Exception e) { + log.error(e.getMessage()); + } finally { + conn.del(serializeKey(qc)); + conn.close(); + } + + } + + private byte[] serializeKey(String object) { + return serialize("" + object); + } + + private byte[] serialize(String string) { + return serializationStrategy.serialize(string); + } + + private RedisConnection getConnection() { + return connectionFactory.getConnection(); + } + + private OAuth2Authentication deserializeAuthentication(byte[] bytes) { + return serializationStrategy.deserialize(bytes, OAuth2Authentication.class); + } + + private static String getApprovalKey(OAuth2Authentication authentication) { + String userName = authentication.getUserAuthentication() == null ? "" + : authentication.getUserAuthentication().getName(); + return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName); + } + + private static String getApprovalKey(String clientId, String userName) { + return clientId + (userName == null ? "" : ":" + userName); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/mobile/MobileAuthenticationProvider.java b/zlt-uaa/src/main/java/com/central/oauth/mobile/MobileAuthenticationProvider.java index 0d2fc1f3..99e5fcde 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/mobile/MobileAuthenticationProvider.java +++ b/zlt-uaa/src/main/java/com/central/oauth/mobile/MobileAuthenticationProvider.java @@ -1,10 +1,10 @@ package com.central.oauth.mobile; -import com.central.oauth.service.ZltUserDetailsService; +import com.central.oauth.service.impl.UserDetailServiceFactory; import com.central.oauth2.common.token.MobileAuthenticationToken; +import lombok.Getter; import lombok.Setter; import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; @@ -12,10 +12,14 @@ /** * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Setter +@Getter public class MobileAuthenticationProvider implements AuthenticationProvider { - private ZltUserDetailsService userDetailsService; + private UserDetailServiceFactory userDetailsServiceFactory; private PasswordEncoder passwordEncoder; @Override @@ -23,12 +27,12 @@ public Authentication authenticate(Authentication authentication) { MobileAuthenticationToken authenticationToken = (MobileAuthenticationToken) authentication; String mobile = (String) authenticationToken.getPrincipal(); String password = (String) authenticationToken.getCredentials(); - UserDetails user = userDetailsService.loadUserByMobile(mobile); + UserDetails user = userDetailsServiceFactory.getService(authenticationToken).loadUserByMobile(mobile); if (user == null) { throw new InternalAuthenticationServiceException("手机号或密码错误"); } if (!passwordEncoder.matches(password, user.getPassword())) { - throw new BadCredentialsException("手机号或密码错误"); + throw new InternalAuthenticationServiceException("手机号或密码错误"); } MobileAuthenticationToken authenticationResult = new MobileAuthenticationToken(user, password, user.getAuthorities()); authenticationResult.setDetails(authenticationToken.getDetails()); diff --git a/zlt-uaa/src/main/java/com/central/oauth/mobile/MobileAuthenticationSecurityConfig.java b/zlt-uaa/src/main/java/com/central/oauth/mobile/MobileAuthenticationSecurityConfig.java index 12d09abc..4aaa7a9a 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/mobile/MobileAuthenticationSecurityConfig.java +++ b/zlt-uaa/src/main/java/com/central/oauth/mobile/MobileAuthenticationSecurityConfig.java @@ -1,6 +1,6 @@ package com.central.oauth.mobile; -import com.central.oauth.service.ZltUserDetailsService; +import com.central.oauth.service.impl.UserDetailServiceFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -8,15 +8,20 @@ import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.stereotype.Component; +import javax.annotation.Resource; + /** * mobile的相关处理配置 * * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Component public class MobileAuthenticationSecurityConfig extends SecurityConfigurerAdapter { - @Autowired - private ZltUserDetailsService userDetailsService; + @Resource + private UserDetailServiceFactory userDetailsServiceFactory; @Autowired private PasswordEncoder passwordEncoder; @@ -25,7 +30,7 @@ public class MobileAuthenticationSecurityConfig extends SecurityConfigurerAdapte public void configure(HttpSecurity http) { //mobile provider MobileAuthenticationProvider provider = new MobileAuthenticationProvider(); - provider.setUserDetailsService(userDetailsService); + provider.setUserDetailsServiceFactory(userDetailsServiceFactory); provider.setPasswordEncoder(passwordEncoder); http.authenticationProvider(provider); } diff --git a/zlt-uaa/src/main/java/com/central/oauth/model/Client.java b/zlt-uaa/src/main/java/com/central/oauth/model/Client.java index 996aea49..dfe3e9e9 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/model/Client.java +++ b/zlt-uaa/src/main/java/com/central/oauth/model/Client.java @@ -32,4 +32,13 @@ public class Client extends SuperEntity { private Integer refreshTokenValiditySeconds = 28800; private String additionalInformation = "{}"; private String autoapprove = "true"; + /** + * 是否支持id_token + */ + private Boolean supportIdToken = true; + /** + * id_token有效时间(s) + */ + @TableField(value = "id_token_validity") + private Integer idTokenValiditySeconds = 60; } diff --git a/zlt-uaa/src/main/java/com/central/oauth/model/CustomAuthenticationDetailsSource.java b/zlt-uaa/src/main/java/com/central/oauth/model/CustomAuthenticationDetailsSource.java new file mode 100644 index 00000000..77e36ea4 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/model/CustomAuthenticationDetailsSource.java @@ -0,0 +1,31 @@ +package com.central.oauth.model; + +import com.central.common.constant.SecurityConstants; +import com.central.oauth2.common.token.CustomWebAuthenticationDetails; +import org.springframework.security.authentication.AuthenticationDetailsSource; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +/** + * 表单登录的认证信息对象 + * + * @author zlt + * @version 1.0 + * @date 2021/7/21 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Component +public class CustomAuthenticationDetailsSource implements AuthenticationDetailsSource { + @Override + public CustomWebAuthenticationDetails buildDetails(HttpServletRequest context) { + String remoteAddress = context.getRemoteAddr(); + HttpSession session = context.getSession(false); + String sessionId = session != null ? session.getId() : null; + String accountType = context.getParameter(SecurityConstants.ACCOUNT_TYPE_PARAM_NAME); + return new CustomWebAuthenticationDetails(remoteAddress, sessionId, accountType); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/model/TokenVo.java b/zlt-uaa/src/main/java/com/central/oauth/model/TokenVo.java index 3b0ccdd3..2b107138 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/model/TokenVo.java +++ b/zlt-uaa/src/main/java/com/central/oauth/model/TokenVo.java @@ -8,6 +8,9 @@ /** * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Setter @Getter @@ -33,4 +36,8 @@ public class TokenVo implements Serializable { * 授权类型 */ private String grantType; + /** + * 账号类型 + */ + private String accountType; } diff --git a/zlt-uaa/src/main/java/com/central/oauth/openid/OpenIdAuthenticationProvider.java b/zlt-uaa/src/main/java/com/central/oauth/openid/OpenIdAuthenticationProvider.java index 759124ba..4a4623d8 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/openid/OpenIdAuthenticationProvider.java +++ b/zlt-uaa/src/main/java/com/central/oauth/openid/OpenIdAuthenticationProvider.java @@ -1,24 +1,30 @@ package com.central.oauth.openid; +import com.central.oauth.service.impl.UserDetailServiceFactory; import com.central.oauth2.common.token.OpenIdAuthenticationToken; +import lombok.Getter; +import lombok.Setter; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.social.security.SocialUserDetailsService; /** * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ +@Setter +@Getter public class OpenIdAuthenticationProvider implements AuthenticationProvider { - - private SocialUserDetailsService userDetailsService; + private UserDetailServiceFactory userDetailsServiceFactory; @Override public Authentication authenticate(Authentication authentication) { OpenIdAuthenticationToken authenticationToken = (OpenIdAuthenticationToken) authentication; String openId = (String) authenticationToken.getPrincipal(); - UserDetails user = userDetailsService.loadUserByUserId(openId); + UserDetails user = userDetailsServiceFactory.getService(authenticationToken).loadUserByUserId(openId); if (user == null) { throw new InternalAuthenticationServiceException("openId错误"); } @@ -31,12 +37,4 @@ public Authentication authenticate(Authentication authentication) { public boolean supports(Class authentication) { return OpenIdAuthenticationToken.class.isAssignableFrom(authentication); } - - public SocialUserDetailsService getUserDetailsService() { - return userDetailsService; - } - - public void setUserDetailsService(SocialUserDetailsService userDetailsService) { - this.userDetailsService = userDetailsService; - } } diff --git a/zlt-uaa/src/main/java/com/central/oauth/openid/OpenIdAuthenticationSecurityConfig.java b/zlt-uaa/src/main/java/com/central/oauth/openid/OpenIdAuthenticationSecurityConfig.java index 2a63c0b2..f5ef56cb 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/openid/OpenIdAuthenticationSecurityConfig.java +++ b/zlt-uaa/src/main/java/com/central/oauth/openid/OpenIdAuthenticationSecurityConfig.java @@ -1,27 +1,30 @@ package com.central.oauth.openid; +import com.central.oauth.service.impl.UserDetailServiceFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.DefaultSecurityFilterChain; -import org.springframework.social.security.SocialUserDetailsService; import org.springframework.stereotype.Component; /** * openId的相关处理配置 * * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Component public class OpenIdAuthenticationSecurityConfig extends SecurityConfigurerAdapter { @Autowired - private SocialUserDetailsService userDetailsService; + private UserDetailServiceFactory userDetailsServiceFactory; @Override public void configure(HttpSecurity http) { //openId provider OpenIdAuthenticationProvider provider = new OpenIdAuthenticationProvider(); - provider.setUserDetailsService(userDetailsService); + provider.setUserDetailsServiceFactory(userDetailsServiceFactory); http.authenticationProvider(provider); } } diff --git a/zlt-uaa/src/main/java/com/central/oauth/password/PasswordAuthenticationProvider.java b/zlt-uaa/src/main/java/com/central/oauth/password/PasswordAuthenticationProvider.java new file mode 100644 index 00000000..7fbdd579 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/password/PasswordAuthenticationProvider.java @@ -0,0 +1,148 @@ +package com.central.oauth.password; + +import com.central.oauth.service.impl.UserDetailServiceFactory; +import lombok.Getter; +import lombok.Setter; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsPasswordService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.Assert; + +/** + * 扩展用户名密码provider + * + * @author zlt + * @version 1.0 + * @date 2021/7/24 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +@Getter +public class PasswordAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { + private UserDetailServiceFactory userDetailsServiceFactory; + + private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; + + private PasswordEncoder passwordEncoder; + + /** + * The password used to perform + * {@link PasswordEncoder#matches(CharSequence, String)} on when the user is + * not found to avoid SEC-2056. This is necessary, because some + * {@link PasswordEncoder} implementations will short circuit if the password is not + * in a valid format. + */ + private volatile String userNotFoundEncodedPassword; + + private UserDetailsPasswordService userDetailsPasswordService; + + public PasswordAuthenticationProvider() { + setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()); + } + + // ~ Methods + // ======================================================================================================== + + @Override + @SuppressWarnings("deprecation") + protected void additionalAuthenticationChecks(UserDetails userDetails, + UsernamePasswordAuthenticationToken authentication) + throws AuthenticationException { + if (authentication.getCredentials() == null) { + logger.debug("Authentication failed: no credentials provided"); + + throw new BadCredentialsException(messages.getMessage( + "AbstractUserDetailsAuthenticationProvider.badCredentials", + "Bad credentials")); + } + + String presentedPassword = authentication.getCredentials().toString(); + + if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { + logger.debug("Authentication failed: password does not match stored value"); + + throw new BadCredentialsException(messages.getMessage( + "AbstractUserDetailsAuthenticationProvider.badCredentials", + "Bad credentials")); + } + } + + @Override + protected void doAfterPropertiesSet() { + Assert.notNull(this.userDetailsServiceFactory, "A UserDetailsService must be set"); + } + + @Override + protected final UserDetails retrieveUser(String username, + UsernamePasswordAuthenticationToken authentication) + throws AuthenticationException { + prepareTimingAttackProtection(); + try { + UserDetails loadedUser = userDetailsServiceFactory.getService(authentication).loadUserByUsername(username); + if (loadedUser == null) { + throw new InternalAuthenticationServiceException( + "UserDetailsService returned null, which is an interface contract violation"); + } + return loadedUser; + } + catch (UsernameNotFoundException ex) { + mitigateAgainstTimingAttack(authentication); + throw ex; + } + catch (InternalAuthenticationServiceException ex) { + throw ex; + } + catch (Exception ex) { + throw new InternalAuthenticationServiceException(ex.getMessage(), ex); + } + } + + @Override + protected Authentication createSuccessAuthentication(Object principal, + Authentication authentication, UserDetails user) { + boolean upgradeEncoding = this.userDetailsPasswordService != null + && this.passwordEncoder.upgradeEncoding(user.getPassword()); + if (upgradeEncoding) { + String presentedPassword = authentication.getCredentials().toString(); + String newPassword = this.passwordEncoder.encode(presentedPassword); + user = this.userDetailsPasswordService.updatePassword(user, newPassword); + } + return super.createSuccessAuthentication(principal, authentication, user); + } + + private void prepareTimingAttackProtection() { + if (this.userNotFoundEncodedPassword == null) { + this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD); + } + } + + private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) { + if (authentication.getCredentials() != null) { + String presentedPassword = authentication.getCredentials().toString(); + this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword); + } + } + + /** + * Sets the PasswordEncoder instance to be used to encode and validate passwords. If + * not set, the password will be compared using {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()} + * + * @param passwordEncoder must be an instance of one of the {@code PasswordEncoder} + * types. + */ + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { + Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); + this.passwordEncoder = passwordEncoder; + this.userNotFoundEncodedPassword = null; + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/IClientService.java b/zlt-uaa/src/main/java/com/central/oauth/service/IClientService.java index 02f653b7..eed136a1 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/IClientService.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/IClientService.java @@ -9,9 +9,12 @@ /** * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public interface IClientService extends ISuperService { - Result saveClient(Client clientDto); + Result saveClient(Client clientDto) throws Exception; /** * 查询应用列表 @@ -21,4 +24,6 @@ public interface IClientService extends ISuperService { PageResult listClient(Map params, boolean isPage); void delClient(long id); + + Client loadClientByClientId(String clientId); } diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/IValidateCodeService.java b/zlt-uaa/src/main/java/com/central/oauth/service/IValidateCodeService.java index 481fb2e3..75ee0c6c 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/IValidateCodeService.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/IValidateCodeService.java @@ -7,6 +7,9 @@ /** * @author zlt * @date 2018/12/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ public interface IValidateCodeService { /** @@ -33,5 +36,5 @@ public interface IValidateCodeService { /** * 验证验证码 */ - void validate(HttpServletRequest request); + void validate(String deviceId, String validCode); } diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/ZltUserDetailsService.java b/zlt-uaa/src/main/java/com/central/oauth/service/ZltUserDetailsService.java index 5fbb2942..c02809d1 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/ZltUserDetailsService.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/ZltUserDetailsService.java @@ -1,19 +1,35 @@ -package com.central.oauth.service; - -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; - -/** - * @author zlt - * @date 2018/12/28 - */ -public interface ZltUserDetailsService extends UserDetailsService { - /** - * 根据电话号码查询用户 - * - * @param mobile - * @return - */ - UserDetails loadUserByMobile(String mobile); -} +package com.central.oauth.service; + +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.social.security.SocialUserDetails; + +/** + * @author zlt + * @date 2018/12/28 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public interface ZltUserDetailsService extends UserDetailsService { + /** + * 判断实现类是否属于该类型 + * @param accountType 账号类型 + */ + boolean supports(String accountType); + + /** + * 根据电话号码查询用户 + * + * @param mobile + * @return + */ + UserDetails loadUserByMobile(String mobile); + + /** + * 根据用户id/openId查询用户 + * @param userId 用户id/openId + */ + SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException; +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/ClientServiceImpl.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/ClientServiceImpl.java index 16968c3f..5e014195 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/impl/ClientServiceImpl.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/ClientServiceImpl.java @@ -1,8 +1,8 @@ package com.central.oauth.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.central.common.constant.CommonConstant; import com.central.common.lock.DistributedLock; import com.central.common.redis.template.RedisRepository; import com.central.common.constant.SecurityConstants; @@ -23,11 +23,14 @@ /** * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Slf4j @Service public class ClientServiceImpl extends SuperServiceImpl implements IClientService { - private final static String LOCK_KEY_CLIENTID = CommonConstant.LOCK_KEY_PREFIX+"clientId:"; + private final static String LOCK_KEY_CLIENTID = "clientId:"; @Autowired private RedisRepository redisRepository; @@ -39,7 +42,7 @@ public class ClientServiceImpl extends SuperServiceImpl im private DistributedLock lock; @Override - public Result saveClient(Client client) { + public Result saveClient(Client client) throws Exception { client.setClientSecret(passwordEncoder.encode(client.getClientSecretStr())); String clientId = client.getClientId(); super.saveOrUpdateIdempotency(client, lock @@ -69,6 +72,13 @@ public void delClient(long id) { redisRepository.del(clientRedisKey(clientId)); } + @Override + public Client loadClientByClientId(String clientId) { + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("client_id", clientId); + return this.getOne(wrapper); + } + private String clientRedisKey(String clientId) { return SecurityConstants.CACHE_CLIENT_KEY + ":" + clientId; } diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/CustomTokenServices.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/CustomTokenServices.java new file mode 100644 index 00000000..6ce79c31 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/CustomTokenServices.java @@ -0,0 +1,258 @@ +package com.central.oauth.service.impl; + +import com.central.common.constant.SecurityConstants; +import com.central.oauth2.common.properties.AuthProperties; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.*; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.TokenRequest; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.Set; +import java.util.UUID; + +/** + * 重写 DefaultTokenServices,实现登录同应用同账号互踢 + * + * @author zlt + * @date 2021/1/28 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class CustomTokenServices extends DefaultTokenServices { + private TokenStore tokenStore; + private TokenEnhancer accessTokenEnhancer; + private AuthenticationManager authenticationManager; + private boolean supportRefreshToken = false; + private boolean reuseRefreshToken = true; + + /** + * 是否登录同应用同账号互踢 + */ + private final AuthProperties authProperties; + + public CustomTokenServices(AuthProperties auth) { + this.authProperties = auth; + } + + @Override + @Transactional + public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { + OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication); + OAuth2RefreshToken refreshToken = null; + if (existingAccessToken != null) { + if (authProperties.getIsSingleLogin()) { + if (existingAccessToken.getRefreshToken() != null) { + tokenStore.removeRefreshToken(existingAccessToken.getRefreshToken()); + } + tokenStore.removeAccessToken(existingAccessToken); + } else if (existingAccessToken.isExpired()) { + if (existingAccessToken.getRefreshToken() != null) { + refreshToken = existingAccessToken.getRefreshToken(); + // The token store could remove the refresh token when the + // access token is removed, but we want to + // be sure... + tokenStore.removeRefreshToken(refreshToken); + } + tokenStore.removeAccessToken(existingAccessToken); + } else if (authProperties.getIsShareToken()) { + // oidc每次授权都刷新id_token + existingAccessToken = refreshIdToken(existingAccessToken, authentication); + // Re-store the access token in case the authentication has changed + tokenStore.storeAccessToken(existingAccessToken, authentication); + return existingAccessToken; + } + } + + // Only create a new refresh token if there wasn't an existing one + // associated with an expired access token. + // Clients might be holding existing refresh tokens, so we re-use it in + // the case that the old access token + // expired. + if (refreshToken == null) { + refreshToken = createRefreshToken(authentication); + } + // But the refresh token itself might need to be re-issued if it has + // expired. + else if (refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken; + if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { + refreshToken = createRefreshToken(authentication); + } + } + + OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); + tokenStore.storeAccessToken(accessToken, authentication); + // In case it was modified + refreshToken = accessToken.getRefreshToken(); + if (refreshToken != null) { + tokenStore.storeRefreshToken(refreshToken, authentication); + } + return accessToken; + + } + + /** + * oidc每次授权都刷新id_token + * @param token 已存在的token + * @param authentication 认证信息 + */ + private OAuth2AccessToken refreshIdToken(OAuth2AccessToken token, OAuth2Authentication authentication) { + Set responseTypes = authentication.getOAuth2Request().getResponseTypes(); + if (accessTokenEnhancer != null && responseTypes.contains(SecurityConstants.ID_TOKEN)) { + return accessTokenEnhancer.enhance(token, authentication); + } + return token; + } + + private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) { + if (!isSupportRefreshToken(authentication.getOAuth2Request())) { + return null; + } + int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request()); + String value = UUID.randomUUID().toString(); + if (validitySeconds > 0) { + return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis() + + (validitySeconds * 1000L))); + } + return new DefaultOAuth2RefreshToken(value); + } + + private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { + DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); + int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request()); + if (validitySeconds > 0) { + token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); + } + token.setRefreshToken(refreshToken); + token.setScope(authentication.getOAuth2Request().getScope()); + + return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token; + } + + @Override + @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class}) + public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) + throws AuthenticationException { + if (!supportRefreshToken) { + throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); + } + + OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue); + if (refreshToken == null) { + throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); + } + + OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken); + if (this.authenticationManager != null && !authentication.isClientOnly()) { + AbstractAuthenticationToken userAuthentication = (AbstractAuthenticationToken)authentication.getUserAuthentication(); + Object usesrDetails = userAuthentication.getDetails(); + // The client has already been authenticated, but the user authentication might be old now, so give it a + // chance to re-authenticate. + Authentication user = new PreAuthenticatedAuthenticationToken(userAuthentication, "", authentication.getAuthorities()); + user = authenticationManager.authenticate(user); + //保存账号类型 + ((PreAuthenticatedAuthenticationToken)user).setDetails(usesrDetails); + Object details = authentication.getDetails(); + authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user); + authentication.setDetails(details); + } + String clientId = authentication.getOAuth2Request().getClientId(); + if (clientId == null || !clientId.equals(tokenRequest.getClientId())) { + throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue); + } + + // clear out any access tokens already associated with the refresh + // token. + tokenStore.removeAccessTokenUsingRefreshToken(refreshToken); + + if (isExpired(refreshToken)) { + tokenStore.removeRefreshToken(refreshToken); + throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken); + } + + authentication = createRefreshedAuthentication(authentication, tokenRequest); + if (!reuseRefreshToken) { + tokenStore.removeRefreshToken(refreshToken); + refreshToken = createRefreshToken(authentication); + } + + OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); + tokenStore.storeAccessToken(accessToken, authentication); + if (!reuseRefreshToken) { + tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication); + } + return accessToken; + } + + private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication, TokenRequest request) { + OAuth2Authentication narrowed; + Set scope = request.getScope(); + OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request); + if (scope != null && !scope.isEmpty()) { + Set originalScope = clientAuth.getScope(); + if (originalScope == null || !originalScope.containsAll(scope)) { + throw new InvalidScopeException("Unable to narrow the scope of the client authentication to " + scope + + ".", originalScope); + } + else { + clientAuth = clientAuth.narrowScope(scope); + } + } + narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication()); + return narrowed; + } + + @Override + public void setTokenStore(TokenStore tokenStore) { + this.tokenStore = tokenStore; + super.setTokenStore(tokenStore); + } + + @Override + public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) { + this.accessTokenEnhancer = accessTokenEnhancer; + super.setTokenEnhancer(accessTokenEnhancer); + } + + @Override + public void setAuthenticationManager(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + super.setAuthenticationManager(authenticationManager); + } + + /** + * Whether to support the refresh token. + * + * @param supportRefreshToken Whether to support the refresh token. + */ + @Override + public void setSupportRefreshToken(boolean supportRefreshToken) { + this.supportRefreshToken = supportRefreshToken; + super.setSupportRefreshToken(supportRefreshToken); + } + + /** + * Whether to reuse refresh tokens (until expired). + * + * @param reuseRefreshToken Whether to reuse refresh tokens (until expired). + */ + @Override + public void setReuseRefreshToken(boolean reuseRefreshToken) { + this.reuseRefreshToken = reuseRefreshToken; + super.setReuseRefreshToken(reuseRefreshToken); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisAuthorizationCodeServices.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisAuthorizationCodeServices.java index 4dd83f30..59a2409f 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisAuthorizationCodeServices.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisAuthorizationCodeServices.java @@ -1,6 +1,8 @@ package com.central.oauth.service.impl; +import com.central.common.redis.template.RedisRepository; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices; import org.springframework.stereotype.Service; @@ -8,15 +10,21 @@ import java.util.concurrent.TimeUnit; /** - * @author zlt * JdbcAuthorizationCodeServices替换 + * + * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Service public class RedisAuthorizationCodeServices extends RandomValueAuthorizationCodeServices { - private RedisTemplate redisTemplate; + private final RedisRepository redisRepository; + private final RedisSerializer valueSerializer; - public RedisAuthorizationCodeServices(RedisTemplate redisTemplate) { - this.redisTemplate = redisTemplate; + public RedisAuthorizationCodeServices(RedisRepository redisRepository) { + this.redisRepository = redisRepository; + this.valueSerializer = RedisSerializer.java(); } /** @@ -25,14 +33,14 @@ public RedisAuthorizationCodeServices(RedisTemplate redisTemplat */ @Override protected void store(String code, OAuth2Authentication authentication) { - redisTemplate.opsForValue().set(redisKey(code), authentication, 10, TimeUnit.MINUTES); + redisRepository.setExpire(redisKey(code), authentication, 10, TimeUnit.MINUTES, valueSerializer); } @Override protected OAuth2Authentication remove(final String code) { String codeKey = redisKey(code); - OAuth2Authentication token = (OAuth2Authentication) redisTemplate.opsForValue().get(codeKey); - this.redisTemplate.delete(codeKey); + OAuth2Authentication token = (OAuth2Authentication) redisRepository.get(codeKey, valueSerializer); + redisRepository.del(codeKey); return token; } diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisClientDetailsService.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisClientDetailsService.java index aa3f278c..456b91bd 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisClientDetailsService.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisClientDetailsService.java @@ -88,7 +88,7 @@ public void removeClientDetails(String clientId) { * @param clientId */ private void removeRedisCache(String clientId) { - redisTemplate.opsForValue().get(clientRedisKey(clientId)); + redisTemplate.delete(clientRedisKey(clientId)); } /** @@ -101,7 +101,7 @@ public void loadAllClientToCache() { return; } - list.parallelStream().forEach(client -> redisTemplate.opsForValue().set(clientRedisKey(client.getClientId()), client)); + list.forEach(client -> redisTemplate.opsForValue().set(clientRedisKey(client.getClientId()), client)); } private String clientRedisKey(String clientId) { diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisTokensServiceImpl.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisTokensServiceImpl.java index 8a6c81b0..45fe56a5 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisTokensServiceImpl.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/RedisTokensServiceImpl.java @@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.MapUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; @@ -24,6 +25,9 @@ * * @author zlt * @date 2019/7/12 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Slf4j @Service @@ -35,13 +39,14 @@ public class RedisTokensServiceImpl implements ITokensService { public PageResult listTokens(Map params, String clientId) { Integer page = MapUtils.getInteger(params, "page"); Integer limit = MapUtils.getInteger(params, "limit"); - int[] startEnds = PageUtil.transToStartEnd(page, limit); + int[] startEnds = PageUtil.transToStartEnd(page-1, limit); //根据请求参数生成redis的key String redisKey = getRedisKey(params, clientId); long size = redisRepository.length(redisKey); List result = new ArrayList<>(limit); + RedisSerializer valueSerializer = RedisSerializer.java(); //查询token集合 - List tokenObjs = redisRepository.getList(redisKey, startEnds[0], startEnds[1]-1); + List tokenObjs = redisRepository.getList(redisKey, startEnds[0], startEnds[1]-1, valueSerializer); if (tokenObjs != null) { for (Object obj : tokenObjs) { DefaultOAuth2AccessToken accessToken = (DefaultOAuth2AccessToken)obj; @@ -51,7 +56,7 @@ public PageResult listTokens(Map params, String clientI tokenVo.setExpiration(accessToken.getExpiration()); //获取用户信息 - Object authObj = redisRepository.get(SecurityConstants.REDIS_TOKEN_AUTH + accessToken.getValue()); + Object authObj = redisRepository.get(SecurityConstants.REDIS_TOKEN_AUTH + accessToken.getValue(), valueSerializer); OAuth2Authentication authentication = (OAuth2Authentication)authObj; if (authentication != null) { OAuth2Request request = authentication.getOAuth2Request(); @@ -60,6 +65,10 @@ public PageResult listTokens(Map params, String clientI tokenVo.setGrantType(request.getGrantType()); } + Map additionalInformation = accessToken.getAdditionalInformation(); + String accountType = (String)additionalInformation.get(SecurityConstants.ACCOUNT_TYPE_PARAM_NAME); + tokenVo.setAccountType(accountType); + result.add(tokenVo); } } diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/UnifiedLogoutService.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/UnifiedLogoutService.java new file mode 100644 index 00000000..8fe2bf36 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/UnifiedLogoutService.java @@ -0,0 +1,112 @@ +package com.central.oauth.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.central.oauth.utils.UsernameHolder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.task.TaskExecutor; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import java.util.*; + +/** + * 统一登出服务 + * + * @author zlt + * @version 1.0 + * @date 2021/10/21 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@Service +public class UnifiedLogoutService { + private final static String LOGOUT_NOTIFY_URL_KEY = "LOGOUT_NOTIFY_URL_LIST"; + + @Resource + private RestTemplate restTemplate; + + @Resource + private TaskExecutor taskExecutor; + + @Resource + private RedisClientDetailsService redisClientDetailsService; + + @Resource + private TokenStore tokenStore; + + @Lazy + @Resource + private DefaultTokenServices tokenServices; + + public void allLogout() { + Set urls = this.getLogoutNotifyUrl(); + for (String url : urls) { + taskExecutor.execute(() -> { + try { + restTemplate.getForObject(url, Void.class); + } catch (Exception e) { + log.warn(e.getMessage()); + } + }); + } + } + + /** + * 获取登出需要通知的地址集合 + */ + private Set getLogoutNotifyUrl() { + String username = UsernameHolder.getContext(); + Set logoutNotifyUrls = new HashSet<>(); + try { + List clientDetails = redisClientDetailsService.listClientDetails(); + Map informationMap; + for (ClientDetails client : clientDetails) { + informationMap = client.getAdditionalInformation(); + String urls = (String)informationMap.get(LOGOUT_NOTIFY_URL_KEY); + if (StrUtil.isNotEmpty(urls)) { + Collection tokens = tokenStore.findTokensByClientIdAndUserName(client.getClientId(), username); + if (CollUtil.isNotEmpty(tokens)) { + //注销所有该用户名下的token + tokens.forEach(t -> tokenServices.revokeToken(t.getValue())); + String tokenStr = getTokenValueStr(tokens); + logoutNotifyUrls.addAll(generateNotifyUrls(urls.split(","), tokenStr)); + } + } + } + } finally { + UsernameHolder.clearContext(); + } + return logoutNotifyUrls; + } + + private String getTokenValueStr(Collection tokens) { + if (CollUtil.isNotEmpty(tokens)) { + return StrUtil.join( ",", tokens); + } + return null; + } + + private Set generateNotifyUrls(String[] urls, String tokenStr) { + Set urlSet = new HashSet(urls.length); + for (String url : urls) { + StringBuilder urlBuilder = new StringBuilder(url); + if (url.contains("?")) { + urlBuilder.append("&"); + } else { + urlBuilder.append("?"); + } + urlBuilder.append("tokens=").append(tokenStr); + urlSet.add(urlBuilder.toString()); + } + return urlSet; + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailServiceFactory.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailServiceFactory.java new file mode 100644 index 00000000..c8d61c3f --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailServiceFactory.java @@ -0,0 +1,53 @@ +package com.central.oauth.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.central.common.constant.SecurityConstants; +import com.central.oauth.service.ZltUserDetailsService; +import com.central.oauth2.common.util.AuthUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 用户service工厂 + * + * @author zlt + * @version 1.0 + * @date 2021/7/24 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@Service +public class UserDetailServiceFactory { + private static final String ERROR_MSG = "找不到账号类型为 {} 的实现类"; + + @Resource + private List userDetailsServices; + + public ZltUserDetailsService getService(Authentication authentication) { + String accountType = AuthUtils.getAccountType(authentication); + return this.getService(accountType); + } + + public ZltUserDetailsService getService(String accountType) { + if (StrUtil.isEmpty(accountType)) { + accountType = SecurityConstants.DEF_ACCOUNT_TYPE; + } + log.info("UserDetailServiceFactory.getService:{}", accountType); + if (CollUtil.isNotEmpty(userDetailsServices)) { + for (ZltUserDetailsService userService : userDetailsServices) { + if (userService.supports(accountType)) { + return userService; + } + } + } + throw new InternalAuthenticationServiceException(StrUtil.format(ERROR_MSG, accountType)); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailServiceImpl.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailServiceImpl.java index 4e18ed7b..b240fd4f 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailServiceImpl.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailServiceImpl.java @@ -1,54 +1,64 @@ -package com.central.oauth.service.impl; - -import com.central.common.feign.UserService; -import com.central.oauth.service.ZltUserDetailsService; -import org.springframework.security.authentication.DisabledException; -import org.springframework.security.authentication.InternalAuthenticationServiceException; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.social.security.SocialUserDetails; -import org.springframework.social.security.SocialUserDetailsService; -import org.springframework.stereotype.Service; - -import com.central.common.model.LoginAppUser; - -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Resource; - -/** - * @author zlt - */ -@Slf4j -@Service -public class UserDetailServiceImpl implements ZltUserDetailsService, SocialUserDetailsService { - @Resource - private UserService userService; - - @Override - public UserDetails loadUserByUsername(String username) { - LoginAppUser loginAppUser = userService.findByUsername(username); - if (loginAppUser == null) { - throw new InternalAuthenticationServiceException("用户名或密码错误"); - } - return checkUser(loginAppUser); - } - - @Override - public SocialUserDetails loadUserByUserId(String openId) { - LoginAppUser loginAppUser = userService.findByOpenId(openId); - return checkUser(loginAppUser); - } - - @Override - public UserDetails loadUserByMobile(String mobile) { - LoginAppUser loginAppUser = userService.findByMobile(mobile); - return checkUser(loginAppUser); - } - - private LoginAppUser checkUser(LoginAppUser loginAppUser) { - if (loginAppUser != null && !loginAppUser.isEnabled()) { - throw new DisabledException("用户已作废"); - } - return loginAppUser; - } -} +package com.central.oauth.service.impl; + +import com.central.common.constant.SecurityConstants; +import com.central.common.feign.UserService; +import com.central.oauth.service.ZltUserDetailsService; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.social.security.SocialUserDetails; +import org.springframework.stereotype.Service; + +import com.central.common.model.LoginAppUser; + +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Resource; + +/** + * @author zlt + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@Service +public class UserDetailServiceImpl implements ZltUserDetailsService { + private static final String ACCOUNT_TYPE = SecurityConstants.DEF_ACCOUNT_TYPE; + + @Resource + private UserService userService; + + @Override + public boolean supports(String accountType) { + return ACCOUNT_TYPE.equals(accountType); + } + + @Override + public UserDetails loadUserByUsername(String username) { + LoginAppUser loginAppUser = userService.findByUsername(username); + if (loginAppUser == null) { + throw new InternalAuthenticationServiceException("用户名或密码错误"); + } + return checkUser(loginAppUser); + } + + @Override + public SocialUserDetails loadUserByUserId(String openId) { + LoginAppUser loginAppUser = userService.findByOpenId(openId); + return checkUser(loginAppUser); + } + + @Override + public UserDetails loadUserByMobile(String mobile) { + LoginAppUser loginAppUser = userService.findByMobile(mobile); + return checkUser(loginAppUser); + } + + private LoginAppUser checkUser(LoginAppUser loginAppUser) { + if (loginAppUser != null && !loginAppUser.isEnabled()) { + throw new DisabledException("用户已作废"); + } + return loginAppUser; + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailsByNameServiceFactoryWrapper.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailsByNameServiceFactoryWrapper.java new file mode 100644 index 00000000..39f02f2b --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/UserDetailsByNameServiceFactoryWrapper.java @@ -0,0 +1,59 @@ +package com.central.oauth.service.impl; + +import com.central.oauth.service.ZltUserDetailsService; +import lombok.Setter; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.*; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.util.Assert; + +/** + * 重写 UserDetailsByNameServiceWrapper 支持多帐户类型 + * + * @author zlt + * @version 1.0 + * @date 2021/7/24 + * @see UserDetailsByNameServiceWrapper + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class UserDetailsByNameServiceFactoryWrapper implements + AuthenticationUserDetailsService, InitializingBean { + @Setter + private UserDetailServiceFactory userDetailServiceFactory; + + public UserDetailsByNameServiceFactoryWrapper() { + + } + + public UserDetailsByNameServiceFactoryWrapper(final UserDetailServiceFactory userDetailServiceFactory) { + Assert.notNull(userDetailServiceFactory, "userDetailServiceFactory cannot be null."); + this.userDetailServiceFactory = userDetailServiceFactory; + } + + /** + * Check whether all required properties have been set. + * + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + @Override + public void afterPropertiesSet() { + Assert.notNull(this.userDetailServiceFactory, "UserDetailServiceFactory must be set"); + } + + /** + * Get the UserDetails object from the wrapped UserDetailsService implementation + */ + @Override + public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException { + ZltUserDetailsService userDetailsService; + if (authentication instanceof PreAuthenticatedAuthenticationToken) { + userDetailsService = this.userDetailServiceFactory.getService((Authentication)authentication.getPrincipal()); + } else { + userDetailsService = this.userDetailServiceFactory.getService(authentication); + } + return userDetailsService.loadUserByUsername(authentication.getName()); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/ValidateCodeServiceImpl.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/ValidateCodeServiceImpl.java index 3718c080..e217587c 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/service/impl/ValidateCodeServiceImpl.java +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/ValidateCodeServiceImpl.java @@ -1,6 +1,7 @@ package com.central.oauth.service.impl; import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; import com.central.common.feign.UserService; import com.central.common.redis.template.RedisRepository; import com.central.common.constant.SecurityConstants; @@ -9,18 +10,17 @@ import com.central.oauth.exception.ValidateCodeException; import com.central.oauth.service.IValidateCodeService; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.web.bind.ServletRequestBindingException; -import org.springframework.web.bind.ServletRequestUtils; import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; /** * @author zlt * @date 2018/12/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 */ @Slf4j @Service @@ -95,19 +95,12 @@ public void remove(String deviceId) { * 验证验证码 */ @Override - public void validate(HttpServletRequest request) { - String deviceId = request.getParameter("deviceId"); - if (StringUtils.isBlank(deviceId)) { + public void validate(String deviceId, String validCode) { + if (StrUtil.isBlank(deviceId)) { throw new ValidateCodeException("请在请求参数中携带deviceId参数"); } String code = this.getCode(deviceId); - String codeInRequest; - try { - codeInRequest = ServletRequestUtils.getStringParameter(request, "validCode"); - } catch (ServletRequestBindingException e) { - throw new ValidateCodeException("获取验证码的值失败"); - } - if (StringUtils.isBlank(codeInRequest)) { + if (StrUtil.isBlank(validCode)) { throw new ValidateCodeException("请填写验证码"); } @@ -115,7 +108,7 @@ public void validate(HttpServletRequest request) { throw new ValidateCodeException("验证码不存在或已过期"); } - if (!StringUtils.equals(code, codeInRequest.toLowerCase())) { + if (!StrUtil.equals(code, validCode.toLowerCase())) { throw new ValidateCodeException("验证码不正确"); } diff --git a/zlt-uaa/src/main/java/com/central/oauth/tenant/OauthAuthorizeAspect.java b/zlt-uaa/src/main/java/com/central/oauth/tenant/OauthAuthorizeAspect.java new file mode 100644 index 00000000..8dec5042 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/tenant/OauthAuthorizeAspect.java @@ -0,0 +1,64 @@ +package com.central.oauth.tenant; + +import com.central.common.context.TenantContextHolder; +import com.central.common.model.LoginAppUser; +import com.central.oauth.service.impl.UserDetailServiceFactory; +import com.central.oauth2.common.token.TenantUsernamePasswordAuthenticationToken; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.security.oauth2.common.util.OAuth2Utils; +import org.springframework.stereotype.Component; + +import java.security.Principal; +import java.util.Map; + +/** + * /oauth/authorize拦截器 + * 解决不同租户单点登录时角色没变化 + * + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@Component +@Aspect +public class OauthAuthorizeAspect { + private final UserDetailServiceFactory userDetailsServiceFactory; + + public OauthAuthorizeAspect(UserDetailServiceFactory userDetailsServiceFactory) { + this.userDetailsServiceFactory = userDetailsServiceFactory; + } + + @Around("execution(* org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(..))") + public Object doAroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { + Object[] args = joinPoint.getArgs(); + Map parameters = (Map) args[1]; + Principal principal = (Principal) args[3]; + if (principal instanceof TenantUsernamePasswordAuthenticationToken) { + TenantUsernamePasswordAuthenticationToken tenantToken = (TenantUsernamePasswordAuthenticationToken)principal; + String clientId = tenantToken.getClientId(); + String requestClientId = parameters.get(OAuth2Utils.CLIENT_ID); + //判断是否不同租户单点登录 + if (!requestClientId.equals(clientId)) { + Object details = tenantToken.getDetails(); + try { + TenantContextHolder.setTenant(requestClientId); + //重新查询对应该租户的角色等信息 + LoginAppUser user = (LoginAppUser)userDetailsServiceFactory.getService(tenantToken) + .loadUserByUsername(tenantToken.getName()); + tenantToken = new TenantUsernamePasswordAuthenticationToken(user, tenantToken.getCredentials(), user.getAuthorities(), requestClientId); + tenantToken.setDetails(details); + args[3] = tenantToken; + } finally { + TenantContextHolder.clear(); + } + } + } + return joinPoint.proceed(args); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationProvider.java b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationProvider.java new file mode 100644 index 00000000..c76d0ddc --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationProvider.java @@ -0,0 +1,32 @@ +package com.central.oauth.tenant; + +import com.central.oauth.password.PasswordAuthenticationProvider; +import com.central.oauth2.common.token.TenantUsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * 增加租户id,解决不同租户单点登录时角色没变化 + * + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class TenantAuthenticationProvider extends PasswordAuthenticationProvider { + @Override + protected Authentication createSuccessAuthentication(Object principal, + Authentication authentication, UserDetails user) { + TenantUsernamePasswordAuthenticationToken authenticationToken = (TenantUsernamePasswordAuthenticationToken) authentication; + TenantUsernamePasswordAuthenticationToken result = new TenantUsernamePasswordAuthenticationToken( + principal, authentication.getCredentials(), user.getAuthorities(), authenticationToken.getClientId()); + result.setDetails(authenticationToken.getDetails()); + return result; + } + + @Override + public boolean supports(Class authentication) { + return TenantUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationSecurityConfig.java b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationSecurityConfig.java new file mode 100644 index 00000000..1d00c9a9 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationSecurityConfig.java @@ -0,0 +1,35 @@ +package com.central.oauth.tenant; + +import com.central.oauth.service.impl.UserDetailServiceFactory; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.stereotype.Component; + +/** + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Component +public class TenantAuthenticationSecurityConfig extends SecurityConfigurerAdapter { + private final UserDetailServiceFactory userDetailsServiceFactory; + + private final PasswordEncoder passwordEncoder; + + public TenantAuthenticationSecurityConfig(UserDetailServiceFactory userDetailsServiceFactory, PasswordEncoder passwordEncoder) { + this.userDetailsServiceFactory = userDetailsServiceFactory; + this.passwordEncoder = passwordEncoder; + } + + @Override + public void configure(HttpSecurity http) { + TenantAuthenticationProvider provider = new TenantAuthenticationProvider(); + provider.setUserDetailsServiceFactory(userDetailsServiceFactory); + provider.setPasswordEncoder(passwordEncoder); + http.authenticationProvider(provider); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantUsernamePasswordAuthenticationFilter.java b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantUsernamePasswordAuthenticationFilter.java new file mode 100644 index 00000000..2b6779d9 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantUsernamePasswordAuthenticationFilter.java @@ -0,0 +1,59 @@ +package com.central.oauth.tenant; + +import com.central.common.context.TenantContextHolder; +import com.central.oauth2.common.token.TenantUsernamePasswordAuthenticationToken; +import lombok.Setter; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 替换 UsernamePasswordAuthenticationFilter 增加租户id + * + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +public class TenantUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + private boolean postOnly = true; + + public TenantUsernamePasswordAuthenticationFilter () { + super(); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, + HttpServletResponse response) throws AuthenticationException { + if (postOnly && !request.getMethod().equals("POST")) { + throw new AuthenticationServiceException( + "Authentication method not supported: " + request.getMethod()); + } + + String username = obtainUsername(request); + String password = obtainPassword(request); + String clientId = TenantContextHolder.getTenant(); + + if (username == null) { + username = ""; + } + + if (password == null) { + password = ""; + } + + username = username.trim(); + + TenantUsernamePasswordAuthenticationToken authRequest = new TenantUsernamePasswordAuthenticationToken(username, password, clientId); + + setDetails(request, authRequest); + + return getAuthenticationManager().authenticate(authRequest); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/utils/OidcIdTokenBuilder.java b/zlt-uaa/src/main/java/com/central/oauth/utils/OidcIdTokenBuilder.java new file mode 100644 index 00000000..f38694bb --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/utils/OidcIdTokenBuilder.java @@ -0,0 +1,132 @@ +package com.central.oauth.utils; + +import com.central.common.utils.JsonUtil; +import com.central.oauth2.common.util.JwtUtils; +import lombok.Getter; +import org.springframework.cloud.bootstrap.encrypt.KeyProperties; +import org.springframework.util.Assert; + +import java.util.*; + +import static com.central.oauth2.common.constants.IdTokenClaimNames.*; + +/** + * Oidc协议的IdToken + * + * @author zlt + * @version 1.0 + * @date 2021/4/21 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Getter +public class OidcIdTokenBuilder { + private final Map claims; + private final KeyProperties keyProperties; + + private OidcIdTokenBuilder(KeyProperties keyProperties) { + this.claims = new LinkedHashMap<>(); + this.keyProperties = keyProperties; + } + + public static OidcIdTokenBuilder builder(KeyProperties keyProperties) { + Assert.notNull(keyProperties, "keyProperties required"); + return new OidcIdTokenBuilder(keyProperties); + } + + /** + * Use this claim in the resulting {@link OidcIdTokenBuilder} + * + * @param name The claim name + * @param value The claim value + */ + public OidcIdTokenBuilder claim(String name, Object value) { + this.claims.put(name, value); + return this; + } + + /** + * Use this audience in the resulting {@link OidcIdTokenBuilder} + * + * @param audience The audience to use + */ + public OidcIdTokenBuilder audience(String audience) { + return claim(AUD, audience); + } + + /** + * Use this expiration in the resulting {@link OidcIdTokenBuilder} + * + * @param expiresAt The expiration to use + */ + public OidcIdTokenBuilder expiresAt(long expiresAt) { + return this.claim(EXP, expiresAt); + } + + /** + * Use this issued-at timestamp in the resulting {@link OidcIdTokenBuilder} + * + * @param issuedAt The issued-at timestamp to use + */ + public OidcIdTokenBuilder issuedAt(long issuedAt) { + return this.claim(IAT, issuedAt); + } + + /** + * Use this issuer in the resulting {@link OidcIdTokenBuilder} + * + * @param issuer The issuer to use + */ + public OidcIdTokenBuilder issuer(String issuer) { + return this.claim(ISS, issuer); + } + + /** + * Use this nonce in the resulting {@link OidcIdTokenBuilder} + * + * @param nonce The nonce to use + */ + public OidcIdTokenBuilder nonce(String nonce) { + return this.claim(NONCE, nonce); + } + + /** + * Use this subject in the resulting {@link OidcIdTokenBuilder} + * + * @param subject The subject to use + */ + public OidcIdTokenBuilder subject(String subject) { + return this.claim(SUB, subject); + } + + /** + * 赋值用户姓名 + */ + public OidcIdTokenBuilder name(String name) { + return this.claim(NAME, name); + } + + /** + * 赋值用户登录名 + */ + public OidcIdTokenBuilder loginName(String loginName) { + return this.claim(L_NAME, loginName); + } + + /** + * 赋值用户头像 + */ + public OidcIdTokenBuilder picture(String picture) { + return this.claim(PIC, picture); + } + + /** + * Build the {@link OidcIdTokenBuilder} + * + * @return The constructed {@link OidcIdTokenBuilder} + */ + public String build() { + return JwtUtils.encodeStr(JsonUtil.toJSONString(claims), keyProperties); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/utils/UsernameHolder.java b/zlt-uaa/src/main/java/com/central/oauth/utils/UsernameHolder.java new file mode 100644 index 00000000..1543818c --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/utils/UsernameHolder.java @@ -0,0 +1,25 @@ +package com.central.oauth.utils; + +/** + * @author zlt + * @version 1.0 + * @date 2021/10/22 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class UsernameHolder { + private static final ThreadLocal contextHolder = new ThreadLocal(); + + public static String getContext() { + return contextHolder.get(); + } + + public static void setContext(String username) { + contextHolder.set(username); + } + + public static void clearContext() { + contextHolder.remove(); + } +} diff --git a/zlt-uaa/src/main/resources/application.yml b/zlt-uaa/src/main/resources/application.yml index 03611a0f..0527d6b4 100644 --- a/zlt-uaa/src/main/resources/application.yml +++ b/zlt-uaa/src/main/resources/application.yml @@ -27,4 +27,14 @@ zlt: security: code: # 忽略验证码的应用编号 - ignoreClientCode: app \ No newline at end of file + ignoreClientCode: app + auth: + # 开启统一登出 + unifiedLogout: true + # 同账号登录互踢 + isSingleLogin: true + #多租户配置 + tenant: + enable: true + ignoreTables: + - oauth_client_details \ No newline at end of file diff --git a/zlt-uaa/src/main/resources/mapper/ClientMapper.xml b/zlt-uaa/src/main/resources/mapper/ClientMapper.xml index 6a23273d..3c00e8d4 100644 --- a/zlt-uaa/src/main/resources/mapper/ClientMapper.xml +++ b/zlt-uaa/src/main/resources/mapper/ClientMapper.xml @@ -47,6 +47,10 @@ diff --git a/zlt-uaa/src/main/resources/static/js/common.js b/zlt-uaa/src/main/resources/static/js/common.js index 79fbd8e7..caffc5e0 100644 --- a/zlt-uaa/src/main/resources/static/js/common.js +++ b/zlt-uaa/src/main/resources/static/js/common.js @@ -34,4 +34,9 @@ $(function(){ $(this).parent().next().hide(); } }); + + let query = location.search; + if (query === '?error') { + $('#loginError').show(); + } }); \ No newline at end of file diff --git a/zlt-uaa/src/main/resources/static/login.html b/zlt-uaa/src/main/resources/static/login.html index 6802f408..a1c0453c 100644 --- a/zlt-uaa/src/main/resources/static/login.html +++ b/zlt-uaa/src/main/resources/static/login.html @@ -30,6 +30,7 @@

+
用户名或密码错误
  diff --git a/zlt-uaa/src/test/java/com/central/oauth2/common/util/JwtUtilsTest.java b/zlt-uaa/src/test/java/com/central/oauth2/common/util/JwtUtilsTest.java index 37c167b8..881e05fb 100644 --- a/zlt-uaa/src/test/java/com/central/oauth2/common/util/JwtUtilsTest.java +++ b/zlt-uaa/src/test/java/com/central/oauth2/common/util/JwtUtilsTest.java @@ -1,6 +1,6 @@ package com.central.oauth2.common.util; -import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.junit4.SpringRunner; @@ -16,7 +16,7 @@ public class JwtUtilsTest { @Test public void test() { String jwtToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoiYWJjIiwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6WyJhcHAiXSwiZXhwIjoxNTYzNjgyMTI4LCJhdXRob3JpdGllcyI6WyJBRE1JTiJdLCJqdGkiOiJlMDFlNGU0Yi1hZDVkLTRlMTQtODhiMC00OGQ4YzBjN2U5YjkiLCJjbGllbnRfaWQiOiJ3ZWJBcHAifQ.Qrh2aEoN4TL_WIQ9UpxDrW12aqqoVqxeY826sjbea2LB24RBNDYQl1J5vwXzMaQlG9AgjHRL4bTQihwBYYfdL-VuJXx0_l0xONbz9sHPq60a3gAhxOnekNS5-Qet5feTw7j4o2OwNlxo-xty5s8u2lsQY21zCe0tes_T4XeM76JTBpRbQUFGUU3EKxtUFi3Nk9AII4zerW1AbQNvLo4YW2Wvj___0lq5a-xNdCcHlJid8vKgzEF3v3wECOv6OjgL-fUN8VpUsYVt1-_QZp8opPAf-t3OVTtrVIWrJZ_vWV9d6DN5mynKtZ7_mDyMwo_5w3roAZ0ahoBKPKrtYQyEwQ"; - JSONObject claims = JwtUtils.decodeAndVerify(jwtToken); + JsonNode claims = JwtUtils.decodeAndVerify(jwtToken); //token内容 System.out.println(claims); boolean isValid = JwtUtils.checkExp(claims); diff --git a/zlt-web/back-web/Dockerfile b/zlt-web/back-web/Dockerfile new file mode 100644 index 00000000..fc40b43a --- /dev/null +++ b/zlt-web/back-web/Dockerfile @@ -0,0 +1,9 @@ +FROM nginx:1.21.0 + +LABEL MAINTAINER=Andy +RUN rm -rf /usr/share/nginx/html/* +COPY src/main/resources/static/ /usr/share/nginx/html/ + +# docker run --name zlt-web \ +# -v /host/path/apiUrl.js:/usr/share/nginx/html/module/apiUrl.js:ro +# -d zlt-web:4.5 \ No newline at end of file diff --git a/zlt-web/back-web/pom.xml b/zlt-web/back-web/pom.xml index 96508a96..e30a22c3 100644 --- a/zlt-web/back-web/pom.xml +++ b/zlt-web/back-web/pom.xml @@ -6,7 +6,7 @@ com.zlt zlt-web - 3.5.0 + 5.4.0 back-web 后台管理前端 diff --git a/zlt-web/back-web/src/main/resources/static/index.html b/zlt-web/back-web/src/main/resources/static/index.html index 5095ca33..b8ba735d 100644 --- a/zlt-web/back-web/src/main/resources/static/index.html +++ b/zlt-web/back-web/src/main/resources/static/index.html @@ -4,7 +4,7 @@ - zlt + zlt-mp diff --git a/zlt-web/back-web/src/main/resources/static/login.html b/zlt-web/back-web/src/main/resources/static/login.html index c398802f..59807664 100644 --- a/zlt-web/back-web/src/main/resources/static/login.html +++ b/zlt-web/back-web/src/main/resources/static/login.html @@ -47,6 +47,7 @@
+
@@ -83,7 +84,7 @@ form.on('submit(login-submit)', function (obj) { layer.load(2); $.ajax({ - url: config.base_server + 'api-uaa/oauth/user/token', + url: config.base_server + 'api-uaa/oauth/token', xhrFields: { withCredentials: true }, diff --git a/zlt-web/back-web/src/main/resources/static/module/index.js b/zlt-web/back-web/src/main/resources/static/module/index.js index 5a10849e..183a5f3c 100644 --- a/zlt-web/back-web/src/main/resources/static/module/index.js +++ b/zlt-web/back-web/src/main/resources/static/module/index.js @@ -214,12 +214,26 @@ layui.define(['config', 'admin', 'layer', 'laytpl', 'element', 'form'], function $('#btnLogout').click(function () { layer.confirm('确定退出登录?', function () { let token = config.getToken(); - let accessToken; + let isExistsToken = false; if (token) { - accessToken = token.access_token; + let accessToken = token.access_token; + config.removeToken(); + + if (accessToken) { + isExistsToken = true; + admin.req('api-uaa/oauth/check_token?token='+accessToken, {}, function (data) { + if (data.active) { + let loginPageUrl = window.location.protocol + '//' + window.location.host + '/login.html'; + window.location = config.base_server + 'api-uaa/oauth/remove/token?redirect_uri='+loginPageUrl+'&access_token='+accessToken; + } else { + location.replace('login.html'); + } + }, 'POST'); + } + } + if (!isExistsToken) { + location.replace('login.html'); } - config.removeToken(); - window.location = config.base_server + 'api-uaa/oauth/remove/token?redirect_uri=http://127.0.0.1:8066/login.html&access_token='+accessToken; }); }); // 修改密码 diff --git a/zlt-web/back-web/src/main/resources/static/pages/attestation/app.html b/zlt-web/back-web/src/main/resources/static/pages/attestation/app.html index 812cdfc9..b87fd0d8 100644 --- a/zlt-web/back-web/src/main/resources/static/pages/attestation/app.html +++ b/zlt-web/back-web/src/main/resources/static/pages/attestation/app.html @@ -26,7 +26,7 @@

应用管理

\ No newline at end of file diff --git a/zlt-web/back-web/src/main/resources/static/pages/log/traceLog.html b/zlt-web/back-web/src/main/resources/static/pages/log/traceLog.html new file mode 100644 index 00000000..9b62f01a --- /dev/null +++ b/zlt-web/back-web/src/main/resources/static/pages/log/traceLog.html @@ -0,0 +1,45 @@ + +
+ + \ No newline at end of file diff --git a/zlt-web/back-web/src/main/resources/static/pages/search/index_manager_form.html b/zlt-web/back-web/src/main/resources/static/pages/search/index_manager_form.html index 93e93a0a..b7fed557 100644 --- a/zlt-web/back-web/src/main/resources/static/pages/search/index_manager_form.html +++ b/zlt-web/back-web/src/main/resources/static/pages/search/index_manager_form.html @@ -18,12 +18,6 @@ -
- -
- -
-
diff --git a/zlt-web/back-web/src/main/resources/static/pages/system/menus.html b/zlt-web/back-web/src/main/resources/static/pages/system/menus.html index d678b13f..23d3861e 100644 --- a/zlt-web/back-web/src/main/resources/static/pages/system/menus.html +++ b/zlt-web/back-web/src/main/resources/static/pages/system/menus.html @@ -39,14 +39,7 @@

菜单管理

let form = layui.form; // 渲染表格 - var renderTable = function (param) { - if (param) { - param.access_token = config.getToken().access_token; - } else { - param = { - access_token: config.getToken().access_token - }; - } + var renderTable = function (tenantId) { treetable.render({ treeColIndex: 1, treeSpid: -1, @@ -54,7 +47,10 @@

菜单管理

treePidName: 'parentId', elem: '#menus-table', url: config.base_server + 'api-user/menus/findAlls', - where: param, + where: { + access_token: config.getToken().access_token, + tenantId: tenantId + }, page: false, cols: [[ {type: 'numbers'}, @@ -79,7 +75,7 @@

菜单管理

]] }); }; - renderTable({tenantId: config.clientId}); + renderTable(config.clientId); // 获取应用列表 layer.load(2); @@ -117,7 +113,7 @@

菜单管理

layer.closeAll('loading'); if (0 === data.resp_code) { layer.msg(data.resp_msg, {icon: 1, time: 500}); - renderTable({tenantId: tenantId}); + renderTable(tenantId); } else { layer.msg(data.resp_msg, {icon: 2, time: 500}); } @@ -143,7 +139,7 @@

菜单管理

title: title, path: 'pages/system/menus_form.html', finish: function () { - renderTable({tenantId: tenantId}); + renderTable(tenantId); } }); }; @@ -186,7 +182,7 @@

菜单管理

// 应用下来框点击事件 form.on('select(menu_clients)', function(data){ - renderTable({tenantId: data.value}); + renderTable(data.value); }); }); \ No newline at end of file diff --git a/zlt-web/back-web/src/main/resources/static/pages/system/tokens.html b/zlt-web/back-web/src/main/resources/static/pages/system/tokens.html index e0352e1b..1bbbe9c5 100644 --- a/zlt-web/back-web/src/main/resources/static/pages/system/tokens.html +++ b/zlt-web/back-web/src/main/resources/static/pages/system/tokens.html @@ -46,11 +46,12 @@

token管理

{ sort: true, templet: function (d) { return util.toDateString(d.expiration); - }, title: '到期时间' + }, width:180, title: '到期时间' }, {field: 'username',width:180, sort: true, title: '用户名'}, {field: 'grantType',width:180, sort: true, title: '授权类型'}, {field: 'clientId',width:180, sort: true, title: '所属应用'}, + {field: 'accountType',width:180, sort: true, title: '账号类型'}, {align: 'center',width:100, toolbar: '#tokens-table-bar', title: '操作'} ]] }); diff --git a/zlt-web/pom.xml b/zlt-web/pom.xml index 8f5ba00a..34169a1c 100644 --- a/zlt-web/pom.xml +++ b/zlt-web/pom.xml @@ -6,7 +6,7 @@ com.zlt central-platform - 3.5.0 + 5.4.0 zlt-web 前端