feat: v2.5.4.90
1. 使用Mybatis Plus全面替换已有Mybatis,与Spring Boot Data JPA共存且支持同时使用。使用任何技术都可以无障碍的进行业务代码编写。 2. 整合Mybatis Plus和Spring Boot Data JPA更换数据库配置属性,一处修改即可以同时修改Mybatis Plus和Spring Boot Data JPA使用数据库类型。 3. 新增接口XSS脚本攻击过滤机制,同时支持请求参数和JSON请求体过滤。采用Ebay XSS过滤模型,进一步提升防控能力。 4. 新增SQL 注入攻击防控机制。 5. 解决eurynome-cloud-gateway和eurynome-cloud-management服务启动调用Kafka问题。 6. 解决CacheConfigException错误问题,在错误体系中增加配置参数不合理提醒,让信息反馈更加友好。 7. 解决Spring Boot Admin 不支持Java 8 时间类型问题。 8. 解决Spring Boot Admin 不显示 Git Properties 信息问题。 9. 解决修改Redis密码配置生效问题 10. 梳理dependencies依赖包,对已有依赖进行进行更合理的分类,更加便于依赖包的找寻和维护。 11. 升级依赖包版本 - spring-boot-admin 升级至 2.5.1 - git-commit-id-plugin 升级至 4.9.10 - docker-maven-plugin 升级至 0.37.0 - hutool 升级至 5.7.10 - okhttps 升级至 3.1.4 - JustAuth 升级至1.16.3 - aliyun-java-sdk-core 升级至 4.5.25 - baiducloud-java-sdk 升级至 0.10.175 - aliyun-java-sdk-oss 升级至 3.13.1 - cn.jpush.api 升级至 3.5.2 12. 规范项目文档,增加系统部署、数据库切换等多部分内容 13. 增加Nacos配置导入包,在没有自动部署功能支持的情况下,也可以更加方便的导入配置。 14. 替换 UI SweetAlert 过期方法,解决弹出框不会关闭问题 15. 解决授权码模式(authorization code)验证码被拦截问题
This commit is contained in:
parent
6b3edef026
commit
daef7d38a7
132
README.md
132
README.md
|
@ -12,14 +12,15 @@
|
|||
<a href="https://nacos.io/zh-cn/index.html" target="_blank"><img src="https://img.shields.io/badge/Nacos-2.0.3-brightgreen" alt="Nacos 2.0.3"></a>
|
||||
<a href="./LICENSE"><img src="https://img.shields.io/badge/License-Apache--2.0-blue" alt="License Apache 2.0"></a>
|
||||
<a href="https://blog.csdn.net/Pointer_v" target="_blank"><img src="https://img.shields.io/badge/Author-%E7%A0%81%E5%8C%A0%E5%90%9B-orange" alt="码匠君"></a>
|
||||
<a href="#" target="_blank"><img src="https://img.shields.io/badge/Version-2.5.4.80-red" alt="Version 2.5.4.80"></a>
|
||||
<a href="#" target="_blank"><img src="https://img.shields.io/badge/Version-2.5.4.90-red" alt="Version 2.5.4.90"></a>
|
||||
<a href="https://gitee.com/herodotus/eurynome-cloud"><img src="https://gitee.com/herodotus/eurynome-cloud/badge/star.svg?theme=dark" alt="Gitee star"></a>
|
||||
<a href="https://gitee.com/herodotus/eurynome-cloud"><img src="https://gitee.com/herodotus/eurynome-cloud/badge/fork.svg?theme=dark" alt="Gitee fork"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/herodotus-cloud/eurynome-cloud">Github 仓库</a> |
|
||||
<a href="https://gitee.com/herodotus/eurynome-cloud">Gitee 仓库</a>
|
||||
<a href="https://gitee.com/herodotus/eurynome-cloud">Gitee 仓库</a> |
|
||||
<a href="https://gitee.com/herodotus/eurynome-cloud/wikis">文档</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
@ -49,98 +50,7 @@ Eurynome Cloud是一款企业级微服务架构和服务能力开发平台。基
|
|||
|
||||
## [2]、功能介绍
|
||||
|
||||
### 统一安全认证中心
|
||||
|
||||
| 功能 | 说明 |
|
||||
| ------------------------ | ------------------------------------------------------------ |
|
||||
| OAuth2.0 安全认证 | 支持OAuth2.0授权码模式、隐式授权码模式(简单模式)、密码模式和客户端模式四种登录模式 |
|
||||
| JWT Token令牌 | 采用JWT对OAuth2 Token进行加密 |
|
||||
| 自定义OAuth2页面 | 自定义OAuth2 login、confirm、error页面,提升系统使用用户体验。可结合自身需求进行修改。 |
|
||||
| OAuth2 登录验证码 | OAuth2登录增加验证码保护,支持gif、中文、算数等类型,可通过配置进行修改以及是否显示验证码控制 |
|
||||
| OAuth2登录数据加密传输 | 基于AES对OAuth2登录数据进行动态加密传输,可通过配置对表单参数名进行动态配置,提升系统安全性 |
|
||||
| 平台权限管理 | 基于RBAC模型,以角色和接口为核心,使用统一逻辑实现@PreAuthorize注解权限与URL权限的全面整合及动态可配置化。统一平台接口白名单,IP地址白名单,以及Scope绑定URL的管理。无须配置Security权限注解,支持URL粒度的鉴权和用户权限的动态配置。 完美支持单体式架构、UPMS自身应用需求、分布式架构以及分布式各服务多实例等各种应用场景。完美支持单体式架构、UPMS自身应用需求、分布式架构以及分布式各服务多实例等各种应用场景。 |
|
||||
| User、Client数据策略访问 | 支持OAuth2 UserDetails、ClientDetails数据直连数据库和Feign两种数据获取策略模式,OAuth2直连数据库性能更优,Feign访问数据服务独立可动态扩展。可通过配置动态修改具体采用哪种策略 |
|
||||
| 手机短信验证码注册和登录 | 支持通过手机验证码登录认证,与平台为统一体系,统一返回OAuth2 Token,支持服务接口鉴权。 |
|
||||
| 第三方系统社交注册和登录 | 基于JustAuth实现第三方系统社交登录认证,,与平台为统一体系,统一返回OAuth2 Token,支持服务接口鉴权,。所有JustAuth支持的第三方系统均支持。 |
|
||||
| 微信小程序注册和登录 | 支持微信小程序登录认证,与平台为统一体系,统一返回OAuth2 Token,支持服务接口鉴权。 |
|
||||
| 其它注册和登录 | 采用策略模式对外部登录和注册进行支持,目前未支持的登录,可参考标准,动态扩展,即可支持。 |
|
||||
|
||||
### 统一服务访问网关
|
||||
|
||||
| 功能 | 说明 |
|
||||
| -------------- | ------------------------------------------------------------ |
|
||||
| 网关动态路由 | 基于Gateway和Nacos实现服务网关动态路由,无须增加任何配置,即可支持服务的发现与路由 |
|
||||
| 网关服务鉴权 | 服务网关集成部分权限认证功能,提升系统安全性,降低平台压力 |
|
||||
| 网关服务限流 | 基于Sentinel实现服务的限流,支持基于Gateway Filter的自定义限流 |
|
||||
| 动态文档聚合 | 网关动态Swagger文档聚合,使用Swagger 3.0,支持Knife4j增强。不同环境访问控制 |
|
||||
| 统一跨域处理 | 基于Gateway实现统一跨域处理 |
|
||||
| 统一错误处理 | 平台统一错误处理,支持自定义错误码体系 |
|
||||
| 自定义动态路由 | 支持基于DB的动态路由管理与路由规则配置 |
|
||||
|
||||
### 微服务架构支撑
|
||||
|
||||
| 功能 | 说明 |
|
||||
| ------------ | ------------------------------------------------------------ |
|
||||
| 服务注册发现 | 基于Nacos实现服务的注册与发现。 |
|
||||
| 服务负载均衡 | Spring Cloud Netflix停止维护,使用Spring Cloud Loadbalancer全面替换Ribbon, |
|
||||
| 服务熔断降级 | 整合OpenFeign和Sentinel,从熔断降级、系统负载保护、热点防护等多个维度来保障微服务的稳定性 |
|
||||
| 统一配置中心 | 基于Nacos搭建的统一配置中心,使用MySQL8,支持配置文件自动导入和关键信息加密,可根据文件夹名称自动分组配置。 |
|
||||
| 统一日志中心 | 采用TCP直连的方式采集和发送日志,集成Skywalking TraceID实现日志聚合及ELK日志分析。通过@EnableXXX注解开启或关闭日志采集功能。 |
|
||||
| 分布任务调度 | 极简集成xxl-job,支持分布式任务调度功能 |
|
||||
| 分布事务处理 | 集成Seata,支持分布式事务,无代码侵入,灵活便捷 |
|
||||
| 分布对象存储 | 支持Minio分布式对象存储。同时,集成阿里云OSS,可通过@EnableXXX注解开启或关闭功能。 |
|
||||
| 分布式工作流 | 以Camunda引擎为基础定义分布式工作流引擎,提供自研人事信息管理以及人事管理体系与工作流用户体系的实时集成 |
|
||||
|
||||
### 微服务运行监控
|
||||
|
||||
| 功能 | 说明 |
|
||||
| -------------- | ------------------------------------------------------------ |
|
||||
| 服务调用链监控 | 集成Skywalking进行服务调用链的监控,调用链监控深度可延伸至Undertow、数据库、Redis |
|
||||
| 应用吞吐量监控 | 集成Skywalking进行应用吞吐量监控 |
|
||||
| 熔断、降级监控 | 集成Sentinel进行服务的熔断、降级监控 |
|
||||
| 微服务状态监控 | 集成Spring Boot Admin进行服务运行状态的监控 |
|
||||
|
||||
### 数据自动化处理
|
||||
|
||||
| 功能 | 说明 |
|
||||
| ------------------- | ------------------------------------------------------------ |
|
||||
| 数据模型初始化 | RBAC、OAuth2部分核心表、Camunda数据表,在初始部署过程中为自动化创建 |
|
||||
| 核心应用数据初始化 | RBAC、OAuth2等核心数据,在初始部署过程中为自动化导入 |
|
||||
| URL权限数据动态汇总 | 自动扫描URL接口作为权限汇总存入系统,根据URL相关信息生成唯一ID,多次导入数据不会重复。可通过配置动态开启或修改扫描内容 |
|
||||
| Yml配置自动导入 | 服务所需使用的配置文件,可自动根据文件夹进行分组并导入到Nacos中。 |
|
||||
|
||||
### 服务开发支撑
|
||||
|
||||
| 功能 | 说明 |
|
||||
| ---------------- | ------------------------------------------------------------ |
|
||||
| 通用CRUD封装 | 各种类型的代码生成器较多,结合实际应用使用效果来看,代码生成器在实际开发中使用频率低于理想预期。因此,没有考虑提供代码生成器,而是对常规的CRUD进行了多层次的封装,使定制化服务的开发更加便捷。 |
|
||||
| 自研两级缓存封装 | 自研基于Caffeine和Redis分布式两级缓存,完美支持JPA Hibernate二级缓存,完美支持各类查询数据缓存以及JPA @ManyToMany, @ManyToOne等关联查询。实现基于Caffeine的Hibernate二级缓存,可与自研两级缓存快速切换,仅使用本地缓存创建Key繁琐和分页数据无法更新的问题 |
|
||||
| 多类型数据库支持 | 默认采用postgresql数据库,支持MySQL、Oracle、H2等多种关系型数据库,无须修改代码可动态切换。数据层同时支持Spring Data Jpa和Mybatis |
|
||||
| 多消息队列支持 | 适配RabbitMQ和Kafka,默认使用Kafka,支持消息总线(Spring Cloud Bus) |
|
||||
| 多种服务调用方式 | 默认采用OpenFeign进行服务间调用,支持RestTemplate和OkHttps |
|
||||
| 共享式多环境切换 | 共享式、统一化多环境配置模式,Yml、Docker均采用此方式配置,避免类似的服务配置、Dockerfile配置导出复制和修改的问题 |
|
||||
| 多团队开发管理 | 支持多团队开发,可针对各个团队开发服务,进行单独的授权配置。 |
|
||||
| 注解模型模块模式 | 除必要依赖逻辑以及强注入顺序要求的模块采用Spring SPI(spring.factories)机制外,其它功能模块均采用@EnableXXX注解编程模型,可选择性进行控制是否开启对应模块,减少不必要的依赖注入和启动。 |
|
||||
| 代码打包记录查询 | 对代码编译信息进行记录,可查询代码版本以及编译时间等相关信息,方便运维人员更好的掌握代码信息。 |
|
||||
| 人事信息管理 | 除已有的用户体系外,集成单位、部门、人员等人事管理功能,满足大多数人事管理场景,并与Camunda工作流用户体系无缝整合,实现数据实时同步 |
|
||||
|
||||
### 信息发送集成
|
||||
|
||||
| 功能 | 说明 |
|
||||
| ------------------ | ------------------------------------------------------------ |
|
||||
| 微信小程序订阅消息 | 支持微信小程序订阅消息发送。提供订阅消息模版工厂,可根据自身业务需求,编写少量代码既可以拓展支持新订阅消息模版。 |
|
||||
| 极光消息推送集成 | 集成极光消息推送,对极光后台API进行封装,封装度高、调用方便,可快速与自定义业务需求整合,通过@EnableXXX注解开启或关闭。 |
|
||||
| 环信消息集成 | 集成环信IM和消息推送,使用更加便捷,可在应用中根据自定义需求快速整合和拓展IM,发送手机推送消息。通过@EnableXXX注解开启或关闭。 |
|
||||
| 多通道SMS集成 | 集成阿里、百度、中国移动、华为、京东、极光、网易、七牛、腾讯、又拍、云片等平台短信发送通道。可通过配置动态选择具体使用通道。支持多模版定义以及模版参数顺序控制 |
|
||||
|
||||
### 内容审核集成
|
||||
|
||||
| 功能 | 说明 |
|
||||
| ------------------ | ------------------------------------------------------------ |
|
||||
| 阿里云内容审核 | 集成阿里云内容审核,支持文本、图片、音频、视频、网页内容审核,支持同步审核、异步审核、异步Callback方式审核通过@EnableXXX注解开启或关闭。 |
|
||||
| 微信小程序内容审核 | 集成微信小程序内容审核,支持文本、图片、音频内容审核,支持同步、异步审核。通过@EnableXXX注解开启或关闭。 |
|
||||
| 百度证照识别 | 集成百度证照审核,支持营业执照、身份证OCR识别。通过@EnableXXX注解开启或关闭。 |
|
||||
| 天眼查企业信息查询 | 集成天眼查企业信息查询。通过@EnableXXX注解开启或关闭。 |
|
||||

|
||||
|
||||
## [3]、技术栈和版本说明
|
||||
|
||||
|
@ -151,7 +61,7 @@ Eurynome Cloud是一款企业级微服务架构和服务能力开发平台。基
|
|||
Spring Boot | 2.5.4
|
||||
Spring Cloud | 2020.0.3
|
||||
Spring Cloud Alibaba | 2021.1
|
||||
Spring Boot Admin | 2.4.2
|
||||
Spring Boot Admin | 2.5.1
|
||||
Nacos | 2.0.3 |
|
||||
Sentinel | 1.8.0 |
|
||||
Seata | 1.3.0 |
|
||||
|
@ -160,20 +70,19 @@ Seata | 1.3.0 |
|
|||
|
||||
### (2)所涉及的相关的技术:
|
||||
|
||||
* JSON序列化:Jackson & FastJson
|
||||
* 消息队列:Kafka 适配RabbitMQ,支持消息总线(Spring Cloud Bus)
|
||||
* 数据缓存:JetCache + Redis + Caffeine
|
||||
* 数据库: Postgresql,MySQL,Oracle ...
|
||||
* 前端实现:Vue + Vuetify
|
||||
* 持久层框架: Spring Data Jpa & Mybatis
|
||||
* API网关:Gateway
|
||||
* 服务注册&发现和配置中心: Nacos
|
||||
* 服务消费:OpenFeign & RestTemplate & OkHttp3
|
||||
* 持久层框架: Spring Data Jpa & Mybatis Plus
|
||||
* API网关:Spring Cloud Gateway
|
||||
* 服务注册&发现和配置中心: Alibaba Nacos
|
||||
* 服务消费:Spring Cloud OpenFeign & RestTemplate & OkHttps
|
||||
* 负载均衡:Spring Cloud Loadbalancer
|
||||
* 服务熔断&降级&限流:Sentinel
|
||||
* 分布式事务:Seata
|
||||
* 服务熔断&降级&限流:Alibaba Sentinel
|
||||
* 服务监控:Spring Boot Admin
|
||||
* 消息队列:使用Spring Cloud消息总线Spring Cloud Bus 默认Kafka 适配RabbitMQ
|
||||
* 链路跟踪:Skywalking
|
||||
* 分布式事务:Seata
|
||||
* 数据缓存:JetCache + Redis + Caffeine
|
||||
* 数据库: Postgresql,MySQL,Oracle ...
|
||||
* JSON序列化:Jackson & FastJson
|
||||
* 文件服务:阿里云OSS/Minio
|
||||
* 数据调试:p6spy
|
||||
* 日志中心:ELK
|
||||
|
@ -193,17 +102,16 @@ Seata | 1.3.0 |
|
|||
eurynome-cloud
|
||||
├── configurations -- 配置文件脚本和统一Docker build上下文目录
|
||||
├── dependencies -- 工程Maven顶级依赖,统一控制版本和依赖
|
||||
├── documents -- 工程相关文档
|
||||
├── documents -- 工程相关文档(会逐步清理,统一采用Gitee Wiki)
|
||||
├── packages -- 基础通用依赖包
|
||||
├ ├── eurynome-cloud-assistant -- Spring相关公共辅助工具、注解相关工具代码组件
|
||||
├ ├── eurynome-cloud-common -- 公共工具类
|
||||
├ ├── eurynome-cloud-assistant -- 辅助相关代码组件
|
||||
├ ├── eurynome-cloud-data -- 数据持久化、数据缓存以及Redis等数据处理相关代码组件
|
||||
├ ├── eurynome-cloud-kernel -- 微服务接入平台必备组件
|
||||
├ ├── eurynome-cloud-oauth -- OAuth2通用代码
|
||||
├ ├── eurynome-cloud-oauth-starter -- 自定义OAuth2 Starter,Athena单体版核心Starter
|
||||
├ ├── eurynome-cloud-rest -- Rest相关代码组件
|
||||
├ ├── eurynome-cloud-sercurity -- Security通用代码
|
||||
├ ├── eurynome-cloud-oauth -- OAuth2通用代码
|
||||
├ ├── eurynome-cloud-message -- 消息队列、BUG相关代码组件
|
||||
├ ├── eurynome-cloud-kernel -- 微服务接入平台必备组件
|
||||
├ ├── eurynome-cloud-oauth-starter -- 自定义OAuth2 Starter
|
||||
├ └── eurynome-cloud-starter -- 微服务核心Starter
|
||||
├── platform -- 平台核心服务
|
||||
├ ├── eurynome-cloud-gateway -- 服务网关
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
VERSION=2.5.4.80
|
||||
VERSION=2.5.4.90
|
||||
GATEWAY_SERVICE_NAME=eurynome-cloud-gateway
|
||||
GATEWAY_SERVICE_PORT=8847
|
||||
UAA_SERVICE_NAME=eurynome-cloud-uaa
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
<groupId>cn.herodotus.eurynome</groupId>
|
||||
<artifactId>dependencies</artifactId>
|
||||
<version>2.5.4.80</version>
|
||||
<version>2.5.4.90</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>eurynome-cloud-dependencies</name>
|
||||
|
@ -62,43 +62,47 @@
|
|||
<spring-boot-dependencies.version>2.5.4</spring-boot-dependencies.version>
|
||||
<spring-cloud-dependencies.version>2020.0.3</spring-cloud-dependencies.version>
|
||||
<spring-cloud-alibaba-dependencies.version>2021.1</spring-cloud-alibaba-dependencies.version>
|
||||
<spring-boot-admin.version>2.5.0</spring-boot-admin.version>
|
||||
<spring-boot-admin.version>2.5.1</spring-boot-admin.version>
|
||||
|
||||
<!--Spring Boot Dependencies中没有的Maven Plugin-->
|
||||
<docker-maven-plugin.version>0.36.1</docker-maven-plugin.version>
|
||||
<git-commit-id-maven-plugin.version>5.0.0</git-commit-id-maven-plugin.version>
|
||||
<!--Maven Plugin 相关组件-->
|
||||
<docker-maven-plugin.version>0.37.0</docker-maven-plugin.version>
|
||||
<git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
|
||||
<mapstruct-processor.version>1.4.2.Final</mapstruct-processor.version>
|
||||
|
||||
<!--Spring Boot Dependencies中没有的工具包-->
|
||||
<!--开源通用工具包-->
|
||||
<commons-collections4.version>4.4</commons-collections4.version>
|
||||
<commons-text.version>1.9</commons-text.version>
|
||||
<commons-beanutils.version>1.9.4</commons-beanutils.version>
|
||||
<commons-io.version>2.11.0</commons-io.version>
|
||||
<guava.version>30.1.1-jre</guava.version>
|
||||
<hutool.version>5.7.8</hutool.version>
|
||||
<antisamy.version>1.6.4</antisamy.version>
|
||||
|
||||
<!--Alibaba开源的工具包-->
|
||||
<!-- 应用开发辅助工具组件 -->
|
||||
<lombok.version>1.18.20</lombok.version>
|
||||
<p6spy.version>3.9.1</p6spy.version>
|
||||
<springfox.version>2.9.2</springfox.version>
|
||||
<swagger.version>1.6.2</swagger.version>
|
||||
<logstash-logback-encoder.version>6.6</logstash-logback-encoder.version>
|
||||
|
||||
<!-- 阿里巴巴开源工具依赖 -->
|
||||
<alibaba-fastjson.version>1.2.75</alibaba-fastjson.version>
|
||||
<alibaba-jetcache.version>2.6.0</alibaba-jetcache.version>
|
||||
<alibaba-nacos.version>2.0.3</alibaba-nacos.version>
|
||||
|
||||
<!--Spring Boot Dependencies中没有的依赖Starter组件-->
|
||||
<starter-jasypt.version>3.0.3</starter-jasypt.version>
|
||||
<starter-mybatis.version>2.2.0</starter-mybatis.version>
|
||||
<starter-camunda.version>7.15.0</starter-camunda.version>
|
||||
|
||||
<!--Spring Boot Dependencies中没有的依赖组件-->
|
||||
<springfox.version>2.9.2</springfox.version>
|
||||
<swagger.version>1.6.2</swagger.version>
|
||||
<!-- 国内开源通用组件 -->
|
||||
<hutool.version>5.7.10</hutool.version>
|
||||
<okhttps.version>3.1.4</okhttps.version>
|
||||
<just-auth.verison>1.16.3</just-auth.verison>
|
||||
<knife4j.version>2.0.6</knife4j.version>
|
||||
<p6spy.version>3.9.1</p6spy.version>
|
||||
<kaptcha.version>2.3.2</kaptcha.version>
|
||||
<easy-captcha.version>1.6.2</easy-captcha.version>
|
||||
<okhttps.version>3.1.3</okhttps.version>
|
||||
<lombok.version>1.18.20</lombok.version>
|
||||
<just-auth.verison>1.16.2</just-auth.verison>
|
||||
|
||||
<logstash-logback-encoder.version>6.6</logstash-logback-encoder.version>
|
||||
<!--第三方starter-->
|
||||
<starter-jasypt.version>3.0.3</starter-jasypt.version>
|
||||
<starter-camunda.version>7.15.0</starter-camunda.version>
|
||||
<starter-mybatis-plus.version>3.4.3.2</starter-mybatis-plus.version>
|
||||
<mybatis-plus-generator.version>3.5.0</mybatis-plus-generator.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
</properties>
|
||||
|
||||
<!-- 管理依赖版本号,子项目不会默认依赖 -->
|
||||
|
@ -124,6 +128,8 @@
|
|||
<artifactId>spring-boot-admin-starter-server</artifactId>
|
||||
<version>${spring-boot-admin.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Eurynome Cloud -->
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
|
@ -191,14 +197,29 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 通用工具组件 -->
|
||||
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||
|
||||
<!-- Maven插件相关组件 -->
|
||||
<!-- https://mvnrepository.com/artifact/io.fabric8/docker-maven-plugin -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>${docker-maven-plugin.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/pl.project13.maven/git-commit-id-plugin -->
|
||||
<dependency>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>${git-commit-id-plugin.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct-processor.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 开源通用工具包 -->
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
|
@ -228,46 +249,46 @@
|
|||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps-fastjson</artifactId>
|
||||
<version>${okhttps.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps-gson</artifactId>
|
||||
<version>${okhttps.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps-jackson</artifactId>
|
||||
<version>${okhttps.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
<version>${jackson-bom.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>${jackson-bom.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jdk8 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
<version>${jackson-bom.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
<version>${jackson-bom.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/me.zhyd.oauth/JustAuth -->
|
||||
<!-- https://mvnrepository.com/artifact/org.owasp.antisamy/antisamy -->
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>${just-auth.verison}</version>
|
||||
<groupId>org.owasp.antisamy</groupId>
|
||||
<artifactId>antisamy</artifactId>
|
||||
<version>${antisamy.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 应用开发辅助工具组件 -->
|
||||
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
|
@ -290,20 +311,6 @@
|
|||
<artifactId>swagger-models</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-spring-ui -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-ui</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct-processor.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
|
||||
<dependency>
|
||||
<groupId>p6spy</groupId>
|
||||
|
@ -316,18 +323,7 @@
|
|||
<artifactId>logstash-logback-encoder</artifactId>
|
||||
<version>${logstash-logback-encoder.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.penggle/kaptcha -->
|
||||
<dependency>
|
||||
<groupId>com.github.penggle</groupId>
|
||||
<artifactId>kaptcha</artifactId>
|
||||
<version>${kaptcha.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.whvcse/easy-captcha -->
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
<artifactId>easy-captcha</artifactId>
|
||||
<version>${easy-captcha.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 阿里巴巴依赖 -->
|
||||
<dependency>
|
||||
|
@ -348,6 +344,55 @@
|
|||
<version>${alibaba-jetcache.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 国内开源通用组件 -->
|
||||
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps-fastjson</artifactId>
|
||||
<version>${okhttps.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps-gson</artifactId>
|
||||
<version>${okhttps.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps-jackson</artifactId>
|
||||
<version>${okhttps.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/me.zhyd.oauth/JustAuth -->
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>${just-auth.verison}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-spring-ui -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-ui</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.penggle/kaptcha -->
|
||||
<dependency>
|
||||
<groupId>com.github.penggle</groupId>
|
||||
<artifactId>kaptcha</artifactId>
|
||||
<version>${kaptcha.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.whvcse/easy-captcha -->
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
<artifactId>easy-captcha</artifactId>
|
||||
<version>${easy-captcha.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 第三方Starter -->
|
||||
<dependency>
|
||||
<groupId>org.camunda.bpm.springboot</groupId>
|
||||
|
@ -360,11 +405,23 @@
|
|||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>${starter-jasypt.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
|
||||
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>${starter-mybatis.version}</version>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${starter-mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId>
|
||||
<version>${mybatis-plus-generator.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity-engine-core -->
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
<version>${velocity.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
@ -431,9 +488,9 @@
|
|||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>io.github.git-commit-id</groupId>
|
||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||
<version>${git-commit-id-maven-plugin.version}</version>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>${git-commit-id-plugin.version}</version>
|
||||
<configuration>
|
||||
<!-- 检查的仓库根目录,${project.basedir}:项目根目录,即包含pom.xml文件的目录 -->
|
||||
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
|
||||
|
@ -446,8 +503,9 @@
|
|||
<!-- 生成git属性文件路径及文件名,默认${project.build.outputDirectory}/git.properties -->
|
||||
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties
|
||||
</generateGitPropertiesFilename>
|
||||
<dateFormat>yyyy-MM-dd HH:mm:ss</dateFormat>
|
||||
<!-- 生成git属性文件格式,默认值properties -->
|
||||
<format>json</format>
|
||||
<!-- <format>json</format>-->
|
||||
<!-- 配置git-describe命令 -->
|
||||
<gitDescribe>
|
||||
<skip>false</skip>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<parent>
|
||||
<artifactId>packages</artifactId>
|
||||
<groupId>cn.herodotus.eurynome</groupId>
|
||||
<version>2.5.4.80</version>
|
||||
<version>2.5.4.90</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ public class HerodotusExceptionHandler {
|
|||
EXCEPTION_DICTIONARY.put("MethodArgumentNotValidException", getValidationResult(ResultStatus.METHOD_ARGUMENT_NOT_VALID));
|
||||
// 7*.** 对应错误
|
||||
EXCEPTION_DICTIONARY.put("RedisPipelineException", getResult(ResultStatus.PIPELINE_INVALID_COMMANDS, HttpStatus.SC_INTERNAL_SERVER_ERROR));
|
||||
EXCEPTION_DICTIONARY.put("CacheConfigException", getResult(ResultStatus.CACHE_CONFIG_NOT_FOUND, HttpStatus.SC_NOT_IMPLEMENTED));
|
||||
// 以下是没有重新梳理过的错误。
|
||||
EXCEPTION_DICTIONARY.put("UsernameNotFoundException", getUnauthorizedResult(ResultStatus.USERNAME_NOT_FOUND));
|
||||
EXCEPTION_DICTIONARY.put("BadCredentialsException", getUnauthorizedResult(ResultStatus.BAD_CREDENTIALS));
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
<parent>
|
||||
<artifactId>packages</artifactId>
|
||||
<groupId>cn.herodotus.eurynome</groupId>
|
||||
<version>2.5.4.80</version>
|
||||
<version>2.5.4.90</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>eurynome-cloud-common</artifactId>
|
||||
<version>2.5.4.80</version>
|
||||
<version>2.5.4.90</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>结对基础通用的工具类包,此包的定位是放在任何工程中都可以使用,而且尽可能依赖少的存在上下文组件</description>
|
||||
|
@ -85,6 +85,14 @@
|
|||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
|
@ -118,6 +126,17 @@
|
|||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.owasp.antisamy</groupId>
|
||||
<artifactId>antisamy</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -75,6 +75,16 @@ public enum ResultStatus {
|
|||
WARNING(4001, "警告"),
|
||||
|
||||
METHOD_NOT_ALLOWED(4105, "请求方法不支持"),
|
||||
|
||||
/**
|
||||
* 43.** 禁止的请求,与403对应
|
||||
*/
|
||||
SQL_INJECTION_REQUEST(4301, "疑似SQL注入请求"),
|
||||
|
||||
|
||||
/**
|
||||
* 46.** 不接受的请求,与406对应
|
||||
*/
|
||||
REPEAT_SUBMISSION(4601, "不要重复提交"),
|
||||
FREQUENT_REQUESTS(4602, "请求过于频繁请稍后再试"),
|
||||
|
||||
|
@ -88,8 +98,10 @@ public enum ResultStatus {
|
|||
/**
|
||||
* 7*.* 基础设施交互错误
|
||||
* 71.* Redis 操作出现错误
|
||||
* 72.* Cache 操作出现错误
|
||||
*/
|
||||
PIPELINE_INVALID_COMMANDS(7100, "Redis管道包含一个或多个无效命令"),
|
||||
CACHE_CONFIG_NOT_FOUND(7200, "服务需要使用缓存,但是未找到*-cache.yaml配置"),
|
||||
|
||||
|
||||
// TODO: 以下状态码和错误信息要重新梳理,重点要看自定义错误码以及对应的HttpStatus是否合适。如果发现新的没有定义的错误,增加到上面。
|
||||
|
|
|
@ -65,6 +65,7 @@ public class PropertyConstants {
|
|||
|
||||
public static final String ITEM_SPRING_APPLICATION_NAME = "spring.application.name";
|
||||
public static final String ITEM_SPRING_JPA_HIBERNATE_DDL_AUTO = "spring.jpa.hibernate.ddl-auto";
|
||||
public static final String ITEM_SPRING_SQL_INIT_PLATFORM = "spring.sql.init.platform";
|
||||
|
||||
public static final String ITEM_PLATFORM_DATA_ACCESS_STRATEGY = PROPERTY_PREFIX_PLATFORM + ".data-access-strategy";
|
||||
public static final String ITEM_PLATFORM_ARCHITECTURE = PROPERTY_PREFIX_PLATFORM + ".architecture";
|
||||
|
@ -73,6 +74,11 @@ public class PropertyConstants {
|
|||
public static final String ITEM_MANAGEMENT_KAFKA_ENABLED = PROPERTY_PREFIX_MANAGEMENT_QUEUE + ".kafka.enabled";
|
||||
public static final String ITEM_MANAGEMENT_LOG_CENTER_ENABLED = PROPERTY_PREFIX_MANAGEMENT_LOG_CENTER + ".server-addr";
|
||||
|
||||
public static final String ANNOTATION_APPLICATION_NAME = "${" + ITEM_SPRING_APPLICATION_NAME + "}";
|
||||
public static final String ANNOTATION_PREFIX = "${";
|
||||
public static final String ANNOTATION_SUFFIX = "}";
|
||||
|
||||
|
||||
public static final String ANNOTATION_APPLICATION_NAME = ANNOTATION_PREFIX + ITEM_SPRING_APPLICATION_NAME + ANNOTATION_SUFFIX;
|
||||
public static final String ANNOTATION_SQL_INIT_PLATFORM = ANNOTATION_PREFIX + ITEM_SPRING_SQL_INIT_PLATFORM + ANNOTATION_SUFFIX;
|
||||
public static final String ANNOTATION_DEBEZIUM_ENABLED = "${herodotus.platform.debezium.enabled}";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 Gengwei Zheng (herodotus@aliyun.com)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Project Name: eurynome-cloud
|
||||
* Module Name: eurynome-cloud-common
|
||||
* File Name: XssStringJsonSerializer.java
|
||||
* Author: gengwei.zheng
|
||||
* Date: 2021/09/01 12:41:01
|
||||
*/
|
||||
|
||||
package cn.herodotus.eurynome.common.jackson.deserializer;
|
||||
|
||||
import cn.herodotus.eurynome.common.utils.XssUtils;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* <p>Description: Xss Json 处理 </p>
|
||||
*
|
||||
* @author : gengwei.zheng
|
||||
* @date : 2021/9/1 12:16
|
||||
*/
|
||||
public class XssStringJsonDeserializer extends JsonDeserializer<String> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(XssStringJsonDeserializer.class);
|
||||
|
||||
@Override
|
||||
public Class<String> handledType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
|
||||
String value = jsonParser.getValueAsString();
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
return XssUtils.cleaning(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -24,12 +24,14 @@ package cn.herodotus.eurynome.common.utils;
|
|||
|
||||
import cn.herodotus.eurynome.common.jackson.FastJsonSerializerFeatureCompatibleForJackson;
|
||||
import cn.herodotus.eurynome.common.jackson.SerializerFeature;
|
||||
import cn.herodotus.eurynome.common.jackson.deserializer.XssStringJsonDeserializer;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -40,19 +42,19 @@ import java.util.List;
|
|||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @author gengwei.zheng
|
||||
* <p>Description: Jackson单例工具类 </p>
|
||||
*
|
||||
* @author : gengwei.zheng
|
||||
* @date : 2021/9/1 12:18
|
||||
*/
|
||||
public class JacksonUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(JacksonUtils.class);
|
||||
|
||||
private static final ObjectMapper objectMapper;
|
||||
private static volatile JacksonUtils INSTANCE;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public static ObjectMapper getObjectMapper() {
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
static {
|
||||
private JacksonUtils() {
|
||||
objectMapper = new ObjectMapper();
|
||||
// 设置为中国上海时区
|
||||
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
|
||||
|
@ -68,6 +70,7 @@ public class JacksonUtils {
|
|||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
// 单引号处理
|
||||
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
||||
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
|
||||
|
||||
/**
|
||||
|
@ -77,6 +80,7 @@ public class JacksonUtils {
|
|||
SimpleModule simpleModule = new SimpleModule();
|
||||
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
|
||||
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
|
||||
simpleModule.addDeserializer(String.class, new XssStringJsonDeserializer());
|
||||
|
||||
objectMapper.registerModule(simpleModule);
|
||||
// 兼容fastJson 的一些空值处理
|
||||
|
@ -90,15 +94,35 @@ public class JacksonUtils {
|
|||
objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new FastJsonSerializerFeatureCompatibleForJackson(features)));
|
||||
}
|
||||
|
||||
private static JacksonUtils getInstance() {
|
||||
if (ObjectUtils.isEmpty(INSTANCE)) {
|
||||
synchronized (JacksonUtils.class) {
|
||||
if (ObjectUtils.isEmpty(INSTANCE)) {
|
||||
INSTANCE = new JacksonUtils();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private ObjectMapper objectMapper() {
|
||||
return this.objectMapper;
|
||||
}
|
||||
|
||||
public static ObjectMapper getObjectMapper() {
|
||||
return JacksonUtils.getInstance().objectMapper();
|
||||
}
|
||||
|
||||
public static <T> T toObject(String json, Class<T> clazz) {
|
||||
try {
|
||||
return getObjectMapper().readValue(json, clazz);
|
||||
} catch (JsonParseException e) {
|
||||
logger.error("[JacksonUtils] |- toObject parse json error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toObject parse json error! {}", e.getMessage());
|
||||
} catch (JsonMappingException e) {
|
||||
logger.error("[JacksonUtils] |- toObject mapping to object error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toObject mapping to object error! {}", e.getMessage());
|
||||
} catch (IOException e) {
|
||||
logger.error("[JacksonUtils] |- toObject read content error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toObject read content error! {}", e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -108,27 +132,26 @@ public class JacksonUtils {
|
|||
try {
|
||||
return getObjectMapper().writeValueAsString(entity);
|
||||
} catch (JsonParseException e) {
|
||||
logger.error("[JacksonUtils] |- toCollection parse json error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toCollection parse json error! {}", e.getMessage());
|
||||
} catch (JsonMappingException e) {
|
||||
logger.error("[JacksonUtils] |- toCollection mapping to object error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toCollection mapping to object error! {}", e.getMessage());
|
||||
} catch (IOException e) {
|
||||
logger.error("[JacksonUtils] |- toCollection read content error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toCollection read content error! {}", e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> List<T> toList(String json, Class<T> clazz) {
|
||||
JavaType javaType = getObjectMapper().getTypeFactory().constructParametricType(ArrayList.class, clazz);
|
||||
try {
|
||||
return (List<T>) getObjectMapper().readValue(json, javaType);
|
||||
return getObjectMapper().readValue(json, javaType);
|
||||
} catch (JsonParseException e) {
|
||||
logger.error("[JacksonUtils] |- toCollection parse json error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toCollection parse json error! {}", e.getMessage());
|
||||
} catch (JsonMappingException e) {
|
||||
logger.error("[JacksonUtils] |- toCollection mapping to object error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toCollection mapping to object error! {}", e.getMessage());
|
||||
} catch (IOException e) {
|
||||
logger.error("[JacksonUtils] |- toCollection read content error! {}", e.getMessage());
|
||||
logger.error("[Eurynome] |- JacksonUtils toCollection read content error! {}", e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -138,11 +161,11 @@ public class JacksonUtils {
|
|||
try {
|
||||
return getObjectMapper().readValue(json, typeReference);
|
||||
} catch (JsonParseException e) {
|
||||
logger.error("-| [JacksonUtils]: toCollection parse json error! {}", e.getMessage());
|
||||
logger.error("-| [Eurynome]: JacksonUtils toCollection parse json error! {}", e.getMessage());
|
||||
} catch (JsonMappingException e) {
|
||||
logger.error("-| [JacksonUtils]: toCollection mapping to object error! {}", e.getMessage());
|
||||
logger.error("-| [Eurynome]: JacksonUtils toCollection mapping to object error! {}", e.getMessage());
|
||||
} catch (IOException e) {
|
||||
logger.error("-| [JacksonUtils]: toCollection read content error! {}", e.getMessage());
|
||||
logger.error("-| [Eurynome]: JacksonUtils toCollection read content error! {}", e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 Gengwei Zheng (herodotus@aliyun.com)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Project Name: eurynome-cloud
|
||||
* Module Name: eurynome-cloud-common
|
||||
* File Name: ResourceUtils.java
|
||||
* Author: gengwei.zheng
|
||||
* Date: 2021/09/01 12:18:01
|
||||
*/
|
||||
|
||||
package cn.herodotus.eurynome.common.utils;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* <p>Description: 资源查找单例工具类 </p>
|
||||
*
|
||||
* @author : gengwei.zheng
|
||||
* @date : 2021/9/1 12:18
|
||||
*/
|
||||
public class ResourceUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ResourceUtils.class);
|
||||
|
||||
private static volatile ResourceUtils INSTANCE;
|
||||
|
||||
private final PathMatchingResourcePatternResolver resolver;
|
||||
|
||||
private ResourceUtils() {
|
||||
this.resolver = new PathMatchingResourcePatternResolver();
|
||||
}
|
||||
|
||||
private static ResourceUtils getInstance() {
|
||||
if (ObjectUtils.isEmpty(INSTANCE)) {
|
||||
synchronized (ResourceUtils.class) {
|
||||
if (ObjectUtils.isEmpty(INSTANCE)) {
|
||||
INSTANCE = new ResourceUtils();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private PathMatchingResourcePatternResolver getResolver() {
|
||||
return this.resolver;
|
||||
}
|
||||
|
||||
public static Resource getResource(String location) {
|
||||
return ResourceUtils.getInstance().getResolver().getResource(location);
|
||||
}
|
||||
|
||||
public static Resource[] getResources(String locationPattern) throws IOException {
|
||||
return ResourceUtils.getInstance().getResolver().getResources(locationPattern);
|
||||
}
|
||||
|
||||
public static File getFile(String location) throws IOException {
|
||||
return ResourceUtils.getResource(location).getFile();
|
||||
}
|
||||
|
||||
public static InputStream getInputStream(String location) throws IOException {
|
||||
return ResourceUtils.getResource(location).getInputStream();
|
||||
}
|
||||
|
||||
public static String getFilename(String location) {
|
||||
return ResourceUtils.getResource(location).getFilename();
|
||||
}
|
||||
|
||||
public static URI getURI(String location) throws IOException {
|
||||
return ResourceUtils.getResource(location).getURI();
|
||||
}
|
||||
|
||||
public static URL getURL(String location) throws IOException {
|
||||
return ResourceUtils.getResource(location).getURL();
|
||||
}
|
||||
|
||||
public static long contentLength(String location) throws IOException {
|
||||
return ResourceUtils.getResource(location).contentLength();
|
||||
}
|
||||
|
||||
public static long lastModified(String location) throws IOException {
|
||||
return ResourceUtils.getResource(location).lastModified();
|
||||
}
|
||||
|
||||
public static boolean exists(String location) {
|
||||
return ResourceUtils.getResource(location).exists();
|
||||
}
|
||||
|
||||
public static boolean isFile(String location) {
|
||||
return ResourceUtils.getResource(location).isFile();
|
||||
}
|
||||
|
||||
public static boolean isReadable(String location) {
|
||||
return ResourceUtils.getResource(location).isReadable();
|
||||
}
|
||||
|
||||
public static boolean isOpen(String location) {
|
||||
return ResourceUtils.getResource(location).isOpen();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 Gengwei Zheng (herodotus@aliyun.com)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Project Name: eurynome-cloud
|
||||
* Module Name: eurynome-cloud-common
|
||||
* File Name: SQLInjectionUtils.java
|
||||
* Author: gengwei.zheng
|
||||
* Date: 2021/09/01 12:19:01
|
||||
*/
|
||||
|
||||
package cn.herodotus.eurynome.common.utils;
|
||||
|
||||
import cn.herodotus.eurynome.common.constant.magic.SymbolConstants;
|
||||
import cn.hutool.core.net.URLDecoder;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* <p>Description: SQL注入处理工具类 </p>
|
||||
*
|
||||
* @author : gengwei.zheng
|
||||
* @date : 2021/9/1 12:19
|
||||
*/
|
||||
public class SQLInjectionUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SQLInjectionUtils.class);
|
||||
|
||||
private static final String SQL_REGEX = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
|
||||
|
||||
private static final Pattern SQL_PATTERN = Pattern.compile(SQL_REGEX, Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static boolean matching(String lowerValue, String param) {
|
||||
if (SQL_PATTERN.matcher(param).find()) {
|
||||
log.error("[Eurynome] |- The parameter contains keywords {} that do not allow SQL!", lowerValue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String toLowerCase(Object obj) {
|
||||
//这里需要将参数转换为小写来处理
|
||||
return Optional.ofNullable(obj)
|
||||
.map(Object::toString)
|
||||
.map(String::toLowerCase)
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
private static boolean checking(Object value) {
|
||||
//这里需要将参数转换为小写来处理
|
||||
String lowerValue = toLowerCase(value);
|
||||
return matching(lowerValue, lowerValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* get请求sql注入校验
|
||||
*
|
||||
* @param value 具体的检验
|
||||
* @return 是否存在不合规内容
|
||||
*/
|
||||
public static boolean checkForGet(String value) {
|
||||
|
||||
//参数需要url编码
|
||||
//这里需要将参数转换为小写来处理
|
||||
//不改变原值
|
||||
//value示例 order=asc&pageNum=1&pageSize=100&parentId=0
|
||||
String lowerValue = URLDecoder.decode(value, StandardCharsets.UTF_8).toLowerCase();
|
||||
|
||||
//获取到请求中所有参数值-取每个key=value组合第一个等号后面的值
|
||||
return Stream.of(lowerValue.split("\\&"))
|
||||
.map(kp -> kp.substring(kp.indexOf(SymbolConstants.EQUAL) + 1))
|
||||
.parallel()
|
||||
.anyMatch(param -> matching(lowerValue, param));
|
||||
}
|
||||
|
||||
/**
|
||||
* post请求sql注入校验
|
||||
*
|
||||
* @param value 具体的检验
|
||||
* @return 是否存在不合规内容
|
||||
*/
|
||||
public static boolean checkForPost(String value) {
|
||||
|
||||
Object jsonObj = JSON.parse(value);
|
||||
if (jsonObj instanceof JSONObject) {
|
||||
JSONObject json = (JSONObject) jsonObj;
|
||||
return json.entrySet().stream().parallel().anyMatch(entry -> checking(entry.getValue()));
|
||||
}
|
||||
|
||||
if (jsonObj instanceof JSONArray) {
|
||||
JSONArray json = (JSONArray) jsonObj;
|
||||
return json.stream().parallel().anyMatch(SQLInjectionUtils::checking);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 Gengwei Zheng (herodotus@aliyun.com)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Project Name: eurynome-cloud
|
||||
* Module Name: eurynome-cloud-common
|
||||
* File Name: XssUtils.java
|
||||
* Author: gengwei.zheng
|
||||
* Date: 2021/09/01 12:21:01
|
||||
*/
|
||||
|
||||
package cn.herodotus.eurynome.common.utils;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.owasp.validator.html.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* <p>Description: Antisamy 单例 工具类 </p>
|
||||
*
|
||||
* @author : gengwei.zheng
|
||||
* @date : 2021/9/1 12:21
|
||||
*/
|
||||
public class XssUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(XssUtils.class);
|
||||
|
||||
private static volatile XssUtils INSTANCE;
|
||||
private final AntiSamy antiSamy;
|
||||
|
||||
private XssUtils() {
|
||||
Policy policy = createPolicy();
|
||||
this.antiSamy = ObjectUtils.isNotEmpty(policy) ? new AntiSamy(policy) : new AntiSamy();
|
||||
}
|
||||
|
||||
private static XssUtils getInstance() {
|
||||
if (ObjectUtils.isEmpty(INSTANCE)) {
|
||||
synchronized (XssUtils.class) {
|
||||
if (ObjectUtils.isEmpty(INSTANCE)) {
|
||||
INSTANCE = new XssUtils();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private Policy createPolicy() {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL("classpath:antisamy/antisamy-ebay.xml");
|
||||
return Policy.getInstance(url);
|
||||
} catch (IOException | PolicyException e) {
|
||||
log.warn("[Eurynome] |- Antisamy create policy error! {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AntiSamy antiSamy() {
|
||||
return antiSamy;
|
||||
}
|
||||
|
||||
public static AntiSamy getAntiSamy() {
|
||||
return XssUtils.getInstance().antiSamy();
|
||||
}
|
||||
|
||||
public static CleanResults scan(String taintedHtml) throws ScanException, PolicyException {
|
||||
return getAntiSamy().scan(taintedHtml);
|
||||
}
|
||||
|
||||
public static String cleaning(String value) {
|
||||
try {
|
||||
log.trace("[Eurynome] |- Before Antisamy cleaning XSS, value is: [{}]", value);
|
||||
|
||||
// 使用AntiSamy清洗数据
|
||||
final CleanResults cleanResults = XssUtils.scan(value);
|
||||
// 获得安全的HTML输出
|
||||
value = cleanResults.getCleanHTML();
|
||||
// 对转义的HTML特殊字符(<、>、"等)进行反转义,因为AntiSamy调用scan方法时会将特殊字符转义
|
||||
String result = StringEscapeUtils.unescapeHtml4(value);
|
||||
|
||||
log.trace("[Eurynome] |- After Antisamy cleaning XSS, value is: [{}]", value);
|
||||
return result;
|
||||
} catch (ScanException | PolicyException e) {
|
||||
log.error("[Eurynome] |- Antisamy cleaning XSS catch error! {}", e.getMessage());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,147 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xsd:element name="anti-samy-rules">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="include" type="Include" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="directives" type="Directives"/>
|
||||
<xsd:element name="common-regexps" type="CommonRegexps"/>
|
||||
<xsd:element name="common-attributes" type="AttributeList"/>
|
||||
<xsd:element name="global-tag-attributes" type="AttributeList"/>
|
||||
<xsd:element name="dynamic-tag-attributes" type="AttributeList" minOccurs="0"/>
|
||||
<xsd:element name="tags-to-encode" type="TagsToEncodeList" minOccurs="0"/>
|
||||
<xsd:element name="tag-rules" type="TagRules"/>
|
||||
<xsd:element name="css-rules" type="CSSRules"/>
|
||||
<xsd:element name="allowed-empty-tags" type="AllowedEmptyTags" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="Include">
|
||||
<xsd:attribute name="href" use="required" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="Directives">
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="directive" type="Directive" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="Directive">
|
||||
<xsd:attribute name="name" use="required"/>
|
||||
<xsd:attribute name="value" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="CommonRegexps">
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="regexp" type="RegExp" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="AttributeList">
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="attribute" type="Attribute" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="TagsToEncodeList">
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="tag" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="TagRules">
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="tag" type="Tag" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="Tag">
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="attribute" type="Attribute" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required"/>
|
||||
<xsd:attribute name="action" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="AllowedEmptyTags">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="literal-list" type="LiteralList" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexTyp |