mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 12:39:05 +08:00
feat(encrypt-api): 支持全局请求报文加密
BREAKING CHANGE: 文档和用法完全和之前不一样,不需要耦合代码
This commit is contained in:
1 parent
1665598520
commit
ec9eb5a69e
38 files changed
+678
-965
No files matched your search
@@ -62,7 +62,11 @@
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-app-server-api</artifactId>
|
||||
<version>5.7.0</version>
|
||||
</dependency>
|
||||
<!--必备: API加解密 -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-encrypt-api</artifactId>
|
||||
</dependency>
|
||||
<!--必备: log 依赖-->
|
||||
<dependency>
|
||||
|
||||
@@ -83,6 +83,11 @@
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
<!-- API加解密 -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-encrypt-api</artifactId>
|
||||
</dependency>
|
||||
<!-- 调用验证码核心模块 -->
|
||||
<dependency>
|
||||
<groupId>io.springboot.plugin</groupId>
|
||||
|
||||
+190
-180
@@ -25,228 +25,238 @@ package com.pig4cloud.pigx.common.core.constant;
|
||||
*/
|
||||
public interface SecurityConstants {
|
||||
|
||||
/**
|
||||
* 启动时是否检查Inner注解安全性
|
||||
*/
|
||||
boolean INNER_CHECK = true;
|
||||
/**
|
||||
* 启动时是否检查Inner注解安全性
|
||||
*/
|
||||
boolean INNER_CHECK = true;
|
||||
|
||||
/**
|
||||
* 刷新
|
||||
*/
|
||||
String REFRESH_TOKEN = "refresh_token";
|
||||
/**
|
||||
* 刷新
|
||||
*/
|
||||
String REFRESH_TOKEN = "refresh_token";
|
||||
|
||||
/**
|
||||
* 验证码有效期
|
||||
*/
|
||||
int CODE_TIME = 60;
|
||||
/**
|
||||
* 验证码有效期
|
||||
*/
|
||||
int CODE_TIME = 60;
|
||||
|
||||
/**
|
||||
* 验证码长度
|
||||
*/
|
||||
String CODE_SIZE = "4";
|
||||
/**
|
||||
* 验证码长度
|
||||
*/
|
||||
String CODE_SIZE = "4";
|
||||
|
||||
/**
|
||||
* 角色前缀
|
||||
*/
|
||||
String ROLE = "ROLE_";
|
||||
/**
|
||||
* 角色前缀
|
||||
*/
|
||||
String ROLE = "ROLE_";
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
String PIGX_PREFIX = "pigx_";
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
String PIGX_PREFIX = "pigx_";
|
||||
|
||||
/**
|
||||
* token 相关前缀
|
||||
*/
|
||||
String TOKEN_PREFIX = "token:";
|
||||
/**
|
||||
* token 相关前缀
|
||||
*/
|
||||
String TOKEN_PREFIX = "token:";
|
||||
|
||||
/**
|
||||
* oauth 相关前缀
|
||||
*/
|
||||
String OAUTH_PREFIX = "oauth:";
|
||||
/**
|
||||
* oauth 相关前缀
|
||||
*/
|
||||
String OAUTH_PREFIX = "oauth:";
|
||||
|
||||
/**
|
||||
* 授权码模式code key 前缀
|
||||
*/
|
||||
String OAUTH_CODE_PREFIX = "oauth:code:";
|
||||
/**
|
||||
* 授权码模式code key 前缀
|
||||
*/
|
||||
String OAUTH_CODE_PREFIX = "oauth:code:";
|
||||
|
||||
/**
|
||||
* 项目的license
|
||||
*/
|
||||
String PIGX_LICENSE = "https://pig4cloud.com";
|
||||
/**
|
||||
* 项目的license
|
||||
*/
|
||||
String PIGX_LICENSE = "https://pig4cloud.com";
|
||||
|
||||
/**
|
||||
* 内部
|
||||
*/
|
||||
String FROM_IN = "Y";
|
||||
/**
|
||||
* 内部
|
||||
*/
|
||||
String FROM_IN = "Y";
|
||||
|
||||
/**
|
||||
* 标志
|
||||
*/
|
||||
String FROM = "from";
|
||||
/**
|
||||
* 标志
|
||||
*/
|
||||
String FROM = "from";
|
||||
|
||||
/**
|
||||
* 请求header
|
||||
*/
|
||||
String HEADER_FROM_IN = FROM + "=" + FROM_IN;
|
||||
/**
|
||||
* Feign 用户代理
|
||||
*/
|
||||
String FEIGN_USER_AGENT = "pigx-feign";
|
||||
|
||||
/**
|
||||
* OAUTH URL
|
||||
*/
|
||||
String OAUTH_TOKEN_URL = "/oauth2/token";
|
||||
/**
|
||||
* 请求header
|
||||
*/
|
||||
String HEADER_FROM_IN = FROM + "=" + FROM_IN;
|
||||
|
||||
/**
|
||||
* 移动端授权
|
||||
*/
|
||||
String GRANT_MOBILE = "mobile";
|
||||
/**
|
||||
* OAUTH URL
|
||||
*/
|
||||
String OAUTH_TOKEN_URL = "/oauth2/token";
|
||||
|
||||
/**
|
||||
* TOC 客户端
|
||||
*/
|
||||
String HEADER_TOC = "CLIENT-TOC";
|
||||
/**
|
||||
* 移动端授权
|
||||
*/
|
||||
String GRANT_MOBILE = "mobile";
|
||||
|
||||
/**
|
||||
* TOC 客户端
|
||||
*/
|
||||
String HEADER_TOC_YES = "Y";
|
||||
/**
|
||||
* TOC 客户端
|
||||
*/
|
||||
String HEADER_TOC = "CLIENT-TOC";
|
||||
|
||||
/**
|
||||
* QQ获取token
|
||||
*/
|
||||
String QQ_AUTHORIZATION_CODE_URL = "https://graph.qq.com/oauth2.0/token?grant_type="
|
||||
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
|
||||
/**
|
||||
* TOC 客户端
|
||||
*/
|
||||
String HEADER_TOC_YES = "Y";
|
||||
|
||||
/**
|
||||
* 微信获取OPENID
|
||||
*/
|
||||
String WX_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/oauth2/access_token"
|
||||
+ "?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
|
||||
/**
|
||||
* QQ获取token
|
||||
*/
|
||||
String QQ_AUTHORIZATION_CODE_URL = "https://graph.qq.com/oauth2.0/token?grant_type="
|
||||
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
|
||||
|
||||
/**
|
||||
* 微信小程序OPENID
|
||||
*/
|
||||
String MINI_APP_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/jscode2session"
|
||||
+ "?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
|
||||
/**
|
||||
* 微信获取OPENID
|
||||
*/
|
||||
String WX_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/oauth2/access_token"
|
||||
+ "?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
|
||||
|
||||
/**
|
||||
* 码云获取token
|
||||
*/
|
||||
String GITEE_AUTHORIZATION_CODE_URL = "https://gitee.com/oauth/token?grant_type="
|
||||
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
|
||||
/**
|
||||
* 微信小程序OPENID
|
||||
*/
|
||||
String MINI_APP_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/jscode2session"
|
||||
+ "?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
|
||||
|
||||
/**
|
||||
* 开源中国获取token
|
||||
*/
|
||||
String OSC_AUTHORIZATION_CODE_URL = "https://www.oschina.net/action/openapi/token";
|
||||
/**
|
||||
* 码云获取token
|
||||
*/
|
||||
String GITEE_AUTHORIZATION_CODE_URL = "https://gitee.com/oauth/token?grant_type="
|
||||
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
|
||||
|
||||
/**
|
||||
* QQ获取用户信息
|
||||
*/
|
||||
String QQ_USER_INFO_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s";
|
||||
/**
|
||||
* 开源中国获取token
|
||||
*/
|
||||
String OSC_AUTHORIZATION_CODE_URL = "https://www.oschina.net/action/openapi/token";
|
||||
|
||||
/**
|
||||
* 码云获取用户信息
|
||||
*/
|
||||
String GITEE_USER_INFO_URL = "https://gitee.com/api/v5/user?access_token=%s";
|
||||
/**
|
||||
* QQ获取用户信息
|
||||
*/
|
||||
String QQ_USER_INFO_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s";
|
||||
|
||||
/**
|
||||
* 开源中国用户信息
|
||||
*/
|
||||
String OSC_USER_INFO_URL = "https://www.oschina.net/action/openapi/user?access_token=%s&dataType=json";
|
||||
/**
|
||||
* 码云获取用户信息
|
||||
*/
|
||||
String GITEE_USER_INFO_URL = "https://gitee.com/api/v5/user?access_token=%s";
|
||||
|
||||
/**
|
||||
* 钉钉获取 token
|
||||
*/
|
||||
String DING_OLD_GET_TOKEN = "https://oapi.dingtalk.com/gettoken";
|
||||
/**
|
||||
* 开源中国用户信息
|
||||
*/
|
||||
String OSC_USER_INFO_URL = "https://www.oschina.net/action/openapi/user?access_token=%s&dataType=json";
|
||||
|
||||
/**
|
||||
* 钉钉同步部门列表
|
||||
*/
|
||||
String DING_OLD_DEPT_URL = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
|
||||
/**
|
||||
* 钉钉获取 token
|
||||
*/
|
||||
String DING_OLD_GET_TOKEN = "https://oapi.dingtalk.com/gettoken";
|
||||
|
||||
/**
|
||||
* 钉钉部门用户id列表
|
||||
*/
|
||||
String DING_DEPT_USERIDS_URL = "https://oapi.dingtalk.com/topapi/user/listid";
|
||||
/**
|
||||
* 钉钉同步部门列表
|
||||
*/
|
||||
String DING_OLD_DEPT_URL = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
|
||||
|
||||
/**
|
||||
* 钉钉用户详情
|
||||
*/
|
||||
String DING_USER_INFO_URL = "https://oapi.dingtalk.com/topapi/v2/user/get";
|
||||
/**
|
||||
* 钉钉部门用户id列表
|
||||
*/
|
||||
String DING_DEPT_USERIDS_URL = "https://oapi.dingtalk.com/topapi/user/listid";
|
||||
|
||||
/**
|
||||
* {bcrypt} 加密的特征码
|
||||
*/
|
||||
String BCRYPT = "{bcrypt}";
|
||||
/**
|
||||
* 钉钉用户详情
|
||||
*/
|
||||
String DING_USER_INFO_URL = "https://oapi.dingtalk.com/topapi/v2/user/get";
|
||||
|
||||
/**
|
||||
* 客户端模式
|
||||
*/
|
||||
String CLIENT_CREDENTIALS = "client_credentials";
|
||||
/**
|
||||
* {bcrypt} 加密的特征码
|
||||
*/
|
||||
String BCRYPT = "{bcrypt}";
|
||||
|
||||
/**
|
||||
* 客户端编号
|
||||
*/
|
||||
String CLIENT_ID = "client_id";
|
||||
/**
|
||||
* 客户端模式
|
||||
*/
|
||||
String CLIENT_CREDENTIALS = "client_credentials";
|
||||
|
||||
/**
|
||||
* 客户端唯一令牌
|
||||
*/
|
||||
String CLIENT_RECREATE = "recreate_flag";
|
||||
/**
|
||||
* 客户端编号
|
||||
*/
|
||||
String CLIENT_ID = "client_id";
|
||||
|
||||
/**
|
||||
* 用户ID字段
|
||||
*/
|
||||
String DETAILS_USER_ID = "user_id";
|
||||
/**
|
||||
* 客户端唯一令牌
|
||||
*/
|
||||
String CLIENT_RECREATE = "recreate_flag";
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
String DETAILS_USERNAME = "username";
|
||||
/**
|
||||
* 用户ID字段
|
||||
*/
|
||||
String DETAILS_USER_ID = "user_id";
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
String NAME = "name";
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
String DETAILS_USERNAME = "username";
|
||||
|
||||
/**
|
||||
* 协议字段
|
||||
*/
|
||||
String DETAILS_LICENSE = "license";
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
String NAME = "name";
|
||||
|
||||
/**
|
||||
* 激活字段 兼容外围系统接入
|
||||
*/
|
||||
String ACTIVE = "active";
|
||||
/**
|
||||
* 协议字段
|
||||
*/
|
||||
String DETAILS_LICENSE = "license";
|
||||
|
||||
/**
|
||||
* AES 加密
|
||||
*/
|
||||
String AES = "aes";
|
||||
/**
|
||||
* 激活字段 兼容外围系统接入
|
||||
*/
|
||||
String ACTIVE = "active";
|
||||
|
||||
/**
|
||||
* 授权码模式confirm
|
||||
*/
|
||||
String CUSTOM_CONSENT_PAGE_URI = "/token/confirm_access";
|
||||
/**
|
||||
* AES 加密
|
||||
*/
|
||||
String AES = "aes";
|
||||
|
||||
/**
|
||||
* {noop} 加密的特征码
|
||||
*/
|
||||
String NOOP = "{noop}";
|
||||
/**
|
||||
* 授权码模式confirm
|
||||
*/
|
||||
String CUSTOM_CONSENT_PAGE_URI = "/oauth2/confirm_access";
|
||||
|
||||
/**
|
||||
* 短信登录 参数名称
|
||||
*/
|
||||
String SMS_PARAMETER_NAME = "mobile";
|
||||
/**
|
||||
* {noop} 加密的特征码
|
||||
*/
|
||||
String NOOP = "{noop}";
|
||||
|
||||
/**
|
||||
* 手机号登录
|
||||
*/
|
||||
String APP = "mobile";
|
||||
/**
|
||||
* 短信登录 参数名称
|
||||
*/
|
||||
String SMS_PARAMETER_NAME = "mobile";
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
String DETAILS_USER = "user_info";
|
||||
/**
|
||||
* 手机号登录
|
||||
*/
|
||||
String APP = "mobile";
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
String DETAILS_USER = "user_info";
|
||||
|
||||
/**
|
||||
* 不脱敏的权限
|
||||
*/
|
||||
String NO_MASK = "no_mask";
|
||||
|
||||
}
|
||||
+18
-21
@@ -1,12 +1,8 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.config.ApiEncryptParamConfiguration;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.config.ApiEncryptProperties;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.core.ApiDecryptRequestBodyAdvice;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.core.ApiEncryptResponseBodyAdvice;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.core.DefaultSecretKeyResolver;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.core.ISecretKeyResolver;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.core.*;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -22,24 +18,25 @@ import org.springframework.context.annotation.Import;
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(ApiEncryptProperties.class)
|
||||
@Import({ ApiEncryptParamConfiguration.class, ApiDecryptRequestBodyAdvice.class, ApiEncryptResponseBodyAdvice.class })
|
||||
@ConditionalOnProperty(value = ApiEncryptProperties.PREFIX + ".enable", havingValue = "true", matchIfMissing = true)
|
||||
@Import({DecryptQueryParametersFilter.class, ApiDecryptRequestBodyAdvice.class, ApiEncryptResponseBodyAdvice.class})
|
||||
@ConditionalOnProperty(value = ApiEncryptProperties.PREFIX + ".enable", havingValue = "true")
|
||||
public class ApiEncryptAutoConfiguration {
|
||||
|
||||
static {
|
||||
// 关闭hutool 强制关闭Bouncy Castle库的依赖
|
||||
SecureUtil.disableBouncyCastle();
|
||||
}
|
||||
static {
|
||||
// 关闭hutool 强制关闭Bouncy Castle库的依赖
|
||||
SecureUtil.disableBouncyCastle();
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的 key 获取策略
|
||||
* @param apiEncryptProperties
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ISecretKeyResolver secretKeyResolver(ApiEncryptProperties apiEncryptProperties) {
|
||||
return new DefaultSecretKeyResolver(apiEncryptProperties);
|
||||
}
|
||||
/**
|
||||
* 默认的 key 获取策略
|
||||
*
|
||||
* @param apiEncryptProperties
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ISecretKeyResolver secretKeyResolver(ApiEncryptProperties apiEncryptProperties) {
|
||||
return new DefaultSecretKeyResolver(apiEncryptProperties);
|
||||
}
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 无加密配置注解
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2025/01/08
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface NoEncrypt {
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.crypto;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt.ApiDecrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt.ApiEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* AES加密解密注解
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@ApiEncrypt(EncryptType.AES)
|
||||
@ApiDecrypt(EncryptType.AES)
|
||||
public @interface ApiCryptoAes {
|
||||
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.crypto;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt.ApiDecrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt.ApiEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* DES加密解密注解
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@ApiEncrypt(EncryptType.DES)
|
||||
@ApiDecrypt(EncryptType.DES)
|
||||
public @interface ApiCryptoDes {
|
||||
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.crypto;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt.ApiDecrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt.ApiEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* RSA加密解密注解
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@ApiEncrypt(EncryptType.RSA)
|
||||
@ApiDecrypt(EncryptType.RSA)
|
||||
public @interface ApiCryptoRsa {
|
||||
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.crypto;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt.ApiDecrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt.ApiEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Sm4加密解密注解
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@ApiEncrypt(EncryptType.SM4)
|
||||
@ApiDecrypt(EncryptType.SM4)
|
||||
public @interface ApiCryptoSm4 {
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 解密含有{@link org.springframework.web.bind.annotation.RequestBody}注解的参数请求数据,可用于整个控制类或者某个控制器上
|
||||
* </p>
|
||||
*
|
||||
* @author licoy.cn
|
||||
* @author L.cm
|
||||
* @version 2018/9/7
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface ApiDecrypt {
|
||||
|
||||
/**
|
||||
* 参数名称 仅针对GET请求有效
|
||||
* @return string
|
||||
*/
|
||||
String parameter() default "";
|
||||
|
||||
/**
|
||||
* 解密类型
|
||||
* @return 类型
|
||||
*/
|
||||
EncryptType value();
|
||||
|
||||
/**
|
||||
* 私钥,用于某些需要单独配置私钥的方法,没有时读取全局配置的私钥
|
||||
* @return 私钥
|
||||
*/
|
||||
String secretKey() default "";
|
||||
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* aes 界面
|
||||
*
|
||||
* @author licoy.cn
|
||||
* @author L.cm
|
||||
* @version 2018/9/7
|
||||
* @see ApiDecrypt
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ApiDecrypt(EncryptType.AES)
|
||||
public @interface ApiDecryptAes {
|
||||
|
||||
/**
|
||||
* 参数名称 仅针对GET请求有效
|
||||
* @return string
|
||||
*/
|
||||
@AliasFor(annotation = ApiDecrypt.class, attribute = "parameter")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Alias for {@link ApiDecrypt#secretKey()}.
|
||||
* @return {String}
|
||||
*/
|
||||
@AliasFor(annotation = ApiDecrypt.class)
|
||||
String secretKey() default "";
|
||||
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author licoy.cn
|
||||
* @version 2018/9/7
|
||||
* @see ApiDecrypt
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ApiDecrypt(EncryptType.DES)
|
||||
public @interface ApiDecryptDes {
|
||||
|
||||
/**
|
||||
* 参数名称 仅针对GET请求有效
|
||||
* @return string
|
||||
*/
|
||||
@AliasFor(annotation = ApiDecrypt.class, attribute = "parameter")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Alias for {@link ApiDecrypt#secretKey()}.
|
||||
* @return {String}
|
||||
*/
|
||||
@AliasFor(annotation = ApiDecrypt.class)
|
||||
String secretKey() default "";
|
||||
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author licoy.cn
|
||||
* @version 2018/9/7
|
||||
* @see ApiDecrypt
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ApiDecrypt(EncryptType.RSA)
|
||||
public @interface ApiDecryptRsa {
|
||||
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* sm4 解密
|
||||
*
|
||||
* @author lengleng
|
||||
* @version 2023/8/30
|
||||
* @see ApiDecrypt
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ApiDecrypt(EncryptType.SM4)
|
||||
public @interface ApiDecryptSm4 {
|
||||
|
||||
/**
|
||||
* 参数名称 仅针对GET请求有效
|
||||
* @return string
|
||||
*/
|
||||
@AliasFor(annotation = ApiDecrypt.class, attribute = "parameter")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Alias for {@link ApiDecrypt#secretKey()}.
|
||||
* @return {String}
|
||||
*/
|
||||
@AliasFor(annotation = ApiDecrypt.class)
|
||||
String secretKey() default "";
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 加密{@link org.springframework.web.bind.annotation.ResponseBody}响应数据,可用于整个控制类或者某个控制器上
|
||||
* </p>
|
||||
*
|
||||
* @author licoy.cn
|
||||
* @author L.cm
|
||||
* @version 2018/9/4
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface ApiEncrypt {
|
||||
|
||||
/**
|
||||
* 加密类型
|
||||
* @return 类型
|
||||
*/
|
||||
EncryptType value();
|
||||
|
||||
/**
|
||||
* 私钥,用于某些需要单独配置私钥的方法,没有时读取全局配置的私钥
|
||||
* @return 私钥
|
||||
*/
|
||||
String secretKey() default "";
|
||||
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* aes 加密
|
||||
*
|
||||
* @author licoy.cn
|
||||
* @author L.cm
|
||||
* @version 2018/9/4
|
||||
* @see ApiEncrypt
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ApiEncrypt(EncryptType.AES)
|
||||
public @interface ApiEncryptAes {
|
||||
|
||||
/**
|
||||
* Alias for {@link ApiEncrypt#secretKey()}.
|
||||
* @return {String}
|
||||
*/
|
||||
@AliasFor(annotation = ApiEncrypt.class)
|
||||
String secretKey() default "";
|
||||
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* des 加密
|
||||
*
|
||||
* @author licoy.cn
|
||||
* @author L.cm
|
||||
* @version 2018/9/4
|
||||
* @see ApiEncrypt
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ApiEncrypt(EncryptType.DES)
|
||||
public @interface ApiEncryptDes {
|
||||
|
||||
/**
|
||||
* Alias for {@link ApiEncrypt#secretKey()}.
|
||||
* @return {String}
|
||||
*/
|
||||
@AliasFor(annotation = ApiEncrypt.class)
|
||||
String secretKey() default "";
|
||||
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* rsa body 加密
|
||||
*
|
||||
* @author licoy.cn
|
||||
* @author L.cm
|
||||
* @version 2018/9/4
|
||||
* @see ApiEncrypt
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ApiEncrypt(EncryptType.RSA)
|
||||
public @interface ApiEncryptRsa {
|
||||
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* sm4 加密
|
||||
*
|
||||
* @author lengleng
|
||||
* @version 2023/8/30
|
||||
* @see ApiEncrypt
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ApiEncrypt(EncryptType.SM4)
|
||||
public @interface ApiEncryptSm4 {
|
||||
|
||||
/**
|
||||
* Alias for {@link ApiEncrypt#secretKey()}.
|
||||
* @return {String}
|
||||
*/
|
||||
@AliasFor(annotation = ApiEncrypt.class)
|
||||
String secretKey() default "";
|
||||
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.api.encrypt.config;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.core.ApiDecryptParamResolver;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* api 签名自动配置
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(value = ApiEncryptProperties.PREFIX + ".enable", havingValue = "true", matchIfMissing = true)
|
||||
public class ApiEncryptParamConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final ApiEncryptProperties apiEncryptProperties;
|
||||
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
argumentResolvers.add(new ApiDecryptParamResolver(apiEncryptProperties));
|
||||
}
|
||||
|
||||
}
|
||||
+64
-33
@@ -1,8 +1,18 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.config;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.NoEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* api 签名配置类
|
||||
@@ -14,46 +24,67 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(ApiEncryptProperties.PREFIX)
|
||||
public class ApiEncryptProperties {
|
||||
public class ApiEncryptProperties implements InitializingBean {
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
public static final String PREFIX = "security.api.encrypt";
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
public static final String PREFIX = "security.api.encrypt";
|
||||
|
||||
/**
|
||||
* 是否开启 api 签名
|
||||
*/
|
||||
private boolean enable = true;
|
||||
/**
|
||||
* 是否开启 api 签名
|
||||
*/
|
||||
private boolean enable;
|
||||
|
||||
/**
|
||||
* url的参数签名,传递的参数名。例如:/user?data=签名后的数据
|
||||
*/
|
||||
private String paramName = "encryption";
|
||||
/**
|
||||
* body 内容 json key, 默认:encryption
|
||||
*/
|
||||
private String bodyJsonKey = "encryption";
|
||||
|
||||
/**
|
||||
* body 内容 json key, 默认:encryption
|
||||
*/
|
||||
private String bodyJsonKey = "encryption";
|
||||
/**
|
||||
* 默认加密类型
|
||||
*/
|
||||
private EncryptType defaultEncryptType = EncryptType.AES;
|
||||
|
||||
/**
|
||||
* aes 密钥
|
||||
*/
|
||||
private String aesKey;
|
||||
/**
|
||||
* aes 密钥
|
||||
*/
|
||||
private String aesKey;
|
||||
|
||||
/**
|
||||
* des 密钥
|
||||
*/
|
||||
private String desKey;
|
||||
/**
|
||||
* des 密钥
|
||||
*/
|
||||
private String desKey;
|
||||
|
||||
/**
|
||||
* sm4 密钥
|
||||
*/
|
||||
private String sm4Key;
|
||||
/**
|
||||
* sm4 密钥
|
||||
*/
|
||||
private String sm4Key;
|
||||
|
||||
/**
|
||||
* rsa 私钥
|
||||
*/
|
||||
private String rsaPrivateKey;
|
||||
/**
|
||||
* rsa 私钥
|
||||
*/
|
||||
private String rsaPrivateKey;
|
||||
|
||||
|
||||
/**
|
||||
* 跳过 URL
|
||||
*/
|
||||
private List<String> skipUrl = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
RequestMappingHandlerMapping mapping = SpringContextHolder.getBean("requestMappingHandlerMapping");
|
||||
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
|
||||
|
||||
map.keySet().forEach(info -> {
|
||||
HandlerMethod handlerMethod = map.get(info);
|
||||
|
||||
// 获取方法上边的注解 替代path variable 为 *
|
||||
NoEncrypt method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), NoEncrypt.class);
|
||||
Optional.ofNullable(method)
|
||||
.ifPresent(inner -> skipUrl.addAll(Objects.requireNonNull(info.getPathPatternsCondition())
|
||||
.getPatternValues()));
|
||||
});
|
||||
}
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.api.encrypt.core;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt.ApiDecrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.bean.CryptoInfoBean;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.config.ApiEncryptProperties;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.util.ApiCryptoUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* param 参数 解析
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ApiDecryptParamResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final ApiEncryptProperties properties;
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return AnnotatedElementUtils.hasAnnotation(parameter.getParameter(), ApiDecrypt.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
Parameter parameter = methodParameter.getParameter();
|
||||
ApiDecrypt apiDecrypt = AnnotatedElementUtils.getMergedAnnotation(parameter, ApiDecrypt.class);
|
||||
|
||||
if (StrUtil.isBlank(apiDecrypt.parameter())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String text = webRequest.getParameter(properties.getParamName());
|
||||
if (StrUtil.isBlank(text)) {
|
||||
return null;
|
||||
}
|
||||
CryptoInfoBean infoBean = new CryptoInfoBean(apiDecrypt.value(), apiDecrypt.secretKey());
|
||||
byte[] textBytes = text.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] decryptData = ApiCryptoUtil.decryptData(textBytes, infoBean);
|
||||
JSONObject jsonObject = JSONUtil.parseObj(decryptData);
|
||||
return jsonObject.get(apiDecrypt.parameter(), parameter.getType());
|
||||
}
|
||||
|
||||
}
|
||||
+64
-54
@@ -3,12 +3,14 @@ package com.pig4cloud.pigx.common.api.encrypt.core;
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt.ApiDecrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.NoEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.bean.CryptoInfoBean;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.bean.DecryptHttpInputMessage;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.config.ApiEncryptProperties;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.exception.DecryptBodyFailException;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.util.ApiCryptoUtil;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.WebUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -27,6 +29,8 @@ import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.pig4cloud.pigx.common.api.encrypt.util.ApiCryptoUtil.KEY_EXTRACTORS;
|
||||
|
||||
/**
|
||||
* 请求数据的加密信息解密处理<br>
|
||||
* 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.RequestBody}</strong>
|
||||
@@ -41,68 +45,74 @@ import java.util.Map;
|
||||
@Order(1)
|
||||
@ControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(value = ApiEncryptProperties.PREFIX + ".enable", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnProperty(value = ApiEncryptProperties.PREFIX + ".enable", havingValue = "true")
|
||||
public class ApiDecryptRequestBodyAdvice implements RequestBodyAdvice {
|
||||
|
||||
private final ApiEncryptProperties properties;
|
||||
private final ApiEncryptProperties properties;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return AnnotationUtil.hasAnnotation(methodParameter.getMethod(), ApiDecrypt.class);
|
||||
}
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
// 非feign调用才进行解密
|
||||
if (StrUtil.isNotBlank(WebUtils.getRequest().getHeader(SecurityConstants.FEIGN_USER_AGENT))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return body;
|
||||
}
|
||||
if (AnnotationUtil.hasAnnotation(methodParameter.getMethod(), NoEncrypt.class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
|
||||
// 判断 body 是否为空
|
||||
InputStream messageBody = inputMessage.getBody();
|
||||
if (messageBody.available() <= 0) {
|
||||
return inputMessage;
|
||||
}
|
||||
return properties.getSkipUrl().stream().noneMatch(WebUtils.getRequest().getRequestURI()::contains);
|
||||
}
|
||||
|
||||
CryptoInfoBean cryptoInfoBean = ApiCryptoUtil.getDecryptInfo(parameter);
|
||||
if (cryptoInfoBean == null) {
|
||||
throw new DecryptBodyFailException("获取解密注解配置为空");
|
||||
}
|
||||
// base64 byte array
|
||||
byte[] bodyByteArray = StreamUtils.copyToByteArray(messageBody);
|
||||
// body 内容 json key, 默认:data {"data":"base64加密字符串"}
|
||||
String bodyJsonKey = properties.getBodyJsonKey();
|
||||
@Override
|
||||
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return body;
|
||||
}
|
||||
|
||||
byte[] decryptedBody = null;
|
||||
if (StrUtil.isBlank(bodyJsonKey)) {
|
||||
decryptedBody = ApiCryptoUtil.decryptData(bodyByteArray, cryptoInfoBean);
|
||||
}
|
||||
else {
|
||||
@Override
|
||||
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
|
||||
// 判断 body 是否为空
|
||||
InputStream messageBody = inputMessage.getBody();
|
||||
if (messageBody.available() <= 0) {
|
||||
return inputMessage;
|
||||
}
|
||||
|
||||
Map<String, Object> data = objectMapper.readValue(bodyByteArray, Map.class);
|
||||
String content = (String) data.get(bodyJsonKey);
|
||||
if (content != null) {
|
||||
decryptedBody = ApiCryptoUtil.decryptData(content.getBytes(StandardCharsets.UTF_8), cryptoInfoBean);
|
||||
}
|
||||
}
|
||||
if (decryptedBody == null) {
|
||||
throw new DecryptBodyFailException(
|
||||
"Decryption error, " + "please check if the selected source data is encrypted correctly."
|
||||
+ " (解密错误,请检查选择的源数据的加密方式是否正确。)");
|
||||
}
|
||||
InputStream inputStream = new ByteArrayInputStream(decryptedBody);
|
||||
return new DecryptHttpInputMessage(inputStream, inputMessage.getHeaders());
|
||||
}
|
||||
String key = KEY_EXTRACTORS.get(properties.getDefaultEncryptType()).apply(properties);
|
||||
CryptoInfoBean cryptoInfoBean = new CryptoInfoBean(properties.getDefaultEncryptType(), key);
|
||||
// base64 byte array
|
||||
byte[] bodyByteArray = StreamUtils.copyToByteArray(messageBody);
|
||||
// body 内容 json key, 默认:data {"data":"base64加密字符串"}
|
||||
String bodyJsonKey = properties.getBodyJsonKey();
|
||||
|
||||
@Override
|
||||
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return body;
|
||||
}
|
||||
byte[] decryptedBody = null;
|
||||
if (StrUtil.isBlank(bodyJsonKey)) {
|
||||
decryptedBody = ApiCryptoUtil.decryptData(bodyByteArray, cryptoInfoBean);
|
||||
} else {
|
||||
|
||||
Map<String, Object> data = objectMapper.readValue(bodyByteArray, Map.class);
|
||||
String content = (String) data.get(bodyJsonKey);
|
||||
if (content != null) {
|
||||
decryptedBody = ApiCryptoUtil.decryptData(content.getBytes(StandardCharsets.UTF_8), cryptoInfoBean);
|
||||
}
|
||||
}
|
||||
if (decryptedBody == null) {
|
||||
throw new DecryptBodyFailException(
|
||||
"Decryption error, " + "please check if the selected source data is encrypted correctly."
|
||||
+ " (解密错误,请检查选择的源数据的加密方式是否正确。)");
|
||||
}
|
||||
InputStream inputStream = new ByteArrayInputStream(decryptedBody);
|
||||
return new DecryptHttpInputMessage(inputStream, inputMessage.getHeaders());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
+53
-36
@@ -4,11 +4,12 @@ import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt.ApiEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.NoEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.bean.CryptoInfoBean;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.config.ApiEncryptProperties;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.exception.EncryptBodyFailException;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.util.ApiCryptoUtil;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.WebUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -19,10 +20,16 @@ import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.pig4cloud.pigx.common.api.encrypt.util.ApiCryptoUtil.KEY_EXTRACTORS;
|
||||
|
||||
/**
|
||||
* 响应数据的加密处理<br>
|
||||
@@ -39,47 +46,57 @@ import java.util.Map;
|
||||
@Order(1)
|
||||
@ControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(value = ApiEncryptProperties.PREFIX + ".enable", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnProperty(value = ApiEncryptProperties.PREFIX + ".enable", havingValue = "true")
|
||||
public class ApiEncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
|
||||
|
||||
private final ApiEncryptProperties properties;
|
||||
private final ApiEncryptProperties properties;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter returnType, Class converterType) {
|
||||
return AnnotationUtil.hasAnnotation(returnType.getMethod(), ApiEncrypt.class);
|
||||
}
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Class converterType) {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
|
||||
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
// 非feign调用才进行加密
|
||||
if (StrUtil.isNotBlank(WebUtils.getRequest().getHeader(SecurityConstants.FEIGN_USER_AGENT))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CryptoInfoBean cryptoInfoBean = ApiCryptoUtil.getEncryptInfo(returnType);
|
||||
if (cryptoInfoBean == null) {
|
||||
throw new EncryptBodyFailException();
|
||||
}
|
||||
// 上传文件的请求不加密,因为 element 的组件,需要自己定义HTTP ,不能使用全局 http 加密工具解析
|
||||
if (Arrays.stream(Objects.requireNonNull(methodParameter.getMethod()).getParameterTypes())
|
||||
.anyMatch(type -> MultipartFile.class.isAssignableFrom(type) || InputStream.class.isAssignableFrom(type))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] bodyJsonBytes;
|
||||
try {
|
||||
bodyJsonBytes = objectMapper.writeValueAsBytes(body);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (AnnotationUtil.hasAnnotation(methodParameter.getMethod(), NoEncrypt.class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// body 内容 json key, 默认:data {"data":"base64加密字符串"}
|
||||
String bodyJsonKey = properties.getBodyJsonKey();
|
||||
if (StrUtil.isBlank(bodyJsonKey)) {
|
||||
return ApiCryptoUtil.encryptData(bodyJsonBytes, cryptoInfoBean);
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>(2);
|
||||
data.put(bodyJsonKey, ApiCryptoUtil.encryptData(bodyJsonBytes, cryptoInfoBean));
|
||||
return data;
|
||||
}
|
||||
return properties.getSkipUrl().stream().noneMatch(WebUtils.getRequest().getRequestURI()::contains);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
|
||||
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
String key = KEY_EXTRACTORS.get(properties.getDefaultEncryptType()).apply(properties);
|
||||
CryptoInfoBean cryptoInfoBean = new CryptoInfoBean(properties.getDefaultEncryptType(), key);
|
||||
|
||||
byte[] bodyJsonBytes;
|
||||
try {
|
||||
bodyJsonBytes = objectMapper.writeValueAsBytes(body);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// body 内容 json key, 默认:data {"data":"base64加密字符串"}
|
||||
String bodyJsonKey = properties.getBodyJsonKey();
|
||||
if (StrUtil.isBlank(bodyJsonKey)) {
|
||||
return ApiCryptoUtil.encryptData(bodyJsonBytes, cryptoInfoBean);
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>(2);
|
||||
data.put(bodyJsonKey, ApiCryptoUtil.encryptData(bodyJsonBytes, cryptoInfoBean));
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.api.encrypt.core;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.bean.CryptoInfoBean;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.config.ApiEncryptProperties;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.util.ApiCryptoUtil;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.RepeatBodyRequestWrapper;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.pig4cloud.pigx.common.api.encrypt.util.ApiCryptoUtil.KEY_EXTRACTORS;
|
||||
|
||||
/**
|
||||
* 解密GET查询参数
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2025/01/08
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(value = ApiEncryptProperties.PREFIX + ".enable", havingValue = "true")
|
||||
public class DecryptQueryParametersFilter extends OncePerRequestFilter implements Ordered {
|
||||
|
||||
private final ApiEncryptProperties encryptProperties;
|
||||
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
// 是否支持解密
|
||||
if (!supportDecrypt(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
if (MapUtil.isEmpty(parameterMap)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 将请求流转换为可多次读取的请求流
|
||||
RepeatBodyRequestWrapper requestWrapper = new RepeatBodyRequestWrapper(request);
|
||||
// 构建前端对应解密AES 因子
|
||||
parameterMap.forEach((k, v) -> {
|
||||
String[] values = parameterMap.get(k);
|
||||
if (ArrayUtil.isEmpty(values)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String key = KEY_EXTRACTORS.get(encryptProperties.getDefaultEncryptType()).apply(encryptProperties);
|
||||
CryptoInfoBean cryptoInfoBean = new CryptoInfoBean(encryptProperties.getDefaultEncryptType(), key);
|
||||
// 解密并覆盖原有值
|
||||
String decryptData = ApiCryptoUtil.decryptData(URLUtil.decode(values[0]), cryptoInfoBean);
|
||||
parameterMap.put(k, new String[]{decryptData});
|
||||
});
|
||||
|
||||
requestWrapper.setParameterMap(parameterMap);
|
||||
filterChain.doFilter(requestWrapper, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持 Decrypt 解密 1. 开启了加密 2. 非 feign 请求 3. GET 方法
|
||||
*
|
||||
* @param request 请求
|
||||
* @return boolean
|
||||
*/
|
||||
private boolean supportDecrypt(HttpServletRequest request) {
|
||||
return encryptProperties.isEnable()
|
||||
&& encryptProperties.getSkipUrl().stream().noneMatch(request.getRequestURI()::contains)
|
||||
&& StrUtil.equalsAnyIgnoreCase(request.getMethod(), HttpMethod.GET.name())
|
||||
&& StrUtil.isBlank(request.getHeader(SecurityConstants.FEIGN_USER_AGENT));
|
||||
}
|
||||
}
|
||||
+7
-10
@@ -2,10 +2,9 @@ package com.pig4cloud.pigx.common.api.encrypt.core;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.config.ApiEncryptProperties;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 默认的密钥处理器
|
||||
*
|
||||
@@ -18,14 +17,12 @@ public class DefaultSecretKeyResolver implements ISecretKeyResolver {
|
||||
|
||||
@Override
|
||||
public String getSecretKey(HttpServletRequest request, EncryptType encryptType) {
|
||||
switch (encryptType) {
|
||||
case DES:
|
||||
return properties.getDesKey();
|
||||
case RSA:
|
||||
return properties.getRsaPrivateKey();
|
||||
default:
|
||||
return properties.getAesKey();
|
||||
}
|
||||
return switch (encryptType) {
|
||||
case SM4 -> properties.getSm4Key();
|
||||
case DES -> properties.getDesKey();
|
||||
case RSA -> properties.getRsaPrivateKey();
|
||||
default -> properties.getAesKey();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
package com.pig4cloud.pigx.common.api.encrypt.core;
|
||||
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 解析加密密钥
|
||||
|
||||
+88
-102
@@ -8,9 +8,8 @@ import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.SmUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.decrypt.ApiDecrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.encrypt.ApiEncrypt;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.bean.CryptoInfoBean;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.config.ApiEncryptProperties;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.core.ISecretKeyResolver;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.enums.EncryptType;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.exception.EncryptBodyFailException;
|
||||
@@ -18,15 +17,14 @@ import com.pig4cloud.pigx.common.api.encrypt.exception.EncryptMethodNotFoundExce
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
import com.pig4cloud.pigx.common.core.util.WebUtils;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -40,116 +38,104 @@ import java.nio.charset.StandardCharsets;
|
||||
@UtilityClass
|
||||
public class ApiCryptoUtil {
|
||||
|
||||
/**
|
||||
* 获取方法控制器上的加密注解信息
|
||||
* @param methodParameter 控制器方法
|
||||
* @return 加密注解信息
|
||||
*/
|
||||
@Nullable
|
||||
public static CryptoInfoBean getEncryptInfo(MethodParameter methodParameter) {
|
||||
ApiEncrypt encryptBody = AnnotatedElementUtils.findMergedAnnotation(methodParameter.getMethod(),
|
||||
ApiEncrypt.class);
|
||||
if (encryptBody == null) {
|
||||
return null;
|
||||
}
|
||||
return new CryptoInfoBean(encryptBody.value(), encryptBody.secretKey());
|
||||
}
|
||||
/**
|
||||
* 密钥提取器
|
||||
*/
|
||||
public static final Map<EncryptType, Function<ApiEncryptProperties, String>> KEY_EXTRACTORS = Map.of(
|
||||
EncryptType.AES, ApiEncryptProperties::getAesKey,
|
||||
EncryptType.RSA, ApiEncryptProperties::getRsaPrivateKey,
|
||||
EncryptType.DES, ApiEncryptProperties::getDesKey,
|
||||
EncryptType.SM4, ApiEncryptProperties::getSm4Key
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取方法控制器上的解密注解信息
|
||||
* @param methodParameter 控制器方法
|
||||
* @return 加密注解信息
|
||||
*/
|
||||
@Nullable
|
||||
public static CryptoInfoBean getDecryptInfo(MethodParameter methodParameter) {
|
||||
ApiDecrypt decryptBody = AnnotatedElementUtils.findMergedAnnotation(methodParameter.getMethod(),
|
||||
ApiDecrypt.class);
|
||||
if (decryptBody == null) {
|
||||
return null;
|
||||
}
|
||||
return new CryptoInfoBean(decryptBody.value(), decryptBody.secretKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择加密方式并进行加密
|
||||
* @param jsonData json 数据
|
||||
* @param infoBean 加密信息
|
||||
* @return 加密结果
|
||||
*/
|
||||
public static String encryptData(byte[] jsonData, CryptoInfoBean infoBean) {
|
||||
EncryptType type = infoBean.getType();
|
||||
if (type == null) {
|
||||
throw new EncryptMethodNotFoundException();
|
||||
}
|
||||
/**
|
||||
* 选择加密方式并进行加密
|
||||
*
|
||||
* @param jsonData json 数据
|
||||
* @param infoBean 加密信息
|
||||
* @return 加密结果
|
||||
*/
|
||||
public static String encryptData(byte[] jsonData, CryptoInfoBean infoBean) {
|
||||
EncryptType type = infoBean.getType();
|
||||
if (type == null) {
|
||||
throw new EncryptMethodNotFoundException();
|
||||
}
|
||||
|
||||
String secretKey = infoBean.getSecretKey();
|
||||
if (StrUtil.isBlank(secretKey)) {
|
||||
secretKey = SpringContextHolder.getBean(ISecretKeyResolver.class).getSecretKey(WebUtils.getRequest(), type);
|
||||
}
|
||||
Assert.hasText(secretKey, type + " key is not configured (未配置" + type + ")");
|
||||
String secretKey = infoBean.getSecretKey();
|
||||
if (StrUtil.isBlank(secretKey)) {
|
||||
secretKey = SpringContextHolder.getBean(ISecretKeyResolver.class).getSecretKey(WebUtils.getRequest(), type);
|
||||
}
|
||||
Assert.hasText(secretKey, type + " key is not configured (未配置" + type + ")");
|
||||
|
||||
if (type == EncryptType.DES) {
|
||||
if (type == EncryptType.DES) {
|
||||
|
||||
return SecureUtil.des(secretKey.getBytes(StandardCharsets.UTF_8)).encryptBase64(jsonData);
|
||||
}
|
||||
if (type == EncryptType.AES) {
|
||||
// 构建前端对应解密AES 因子
|
||||
AES aes = new AES(Mode.CFB, Padding.NoPadding, new SecretKeySpec(secretKey.getBytes(), "AES"),
|
||||
new IvParameterSpec(secretKey.getBytes()));
|
||||
return SecureUtil.des(secretKey.getBytes(StandardCharsets.UTF_8)).encryptBase64(jsonData);
|
||||
}
|
||||
if (type == EncryptType.AES) {
|
||||
// 构建前端对应解密AES 因子
|
||||
AES aes = new AES(Mode.CFB, Padding.NoPadding, new SecretKeySpec(secretKey.getBytes(), "AES"),
|
||||
new IvParameterSpec(secretKey.getBytes()));
|
||||
|
||||
return aes.encryptBase64(jsonData);
|
||||
}
|
||||
if (type == EncryptType.RSA) {
|
||||
return SecureUtil.rsa(secretKey.getBytes(StandardCharsets.UTF_8), null).encryptBase64(jsonData,
|
||||
KeyType.PrivateKey);
|
||||
}
|
||||
return aes.encryptBase64(jsonData);
|
||||
}
|
||||
if (type == EncryptType.RSA) {
|
||||
return SecureUtil.rsa(secretKey.getBytes(StandardCharsets.UTF_8), null)
|
||||
.encryptBase64(jsonData, KeyType.PrivateKey);
|
||||
}
|
||||
|
||||
if (type == EncryptType.SM4) {
|
||||
return SmUtil.sm4(HexUtil.decodeHex(secretKey)).encryptHex(jsonData);
|
||||
}
|
||||
throw new EncryptBodyFailException();
|
||||
}
|
||||
if (type == EncryptType.SM4) {
|
||||
return SmUtil.sm4(HexUtil.decodeHex(secretKey)).encryptHex(jsonData);
|
||||
}
|
||||
throw new EncryptBodyFailException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择加密方式并进行解密
|
||||
* @param bodyData byte array
|
||||
* @param infoBean 加密信息
|
||||
* @return 解密结果
|
||||
*/
|
||||
public static byte[] decryptData(byte[] bodyData, CryptoInfoBean infoBean) {
|
||||
EncryptType type = infoBean.getType();
|
||||
if (type == null) {
|
||||
throw new EncryptMethodNotFoundException();
|
||||
}
|
||||
/**
|
||||
* 选择加密方式并进行解密
|
||||
*
|
||||
* @param bodyData byte array
|
||||
* @param infoBean 加密信息
|
||||
* @return 解密结果
|
||||
*/
|
||||
public static byte[] decryptData(byte[] bodyData, CryptoInfoBean infoBean) {
|
||||
EncryptType type = infoBean.getType();
|
||||
if (type == null) {
|
||||
throw new EncryptMethodNotFoundException();
|
||||
}
|
||||
|
||||
String secretKey = infoBean.getSecretKey();
|
||||
if (StrUtil.isBlank(secretKey)) {
|
||||
secretKey = SpringContextHolder.getBean(ISecretKeyResolver.class).getSecretKey(WebUtils.getRequest(), type);
|
||||
}
|
||||
Assert.hasText(secretKey, type + " key is not configured (未配置" + type + ")");
|
||||
String secretKey = infoBean.getSecretKey();
|
||||
if (StrUtil.isBlank(secretKey)) {
|
||||
secretKey = SpringContextHolder.getBean(ISecretKeyResolver.class).getSecretKey(WebUtils.getRequest(), type);
|
||||
}
|
||||
Assert.hasText(secretKey, type + " key is not configured (未配置" + type + ")");
|
||||
|
||||
if (type == EncryptType.AES) {
|
||||
if (type == EncryptType.AES) {
|
||||
|
||||
AES aes = new AES(Mode.CFB, Padding.NoPadding,
|
||||
new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES"),
|
||||
new IvParameterSpec(secretKey.getBytes(StandardCharsets.UTF_8)));
|
||||
return aes.decrypt(StrUtil.str(bodyData, StandardCharsets.UTF_8));
|
||||
AES aes = new AES(Mode.CFB, Padding.NoPadding,
|
||||
new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES"),
|
||||
new IvParameterSpec(secretKey.getBytes(StandardCharsets.UTF_8)));
|
||||
return aes.decrypt(StrUtil.str(bodyData, StandardCharsets.UTF_8));
|
||||
|
||||
}
|
||||
if (type == EncryptType.DES) {
|
||||
return SecureUtil.des(secretKey.getBytes(StandardCharsets.UTF_8)).decrypt(bodyData);
|
||||
}
|
||||
if (type == EncryptType.RSA) {
|
||||
return SecureUtil.rsa(secretKey.getBytes(StandardCharsets.UTF_8), null).decrypt(bodyData,
|
||||
KeyType.PrivateKey);
|
||||
}
|
||||
}
|
||||
if (type == EncryptType.DES) {
|
||||
return SecureUtil.des(secretKey.getBytes(StandardCharsets.UTF_8)).decrypt(bodyData);
|
||||
}
|
||||
if (type == EncryptType.RSA) {
|
||||
return SecureUtil.rsa(secretKey.getBytes(StandardCharsets.UTF_8), null)
|
||||
.decrypt(bodyData, KeyType.PrivateKey);
|
||||
}
|
||||
|
||||
if (type == EncryptType.SM4) {
|
||||
return SmUtil.sm4(HexUtil.decodeHex(secretKey)).decryptStr(StrUtil.str(bodyData, Charset.defaultCharset()))
|
||||
.getBytes();
|
||||
}
|
||||
if (type == EncryptType.SM4) {
|
||||
return SmUtil.sm4(HexUtil.decodeHex(secretKey))
|
||||
.decryptStr(StrUtil.str(bodyData, Charset.defaultCharset()))
|
||||
.getBytes();
|
||||
}
|
||||
|
||||
throw new EncryptMethodNotFoundException();
|
||||
}
|
||||
throw new EncryptMethodNotFoundException();
|
||||
}
|
||||
|
||||
public static String decryptData(String bodyData, CryptoInfoBean infoBean) {
|
||||
return StrUtil.str(decryptData(bodyData.getBytes(StandardCharsets.UTF_8), infoBean), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -26,6 +26,9 @@ public class PigxFeignInnerRequestInterceptor implements RequestInterceptor, Ord
|
||||
if (noToken != null) {
|
||||
template.header(SecurityConstants.FROM, SecurityConstants.FROM_IN);
|
||||
}
|
||||
|
||||
// 从内部调用标识
|
||||
template.header(SecurityConstants.FEIGN_USER_AGENT, SecurityConstants.FROM_IN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -126,6 +126,11 @@
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-gray</artifactId>
|
||||
</dependency>
|
||||
<!-- API加解密 -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-encrypt-api</artifactId>
|
||||
</dependency>
|
||||
<!--mybatis-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
|
||||
+3
@@ -26,6 +26,7 @@ import com.pig4cloud.pigx.admin.api.dto.SysFileGroupDTO;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysFile;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysFileGroup;
|
||||
import com.pig4cloud.pigx.admin.service.SysFileService;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.NoEncrypt;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pigx.common.security.annotation.Inner;
|
||||
@@ -116,6 +117,7 @@ public class SysFileController {
|
||||
* @return
|
||||
*/
|
||||
@Inner(false)
|
||||
@NoEncrypt
|
||||
@GetMapping("/oss/file")
|
||||
public void file(String fileName, HttpServletResponse response) {
|
||||
sysFileService.getFile(fileName, response);
|
||||
@@ -127,6 +129,7 @@ public class SysFileController {
|
||||
* @param response 本地文件
|
||||
*/
|
||||
@SneakyThrows
|
||||
@NoEncrypt
|
||||
@GetMapping("/local/file/{fileName}")
|
||||
public void localFile(@PathVariable String fileName, HttpServletResponse response) {
|
||||
ClassPathResource resource = new ClassPathResource("file/" + fileName);
|
||||
|
||||
@@ -150,6 +150,11 @@
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-log</artifactId>
|
||||
</dependency>
|
||||
<!--必备: API加解密 -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-encrypt-api</artifactId>
|
||||
</dependency>
|
||||
<!--灰度支持-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
|
||||
+2
@@ -20,6 +20,7 @@ package com.pig4cloud.pigx.codegen.controller;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.codegen.service.GeneratorService;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.NoEncrypt;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.idempotent.annotation.Idempotent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -56,6 +57,7 @@ public class GeneratorController {
|
||||
* @param response 流输出对象
|
||||
*/
|
||||
@SneakyThrows
|
||||
@NoEncrypt
|
||||
@GetMapping("/download")
|
||||
@Idempotent(key = "#tableIds", expireTime = 10, info = "正在生成代码,请勿重复操作")
|
||||
public void download(String tableIds, HttpServletResponse response) {
|
||||
|
||||
@@ -81,6 +81,11 @@
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-sensitive</artifactId>
|
||||
</dependency>
|
||||
<!--必备: API加解密 -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-encrypt-api</artifactId>
|
||||
</dependency>
|
||||
<!-- sentinel-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
|
||||
+3
-1
@@ -1,6 +1,7 @@
|
||||
package com.pig4cloud.pigx.mp.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.NoEncrypt;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
|
||||
import com.pig4cloud.pigx.common.security.annotation.Inner;
|
||||
@@ -24,7 +25,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
@Inner(value = false)
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/{appId}/portal")
|
||||
@RequestMapping("/wx-portal/{appId}")
|
||||
public class WxPortalController {
|
||||
|
||||
/**
|
||||
@@ -74,6 +75,7 @@ public class WxPortalController {
|
||||
* @param nonce 随机数
|
||||
* @return
|
||||
*/
|
||||
@NoEncrypt
|
||||
@XssCleanIgnore
|
||||
@PostMapping(produces = "application/xml; charset=UTF-8")
|
||||
public String post(@PathVariable("appId") String appId, @RequestBody String requestBody,
|
||||
|
||||
@@ -78,6 +78,11 @@
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-swagger</artifactId>
|
||||
</dependency>
|
||||
<!--必备: API加解密 -->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-encrypt-api</artifactId>
|
||||
</dependency>
|
||||
<!--安全模块-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
|
||||
+4
@@ -25,6 +25,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ijpay.alipay.AliPayApi;
|
||||
import com.ijpay.core.kit.HttpKit;
|
||||
import com.ijpay.core.kit.WxPayKit;
|
||||
import com.pig4cloud.pigx.common.api.encrypt.annotation.NoEncrypt;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.log.annotation.SysLog;
|
||||
@@ -146,6 +147,7 @@ public class PayNotifyRecordController {
|
||||
@Inner(false)
|
||||
@SneakyThrows
|
||||
@XssCleanIgnore
|
||||
@NoEncrypt
|
||||
@PostMapping("/ali/callbak")
|
||||
@Operation(summary = "支付宝渠道异步回调", description = "支付宝渠道异步回调")
|
||||
public void aliCallbak(HttpServletRequest request, HttpServletResponse response) {
|
||||
@@ -161,6 +163,7 @@ public class PayNotifyRecordController {
|
||||
*/
|
||||
@Inner(false)
|
||||
@SneakyThrows
|
||||
@NoEncrypt
|
||||
@XssCleanIgnore
|
||||
@PostMapping("/wx/callbak")
|
||||
@Operation(summary = "微信渠道支付回调", description = "微信渠道支付回调")
|
||||
@@ -178,6 +181,7 @@ public class PayNotifyRecordController {
|
||||
*/
|
||||
@Inner(false)
|
||||
@SneakyThrows
|
||||
@NoEncrypt
|
||||
@XssCleanIgnore
|
||||
@PostMapping("/merge/callbak")
|
||||
@Operation(summary = "聚合渠道异步回调", description = "聚合渠道异步回调")
|
||||
|
||||
+22
-17
@@ -12,25 +12,30 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public enum RefundNameEnum {
|
||||
|
||||
/**
|
||||
* 支付宝wap支付
|
||||
*/
|
||||
ALIPAY("ALIPAY_REFUND", "支付宝退款"),
|
||||
/**
|
||||
* 支付宝wap支付
|
||||
*/
|
||||
ALIPAY("ALIPAY_REFUND", "支付宝退款"),
|
||||
|
||||
/**
|
||||
* 微信H5支付
|
||||
*/
|
||||
WEIXIN("WEIXIN_REFUND", "微信退款");
|
||||
/**
|
||||
* 微信H5支付
|
||||
*/
|
||||
WEIXIN("WEIXIN_REFUND", "微信退款"),
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
@Getter
|
||||
private String name;
|
||||
/**
|
||||
* 合并支付
|
||||
*/
|
||||
MERGE_PAY("MERGE_PAY_REFUND", "合并支付退款");
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
@Getter
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user