mirror of
https://gitee.com/incloudcode/yexuejc-springboot.git
synced 2025-07-14 07:47:28 +08:00
新增API请求安全解决方案,加密机制
This commit is contained in:
parent
622ff4455d
commit
b5e5c423f1
114
PARAMS_RSA_DECRYPT_ENCRYPT.md
Normal file
114
PARAMS_RSA_DECRYPT_ENCRYPT.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# 加密解密功能介绍
|
||||||
|
|
||||||
|
|
||||||
|
#### 客户端解密(服务器端加密出参data)
|
||||||
|
|
||||||
|
>API返回值的data部分会用私钥加密<br/>
|
||||||
|
>1.先使用公钥解密得到解密字符串 <br/>
|
||||||
|
>2.1返回data为Map类型:先转成Map,然后ASCII码排序得到->result1=xxxxx&result2=xxxxx,再MD5的到sign <br/>
|
||||||
|
>2.2返回data为List类型:先转成List,然后JSON化,再MD5的到sign <br/>
|
||||||
|
>2.3返回data为字符串或基础类型:先转成String,再MD5的到sign <br/>
|
||||||
|
|
||||||
|
|
||||||
|
>客户端使用公钥解密
|
||||||
|
|
||||||
|
正常api返回值
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": "S",
|
||||||
|
"data":"xxxxx",
|
||||||
|
"msg": [
|
||||||
|
"操作成功"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
加密后api返回值
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": "S",
|
||||||
|
"data":"LZ6ylSuKj36Pd5_TGfHmmJUifKQq_BLLD_CRyw17Z6y9jfcjpK.....",
|
||||||
|
"sign":"dee2f5af75b50f99b16726784230afeb",
|
||||||
|
"msg": [
|
||||||
|
"请求成功"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
解析代码
|
||||||
|
```
|
||||||
|
//map
|
||||||
|
String mapData = "cEPfMp7-rim76XFdbNDSIzDRXyHySpz0VOZP6HC8U-JPl-ZxNZKp6ethEQWWITcuUPzIhp4fHiGKNsHA7F6OxCxibpMLj5-ZsgJJvcczw8Liens5kYgciRF1UziR3LFy6vybN9H1CJnqXaddbl3t_41P-_1l5Ev7YYa8woWp7ulaRPeTCDjohEpmx2Vi6aPSrm3hjjmitkD9gb0O6vFDNnclyNhFepKV3oh93tNv50sEQQ_QSBUXSHUtCnhTiBX8VsRX3h58F2tie7bG8VSk-6KFuXI07OiqFZSNpcwDOuq-GfMlEfPL3pX-gYhoOORPNClRlQHwyfHXBJly3gRtNVpVksHWQjr1xutWgYfwRjQPHBHNZwfx4E0XoCTuz9qH1CzFmmz68i63GzCM286zJ-J26MkiTDO1zH4jhglo38tnzz9HLeDcbbCuJg1jzkvpFiWamM-6odWhtCg65BS1tJJVWg023kWygZMu5Ebrm5WBbbatN87_K5zn211tFpKwRq2oVjO_AfJRY90WlQGEIHnzZNz_cf8mAjlmilHDuNdjYlj3axTUqLfgLDVaIkasREnjMI7oe8oAtG2ju2aq-xSAQZ_U-7-rsyBpoy0jnwRmlyUxhXgIX0zrfBQNXEjzPtg-iJ14R5qz1iOAJL7NtQQeuYngGTj6msDlKGEd_MQTLAFDbpiVPwWX00jLT1Ll3_zhivpPCUAmC8Yz58khkqrqi4FdIxJTDkxd0PFOBH8DYicF7ls4UdOHT24mAKDwUF_TfZ32oiiKSzCD9MJB8GEXjzx7tDFok-HsdOjI6ZnSUJCOTj3wne2E6_a8Gq2_vp5CWyW12wthJbH79aa7JVfy5cx-cZmNid7oCe54KYclz1tdUgLPCQ1ajsEevbRJ_NBkTmY2wAmUpHODeocDaYt_2AwAU2DLiv2uZuaVszNSUy593Zrzxq5AaY-oWbEeD24SyEWJObJtz5knYzr4NxjZShcjx9ezwiwkRZMtLZpA_cCPFAK1nOrN8zHCOZquS17CCSLDySLvGbxNqYeBa_lGSq8cQuQo8yybd1WkuLKUjUiJecmH2XcZNTPCtdRe0eLlRtk5928AQGsQugwSig";
|
||||||
|
String sign = StrUtil.toMD5(
|
||||||
|
StrUtil.getSignContent(
|
||||||
|
JsonUtil.json2Obj(mapData, Map.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
//list
|
||||||
|
String listData = "Sf_FO8YC9EUNTeM0n9EVuDzwvLz3DcxOuG4-5UZ9486lLHAx7IOuAhPgVHpQGsQiqJ7Y3fTaWFr6rRFPL12rVg";
|
||||||
|
String sign = StrUtil.toMD5(
|
||||||
|
JsonUtil.obj2Json(
|
||||||
|
JsonUtil.json2Obj(listData, List.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
//其他
|
||||||
|
String strData = "K9Zyg82WDvIApFmXTxPwjQw_VA041jfBcxMIP6jpMM6xWe1XajGf3__7DqSLrS9MwCra9cYkidcjVJAKZn9cmQ";
|
||||||
|
strData = RSA.publicDecrypt(strData, RSA.getPublicKey(properties.getPublicKey()));
|
||||||
|
String sign =StrUtil.toMD5(JsonUtil.json2Obj(strData, String.class);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### 客户端加密 (服务器端解密入参data)
|
||||||
|
|
||||||
|
>1.客户端先使用公钥加密,参数为Map类型 <br/>
|
||||||
|
>然后ASCII码排序得到新的参数->result1=xxxxx&result2=xxxxx,再MD5的到sign <br/>
|
||||||
|
>加密新的参数得到data(也可以对原map JSON化后加密)
|
||||||
|
>
|
||||||
|
>2.API接收到json参数,解析为ParamsPO <br/>
|
||||||
|
>ParamsPO.data部分进行解密操作,解密后得到原始参数(result1=xxxxx&result2=xxxxx)做MD5校验
|
||||||
|
|
||||||
|
原始参数
|
||||||
|
```
|
||||||
|
Map map = new HashMap();
|
||||||
|
map.put("page", 5);
|
||||||
|
map.put("size", 16);
|
||||||
|
map.put("content", "定制榻榻米垫竹编客厅茶几垫卧室地毯竹地毯飘窗垫日式榻榻米地毯");
|
||||||
|
|
||||||
|
//对参数ASCII码排序
|
||||||
|
String data = StrUtil.getSignContent(map);
|
||||||
|
//封装请求参数
|
||||||
|
ParamsPO params = new ParamsPO();
|
||||||
|
params.setSign(StrUtil.toMD5(datas));
|
||||||
|
params.setData(RSA.publicEncrypt(datas, RSA.getPublicKey(properties.getPublicKey())));
|
||||||
|
```
|
||||||
|
|
||||||
|
加密后的请求参数
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data":"pe0V05nr5bfUp7c/JL1b/b6qJHipA5Qx8vM8BRryu3k=",
|
||||||
|
"sign":"dee2f5af75b50f99b16726784230afeb"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### 配置
|
||||||
|
1. 服务器配置私钥
|
||||||
|
```
|
||||||
|
yexuejc.http.encrypt.private-key=私钥
|
||||||
|
yexuejc.http.encrypt.encrypt=true //加密:默认false
|
||||||
|
yexuejc.http.encrypt.decrypt=true //解密:默认false
|
||||||
|
```
|
||||||
|
|
||||||
|
2.客户端请求
|
||||||
|
```
|
||||||
|
request:POST
|
||||||
|
header:
|
||||||
|
X-User-Agent:token
|
||||||
|
Content-Type:application/json
|
||||||
|
body:
|
||||||
|
{
|
||||||
|
"datas":"pe0V05nr5bfUp7c/JL1b/b6qJHipA5Qx8vM8BRryu3k=",
|
||||||
|
"sign":"123456789"
|
||||||
|
}
|
||||||
|
```
|
@ -4,8 +4,8 @@
|
|||||||
#### 项目介绍
|
#### 项目介绍
|
||||||
基于springboot maven 封装可继承基础工程
|
基于springboot maven 封装可继承基础工程
|
||||||
|
|
||||||
内含parent和base工程可分开使用
|
内含parent和base工程可分开使用<br/>
|
||||||
parent:版本封装
|
parent:版本封装<br/>
|
||||||
base:功能封装
|
base:功能封装
|
||||||
|
|
||||||
#### 引用
|
#### 引用
|
||||||
@ -95,4 +95,7 @@ pom.xml
|
|||||||
|
|
||||||
#### 版本更新
|
#### 版本更新
|
||||||
|
|
||||||
[更新记录](UPDATE.md)
|
[更新记录](UPDATE.md)
|
||||||
|
<br/>
|
||||||
|
[1.0.5新增 针对API请求安全解决方案](PARAMS_RSA_DECRYPT_ENCRYPT.md)<br/>
|
||||||
|
[1.0.5新增 加密功能](PARAMS_RSA_DECRYPT_ENCRYPT.md)
|
11
UPDATE.md
11
UPDATE.md
@ -1,7 +1,16 @@
|
|||||||
uselaw-base 更新内容
|
uselaw-base 更新内容
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
#### version :1.0.4
|
#### version :1.0.5
|
||||||
|
**time:** 2018-5-4 09:54:18<br/>
|
||||||
|
**branch:** master <br/>
|
||||||
|
**update:** <br/>
|
||||||
|
> [使用加密解密](PARAMS_RSA_DECRYPT_ENCRYPT.md)
|
||||||
|
>
|
||||||
|
>1.增加json入参解密、出参加密
|
||||||
|
#
|
||||||
|
|
||||||
|
##### version :1.0.4
|
||||||
**time:** 2018-5-4 09:54:18<br/>
|
**time:** 2018-5-4 09:54:18<br/>
|
||||||
**branch:** master <br/>
|
**branch:** master <br/>
|
||||||
**update:** <br/>
|
**update:** <br/>
|
||||||
|
4
pom.xml
4
pom.xml
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.yexuejc.springboot</groupId>
|
<groupId>com.yexuejc.springboot</groupId>
|
||||||
<artifactId>yexuejc-springboot-parent</artifactId>
|
<artifactId>yexuejc-springboot-parent</artifactId>
|
||||||
<version>1.0.4</version>
|
<version>1.0.5</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>${project.artifactId}</name>
|
<name>${project.artifactId}</name>
|
||||||
@ -35,7 +35,7 @@
|
|||||||
<mybatis-spring-boot-starter.version>1.2.0</mybatis-spring-boot-starter.version>
|
<mybatis-spring-boot-starter.version>1.2.0</mybatis-spring-boot-starter.version>
|
||||||
<mybatis.version>3.4.2</mybatis.version>
|
<mybatis.version>3.4.2</mybatis.version>
|
||||||
|
|
||||||
<yexuejc.base.version>1.0.0</yexuejc.base.version>
|
<yexuejc.base.version>1.1.0</yexuejc.base.version>
|
||||||
<jjwt.version>0.7.0</jjwt.version>
|
<jjwt.version>0.7.0</jjwt.version>
|
||||||
<fastjson.version>1.1.46</fastjson.version>
|
<fastjson.version>1.1.46</fastjson.version>
|
||||||
<commons-codec.version>1.10</commons-codec.version>
|
<commons-codec.version>1.10</commons-codec.version>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.yexuejc.springboot</groupId>
|
<groupId>com.yexuejc.springboot</groupId>
|
||||||
<artifactId>yexuejc-springboot-parent</artifactId>
|
<artifactId>yexuejc-springboot-parent</artifactId>
|
||||||
<version>1.0.4</version>
|
<version>1.0.5</version>
|
||||||
<!-- 本地打包:使用相对关联路径 -->
|
<!-- 本地打包:使用相对关联路径 -->
|
||||||
<!--<relativePath>../../yexuejc</relativePath>-->
|
<!--<relativePath>../../yexuejc</relativePath>-->
|
||||||
</parent>
|
</parent>
|
||||||
@ -22,9 +22,13 @@
|
|||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!--基础包-->
|
<!--基础包-->
|
||||||
|
<!--<dependency>-->
|
||||||
|
<!--<groupId>com.yexuejc.base</groupId>-->
|
||||||
|
<!--<artifactId>yexuejc-base</artifactId>-->
|
||||||
|
<!--</dependency>-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.yexuejc</groupId>
|
<groupId>com.github.yexuejc</groupId>
|
||||||
<artifactId>yexuejc-base</artifactId>
|
<artifactId>yexuejc-base</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 使用springMVC -->
|
<!-- 使用springMVC -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -74,6 +78,14 @@
|
|||||||
<artifactId>jedis</artifactId>
|
<artifactId>jedis</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.yexuejc.base.http.Resps;
|
import com.yexuejc.base.http.Resps;
|
||||||
import com.yexuejc.base.util.DateUtil;
|
import com.yexuejc.base.util.DateUtil;
|
||||||
|
import com.yexuejc.base.util.StrUtil;
|
||||||
import com.yexuejc.springboot.base.filter.ValidationFilter;
|
import com.yexuejc.springboot.base.filter.ValidationFilter;
|
||||||
import com.yexuejc.springboot.base.filter.ValidationFilterProperties;
|
import com.yexuejc.springboot.base.filter.ValidationFilterProperties;
|
||||||
import com.yexuejc.springboot.base.interceptor.LogInterceptor;
|
import com.yexuejc.springboot.base.interceptor.LogInterceptor;
|
||||||
@ -19,14 +20,20 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebMvc相关配置
|
* WebMvc相关配置
|
||||||
*
|
*
|
||||||
@ -45,6 +52,28 @@ public class WebAutoConfiguration extends WebMvcConfigurerAdapter {
|
|||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************编码部分*****************************************************/
|
||||||
|
@Bean
|
||||||
|
public HttpMessageConverter<String> responseBodyConverter() {
|
||||||
|
StringHttpMessageConverter converter = new StringHttpMessageConverter(
|
||||||
|
Charset.forName("UTF-8"));
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureMessageConverters(
|
||||||
|
List<HttpMessageConverter<?>> converters) {
|
||||||
|
super.configureMessageConverters(converters);
|
||||||
|
converters.add(responseBodyConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureContentNegotiation(
|
||||||
|
ContentNegotiationConfigurer configurer) {
|
||||||
|
configurer.favorPathExtension(false);
|
||||||
|
}
|
||||||
|
/******************************************编码部分*****************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加拦截器
|
* 添加拦截器
|
||||||
*/
|
*/
|
||||||
@ -108,7 +137,7 @@ public class WebAutoConfiguration extends WebMvcConfigurerAdapter {
|
|||||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
public Resps<Object> jsonErrorHandler(Throwable e) {
|
public Resps<Object> jsonErrorHandler(Throwable e) {
|
||||||
LogUtil.exceptionLogger.error(e.getMessage(), e);
|
LogUtil.exceptionLogger.error(e.getMessage(), e);
|
||||||
return Resps.error(ERROR_MSG);
|
return Resps.error(StrUtil.setStr(e.getMessage(), ERROR_MSG));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.yexuejc.springboot.base.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关异常
|
||||||
|
*
|
||||||
|
* @author: maxf
|
||||||
|
* @date: 2018/5/12 18:36
|
||||||
|
*/
|
||||||
|
public class GatewayException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -2390195902982826130L;
|
||||||
|
/**
|
||||||
|
* 错误码
|
||||||
|
*/
|
||||||
|
private String code = "E";
|
||||||
|
|
||||||
|
public GatewayException() {
|
||||||
|
super("数据错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
public GatewayException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
package com.yexuejc.springboot.base.filter;
|
||||||
|
|
||||||
|
import com.yexuejc.base.pojo.ParamsPO;
|
||||||
|
import com.yexuejc.base.util.JsonUtil;
|
||||||
|
import com.yexuejc.base.util.StrUtil;
|
||||||
|
import com.yexuejc.springboot.base.exception.GatewayException;
|
||||||
|
import com.yexuejc.springboot.base.util.LogUtil;
|
||||||
|
import com.yexuejc.springboot.base.util.RSA;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpInputMessage;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求数据解密
|
||||||
|
*
|
||||||
|
* @version 1.0.5
|
||||||
|
* @ClassName: ParamsRequestBodyAdvice
|
||||||
|
* @Description:
|
||||||
|
* @author: maxf
|
||||||
|
* @date: 2018/5/12 22:49
|
||||||
|
*/
|
||||||
|
@ControllerAdvice(basePackages = "com")
|
||||||
|
@EnableConfigurationProperties(RsaProperties.class)
|
||||||
|
public class ParamsRequestBodyAdvice implements RequestBodyAdvice {
|
||||||
|
|
||||||
|
private final RsaProperties properties;
|
||||||
|
|
||||||
|
public ParamsRequestBodyAdvice(RsaProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
|
||||||
|
if (properties.isDecrypt()) {
|
||||||
|
ParamsPO paramsPO = JsonUtil.json2Obj(IOUtils.toString(inputMessage.getBody(), "UTF-8"), ParamsPO.class);
|
||||||
|
//RSA解密
|
||||||
|
try {
|
||||||
|
long t = System.currentTimeMillis();
|
||||||
|
String data = new String(
|
||||||
|
RSA.privateDecrypt(
|
||||||
|
paramsPO.getData(),
|
||||||
|
RSA.getPrivateKey(properties.getPrivateKey())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
//md5 校验
|
||||||
|
if (!StrUtil.toMD5(data).equals(paramsPO.getSign())) {
|
||||||
|
LogUtil.accessLogger.error("sign错误,请求内容:{}", JsonUtil.obj2Json(paramsPO));
|
||||||
|
throw new GatewayException("sign错误");
|
||||||
|
}
|
||||||
|
InputStream body = IOUtils.toInputStream(JsonUtil.obj2Json(StrUtil.parseUrlencoded(data)), "UTF-8");
|
||||||
|
LogUtil.accessLogger.info("解密耗时:{}", System.currentTimeMillis() - t);
|
||||||
|
return new MyHttpInputMessage(inputMessage.getHeaders(), body);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new GatewayException("data错误");
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new GatewayException("data错误");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new GatewayException("data错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inputMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyHttpInputMessage implements HttpInputMessage {
|
||||||
|
private HttpHeaders headers;
|
||||||
|
|
||||||
|
private InputStream body;
|
||||||
|
|
||||||
|
public MyHttpInputMessage(HttpHeaders headers, InputStream body) throws Exception {
|
||||||
|
this.headers = headers;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getBody() throws IOException {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package com.yexuejc.springboot.base.filter;
|
||||||
|
|
||||||
|
import com.yexuejc.base.http.Resps;
|
||||||
|
import com.yexuejc.base.util.JsonUtil;
|
||||||
|
import com.yexuejc.base.util.StrUtil;
|
||||||
|
import com.yexuejc.springboot.base.util.LogUtil;
|
||||||
|
import com.yexuejc.springboot.base.util.RSA;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.server.ServerHttpRequest;
|
||||||
|
import org.springframework.http.server.ServerHttpResponse;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回数据加密
|
||||||
|
*
|
||||||
|
* @version 1.0.5
|
||||||
|
* @ClassName: ParamsResponseBodyAdvice
|
||||||
|
* @Description:
|
||||||
|
* @author: maxf
|
||||||
|
* @date: 2018/5/12 22:50
|
||||||
|
*/
|
||||||
|
@ControllerAdvice(basePackages = "com")
|
||||||
|
@EnableConfigurationProperties(RsaProperties.class)
|
||||||
|
public class ParamsResponseBodyAdvice implements ResponseBodyAdvice {
|
||||||
|
|
||||||
|
private final RsaProperties properties;
|
||||||
|
|
||||||
|
public ParamsResponseBodyAdvice(RsaProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(MethodParameter returnType, Class converterType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||||
|
if (returnType.getMethod().isAnnotationPresent(SerializedField.class)) {
|
||||||
|
//获取注解配置的包含和去除字段
|
||||||
|
SerializedField serializedField = returnType.getMethodAnnotation(SerializedField.class);
|
||||||
|
//是否加密
|
||||||
|
properties.setEncrypt(serializedField.encode());
|
||||||
|
}
|
||||||
|
if (properties.isEncrypt()) {
|
||||||
|
if (body instanceof Resps) {
|
||||||
|
long t = System.currentTimeMillis();
|
||||||
|
Resps resps = (Resps) body;
|
||||||
|
if (StrUtil.isNotEmpty(resps.getData())) {
|
||||||
|
String data = "";
|
||||||
|
if (resps.getData() instanceof Map) {
|
||||||
|
data = StrUtil.getSignContent((Map) resps.getData());
|
||||||
|
} else if (resps.getData() instanceof List) {
|
||||||
|
data = JsonUtil.obj2Json(resps.getData());
|
||||||
|
} else if (resps.getData() instanceof String) {
|
||||||
|
data = (String) resps.getData();
|
||||||
|
} else {
|
||||||
|
data = JsonUtil.obj2Json(resps.getData());
|
||||||
|
}
|
||||||
|
resps.setSign(StrUtil.toMD5(data));
|
||||||
|
try {
|
||||||
|
resps.setData(
|
||||||
|
RSA.privateEncrypt(JsonUtil.obj2Json(resps.getData()), RSA.getPrivateKey(properties.getPrivateKey()))
|
||||||
|
);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
LogUtil.accessLogger.error("出参加密错误,进行明文出参{}。\n异常信息:{}", JsonUtil.obj2Json(resps), e.getMessage());
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
LogUtil.accessLogger.error("出参加密错误,进行明文出参{}。\n异常信息:{}", JsonUtil.obj2Json(resps), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogUtil.accessLogger.info("加密耗时:{}", System.currentTimeMillis() - t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.yexuejc.springboot.base.filter;
|
||||||
|
|
||||||
|
import com.yexuejc.base.util.JsonUtil;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密,解密相关配置
|
||||||
|
*
|
||||||
|
* @version 1.0.5
|
||||||
|
* @author: maxf
|
||||||
|
* @date: 2018/5/12 18:14
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "yexuejc.http.encrypt")
|
||||||
|
public class RsaProperties {
|
||||||
|
/**
|
||||||
|
* 私钥
|
||||||
|
*/
|
||||||
|
private String privateKey = "";
|
||||||
|
/**
|
||||||
|
* 公钥
|
||||||
|
*/
|
||||||
|
private String publicKey = "";
|
||||||
|
/**
|
||||||
|
* 是否解密
|
||||||
|
*/
|
||||||
|
private boolean decrypt = false;
|
||||||
|
/**
|
||||||
|
* 是否加密
|
||||||
|
*/
|
||||||
|
private boolean encrypt = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JsonUtil.obj2Json(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDecrypt() {
|
||||||
|
return decrypt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecrypt(boolean decrypt) {
|
||||||
|
this.decrypt = decrypt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEncrypt() {
|
||||||
|
return encrypt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncrypt(boolean encrypt) {
|
||||||
|
this.encrypt = encrypt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateKey() {
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(String privateKey) {
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicKey() {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicKey(String publicKey) {
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.yexuejc.springboot.base.filter;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.Mapping;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定出参加密
|
||||||
|
*
|
||||||
|
* @version 1.0.5
|
||||||
|
* @ClassName: SerializedField
|
||||||
|
* @Description:
|
||||||
|
* @author: maxf
|
||||||
|
* @date: 2018/5/12 22:51
|
||||||
|
*/
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Mapping
|
||||||
|
public @interface SerializedField {
|
||||||
|
/**
|
||||||
|
* 是否加密
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean encode() default true;
|
||||||
|
}
|
@ -13,8 +13,16 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤器配置
|
||||||
|
*
|
||||||
|
* @version 1.0.5
|
||||||
|
* @ClassName: ValidationFilter
|
||||||
|
* @Description:
|
||||||
|
* @author: maxf
|
||||||
|
* @date: 2018/5/14 17:49
|
||||||
|
*/
|
||||||
public class ValidationFilter implements Filter {
|
public class ValidationFilter implements Filter {
|
||||||
|
|
||||||
ValidationFilterProperties properties;
|
ValidationFilterProperties properties;
|
||||||
|
|
||||||
public ValidationFilter(ValidationFilterProperties properties) {
|
public ValidationFilter(ValidationFilterProperties properties) {
|
||||||
@ -26,6 +34,24 @@ public class ValidationFilter implements Filter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求安全
|
||||||
|
* 解决方案:
|
||||||
|
* header头添加X-User-Agent:授权
|
||||||
|
* 入参取时间戳,先MD5,然后进行RSA非对称加密
|
||||||
|
* 按顺序解密:
|
||||||
|
* 1.防泄漏:RSA解密
|
||||||
|
* 2.防篡改:md5参数校验
|
||||||
|
* 3.防重复请求:时间戳(一分钟时间差)
|
||||||
|
* 4.防XSS攻击:header头添加X-User-Agent授权
|
||||||
|
* 5.登录角色token授权
|
||||||
|
*
|
||||||
|
* @param servletRequest
|
||||||
|
* @param servletResponse
|
||||||
|
* @param filterChain
|
||||||
|
* @throws IOException
|
||||||
|
* @throws ServletException
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
@ -46,6 +72,9 @@ public class ValidationFilter implements Filter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* stop1:header头 校验
|
||||||
|
*/
|
||||||
String xuserAgent = request.getHeader(RespsConsts.HEADER_X_USER_AGENT);
|
String xuserAgent = request.getHeader(RespsConsts.HEADER_X_USER_AGENT);
|
||||||
if (xuserAgent == null || xuserAgent.length() == 0) {
|
if (xuserAgent == null || xuserAgent.length() == 0) {
|
||||||
// 写日志
|
// 写日志
|
||||||
@ -68,4 +97,6 @@ public class ValidationFilter implements Filter {
|
|||||||
public void destroy() {
|
public void destroy() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
package com.yexuejc.springboot.base.pojo;
|
|
||||||
|
|
||||||
import com.yexuejc.base.pojo.BaseVO;
|
|
||||||
import com.yexuejc.base.util.JsonUtil;
|
|
||||||
|
|
||||||
import javax.validation.constraints.Min;
|
|
||||||
import javax.validation.constraints.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页 VO
|
|
||||||
*
|
|
||||||
* @author: maxf
|
|
||||||
* @date: 2018/3/28 14:23
|
|
||||||
*/
|
|
||||||
public class PagerVO extends BaseVO {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 3490440129554644587L;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Min(1L)
|
|
||||||
private Integer page = 1;
|
|
||||||
@NotNull
|
|
||||||
@Min(1L)
|
|
||||||
private Integer size = 20;
|
|
||||||
|
|
||||||
public int getOffset() {
|
|
||||||
return (this.page - 1) * this.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return JsonUtil.obj2Json(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getPage() {
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPage(Integer page) {
|
|
||||||
this.page = page;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSize(Integer size) {
|
|
||||||
this.size = size;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package com.yexuejc.springboot.base.util;
|
|||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网络工具类
|
* 网络工具类
|
||||||
@ -20,7 +21,6 @@ public class NetUtil {
|
|||||||
public final static String getIp(HttpServletRequest request) throws IOException {
|
public final static String getIp(HttpServletRequest request) throws IOException {
|
||||||
// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
|
// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
|
||||||
String ip = request.getHeader("X-Forwarded-For");
|
String ip = request.getHeader("X-Forwarded-For");
|
||||||
|
|
||||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
ip = request.getHeader("Proxy-Client-IP");
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
@ -0,0 +1,177 @@
|
|||||||
|
package com.yexuejc.springboot.base.util;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class RSA {
|
||||||
|
|
||||||
|
public static final String CHARSET = "UTF-8";
|
||||||
|
public static final String RSA_ALGORITHM = "RSA";
|
||||||
|
|
||||||
|
public static Map<String, String> initKeys(int keySize) {
|
||||||
|
//为RSA算法创建一个KeyPairGenerator对象
|
||||||
|
KeyPairGenerator kpg;
|
||||||
|
try {
|
||||||
|
kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化KeyPairGenerator对象,密钥长度
|
||||||
|
kpg.initialize(keySize);
|
||||||
|
//生成密匙对
|
||||||
|
KeyPair keyPair = kpg.generateKeyPair();
|
||||||
|
//得到公钥
|
||||||
|
Key publicKey = keyPair.getPublic();
|
||||||
|
String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
|
||||||
|
//得到私钥
|
||||||
|
Key privateKey = keyPair.getPrivate();
|
||||||
|
String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
|
||||||
|
Map<String, String> keyPairMap = new HashMap<String, String>();
|
||||||
|
keyPairMap.put("publicKey", publicKeyStr);
|
||||||
|
keyPairMap.put("privateKey", privateKeyStr);
|
||||||
|
|
||||||
|
return keyPairMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到公钥
|
||||||
|
*
|
||||||
|
* @param publicKey 密钥字符串(经过base64编码)
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
|
//通过X509编码的Key指令获得公钥对象
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
|
||||||
|
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
|
||||||
|
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到私钥
|
||||||
|
*
|
||||||
|
* @param privateKey 密钥字符串(经过base64编码)
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
|
//通过PKCS#8编码的Key指令获得私钥对象
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
|
||||||
|
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
|
||||||
|
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥加密
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param publicKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String publicEncrypt(String data, RSAPublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||||
|
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥解密
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param privateKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||||
|
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥加密
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param privateKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
||||||
|
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥解密
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param publicKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static String publicDecrypt(String data, RSAPublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
||||||
|
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
|
||||||
|
int maxBlock = 0;
|
||||||
|
if (opmode == Cipher.DECRYPT_MODE) {
|
||||||
|
maxBlock = keySize / 8;
|
||||||
|
} else {
|
||||||
|
maxBlock = keySize / 8 - 11;
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
int offSet = 0;
|
||||||
|
byte[] buff;
|
||||||
|
int i = 0;
|
||||||
|
try {
|
||||||
|
while (datas.length > offSet) {
|
||||||
|
if (datas.length - offSet > maxBlock) {
|
||||||
|
buff = cipher.doFinal(datas, offSet, maxBlock);
|
||||||
|
} else {
|
||||||
|
buff = cipher.doFinal(datas, offSet, datas.length - offSet);
|
||||||
|
}
|
||||||
|
out.write(buff, 0, buff.length);
|
||||||
|
i++;
|
||||||
|
offSet = i * maxBlock;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
|
||||||
|
}
|
||||||
|
byte[] resultDatas = out.toByteArray();
|
||||||
|
IOUtils.closeQuietly(out);
|
||||||
|
return resultDatas;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,17 +1,104 @@
|
|||||||
package com.yexuejc.springboot.base;
|
package com.yexuejc.springboot.base;
|
||||||
|
|
||||||
|
import com.yexuejc.base.pojo.ParamsPO;
|
||||||
|
import com.yexuejc.base.util.JsonUtil;
|
||||||
|
import com.yexuejc.base.util.StrUtil;
|
||||||
|
import com.yexuejc.springboot.base.filter.RsaProperties;
|
||||||
|
import com.yexuejc.springboot.base.util.RSA;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringBootTest(classes = ApplicationRun.class)
|
@SpringBootTest(classes = ApplicationRun.class)
|
||||||
public class ApplicationTest {
|
public class ApplicationTest {
|
||||||
|
@Autowired
|
||||||
|
RsaProperties properties;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void contextLoads() {
|
public void contextLoads() throws InvalidKeySpecException, NoSuchAlgorithmException {
|
||||||
System.out.printf("springboot test is runing");
|
Map map = new HashMap();
|
||||||
|
map.put("page", 5);
|
||||||
|
map.put("size", 16);
|
||||||
|
map.put("content", "定制榻榻米垫竹编客厅茶几垫卧室地毯竹地毯飘窗垫日式榻榻米地毯");
|
||||||
|
map.put("content2", map.get("content"));
|
||||||
|
map.put("content3", map.get("content"));
|
||||||
|
map.put("content4", map.get("content"));
|
||||||
|
map.put("content5", map.get("content"));
|
||||||
|
map.put("content6", map.get("content"));
|
||||||
|
|
||||||
|
long t = System.currentTimeMillis();
|
||||||
|
String datas = StrUtil.getSignContent(map);
|
||||||
|
System.out.println(datas);
|
||||||
|
ParamsPO params = new ParamsPO();
|
||||||
|
params.setSign(StrUtil.toMD5(datas));
|
||||||
|
params.setData(RSA.publicEncrypt(datas, RSA.getPublicKey(properties.getPublicKey())));
|
||||||
|
System.out.println("耗时" + (System.currentTimeMillis() - t));
|
||||||
|
System.out.println(JsonUtil.obj2Json(params));
|
||||||
|
|
||||||
|
//{"datas":"GfobmjZv8naQoy6Cy1DVVPQTmXoSkAXKdv8XvxBFCDYcY2khmpOQyiM8Jc5DEeBEoGC_JwyG_f89jRhcHRy4WQ",
|
||||||
|
// "sign":"d46b089cdea6ddbe3a747a27454ae090"}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void t2() throws InvalidKeySpecException, NoSuchAlgorithmException {
|
||||||
|
// String strData = "KrlXChF8LE94EEnycvbi8AygpaZiHKaXH_OmC5sGhGQlvYp1arNk6WW7yR7kAWMLugCS5TKf8FIiYXnyuI8vjA";
|
||||||
|
// String strData = "MNqGKU5oYTXs77mnRgIkN_DkwBgNHUx3uiJ72QmTmyqkF9Rqf2m_q3mc4wTxLRjXLs8KaH_UExiXMhH0Xm63gA";
|
||||||
|
// String strData = "HgTMgxEEFSr6zRLvZQ3U5CAKjJqwKf0lfruZTi32iWObkbJA5mHuOTKU4rXYkej4UsPfArYUA45GfxatwFoB4Q";
|
||||||
|
String strData = "K9Zyg82WDvIApFmXTxPwjQw_VA041jfBcxMIP6jpMM6xWe1XajGf3__7DqSLrS9MwCra9cYkidcjVJAKZn9cmQ";
|
||||||
|
|
||||||
|
|
||||||
|
String mapData = "cEPfMp7-rim76XFdbNDSIzDRXyHySpz0VOZP6HC8U-JPl-ZxNZKp6ethEQWWITcuUPzIhp4fHiGKNsHA7F6OxCxibpMLj5-ZsgJJvcczw8Liens5kYgciRF1UziR3LFy6vybN9H1CJnqXaddbl3t_41P-_1l5Ev7YYa8woWp7ulaRPeTCDjohEpmx2Vi6aPSrm3hjjmitkD9gb0O6vFDNnclyNhFepKV3oh93tNv50sEQQ_QSBUXSHUtCnhTiBX8VsRX3h58F2tie7bG8VSk-6KFuXI07OiqFZSNpcwDOuq-GfMlEfPL3pX-gYhoOORPNClRlQHwyfHXBJly3gRtNVpVksHWQjr1xutWgYfwRjQPHBHNZwfx4E0XoCTuz9qH1CzFmmz68i63GzCM286zJ-J26MkiTDO1zH4jhglo38tnzz9HLeDcbbCuJg1jzkvpFiWamM-6odWhtCg65BS1tJJVWg023kWygZMu5Ebrm5WBbbatN87_K5zn211tFpKwRq2oVjO_AfJRY90WlQGEIHnzZNz_cf8mAjlmilHDuNdjYlj3axTUqLfgLDVaIkasREnjMI7oe8oAtG2ju2aq-xSAQZ_U-7-rsyBpoy0jnwRmlyUxhXgIX0zrfBQNXEjzPtg-iJ14R5qz1iOAJL7NtQQeuYngGTj6msDlKGEd_MQTLAFDbpiVPwWX00jLT1Ll3_zhivpPCUAmC8Yz58khkqrqi4FdIxJTDkxd0PFOBH8DYicF7ls4UdOHT24mAKDwUF_TfZ32oiiKSzCD9MJB8GEXjzx7tDFok-HsdOjI6ZnSUJCOTj3wne2E6_a8Gq2_vp5CWyW12wthJbH79aa7JVfy5cx-cZmNid7oCe54KYclz1tdUgLPCQ1ajsEevbRJ_NBkTmY2wAmUpHODeocDaYt_2AwAU2DLiv2uZuaVszNSUy593Zrzxq5AaY-oWbEeD24SyEWJObJtz5knYzr4NxjZShcjx9ezwiwkRZMtLZpA_cCPFAK1nOrN8zHCOZquS17CCSLDySLvGbxNqYeBa_lGSq8cQuQo8yybd1WkuLKUjUiJecmH2XcZNTPCtdRe0eLlRtk5928AQGsQugwSig";
|
||||||
|
|
||||||
|
|
||||||
|
// String listData = "X65trOhRSpeaN-2qP0zhdYi2jeJDcrTz2JHkc6UFG17xAho-VO0fkD0cA8wxoxcqyTaulOiSzaidZ2VeIvKjuinlKT2r23kdMJxjJodOZojssxgGSxm5gnry2tq5X8dbP7n-jodvAvLE9Gtq7AaBQ36ZQBQ2RcFB3TiHKHGin0gfn6T6A80orYD7i-Bdc0rh_pBEdLwGt1wWY_C8QuxeBmMWh0jmLVfpa3ZZOXVc9I7wIxzc1taQ7f-8Om9SNfXc";
|
||||||
|
String listData = "Sf_FO8YC9EUNTeM0n9EVuDzwvLz3DcxOuG4-5UZ9486lLHAx7IOuAhPgVHpQGsQiqJ7Y3fTaWFr6rRFPL12rVg";
|
||||||
|
|
||||||
|
// 解密步骤
|
||||||
|
strData = RSA.publicDecrypt(strData, RSA.getPublicKey(properties.getPublicKey()));
|
||||||
|
mapData = RSA.publicDecrypt(mapData, RSA.getPublicKey(properties.getPublicKey()));
|
||||||
|
listData = RSA.publicDecrypt(listData, RSA.getPublicKey(properties.getPublicKey()));
|
||||||
|
System.out.println(strData);
|
||||||
|
System.out.println(mapData);
|
||||||
|
System.out.println(listData);
|
||||||
|
|
||||||
|
//其他
|
||||||
|
assertThat(StrUtil.toMD5(JsonUtil.json2Obj(strData, String.class))
|
||||||
|
// ).isEqualTo("fc4ead323d52f2b1122d1a9634c865c6");
|
||||||
|
// ).isEqualTo("c4ca4238a0b923820dcc509a6f75849b");
|
||||||
|
// ).isEqualTo("b326b5062b2f0e69046810717534cb09");
|
||||||
|
).isEqualTo("c977050805d8d1ebaa1e03525cbaee15");
|
||||||
|
|
||||||
|
//map
|
||||||
|
assertThat(
|
||||||
|
StrUtil.toMD5(
|
||||||
|
StrUtil.getSignContent(
|
||||||
|
JsonUtil.json2Obj(mapData, Map.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).isEqualTo("f721e7b0d5415302f5fe7dc5beb2938a");
|
||||||
|
|
||||||
|
|
||||||
|
//list
|
||||||
|
assertThat(
|
||||||
|
StrUtil.toMD5(
|
||||||
|
JsonUtil.obj2Json(
|
||||||
|
JsonUtil.json2Obj(listData, List.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// ).isEqualTo("2a8d8eccabadc897ad65c04940bd833b");
|
||||||
|
).isEqualTo("efe23825367fc9997a9667463e34753a");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.yexuejc.springboot.base.web;
|
||||||
|
|
||||||
|
import com.yexuejc.base.http.Resps;
|
||||||
|
import com.yexuejc.base.pojo.PagerVO;
|
||||||
|
import com.yexuejc.base.util.JsonUtil;
|
||||||
|
import com.yexuejc.base.util.StrUtil;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: maxf
|
||||||
|
* @date: 2018/5/12 14:17
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class IndexCtrl {
|
||||||
|
|
||||||
|
@RequestMapping(value = {"/", "/index"})
|
||||||
|
public Resps index() {
|
||||||
|
return Resps.success("请求成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = {"/2"})
|
||||||
|
public Resps a(String name, @Param("name") String pwd) {
|
||||||
|
System.out.println("请求参数:name = [" + name + "], pwd = [" + pwd + "]");
|
||||||
|
return Resps.success("请求成功").setSucc("id>" + StrUtil.genUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = {"/4"})
|
||||||
|
public Resps e() {
|
||||||
|
List<String> list = new ArrayList();
|
||||||
|
list.add("asdsad");
|
||||||
|
list.add("发生大幅度发");
|
||||||
|
list.add("1351615");
|
||||||
|
return Resps.success("请求成功").setSucc(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = {"/5"})
|
||||||
|
public Resps f() {
|
||||||
|
List<PagerVO> list = new ArrayList();
|
||||||
|
PagerVO pq = new PagerVO();
|
||||||
|
pq.setPage(333);
|
||||||
|
pq.setSize(1);
|
||||||
|
list.add(new PagerVO());
|
||||||
|
PagerVO pw = new PagerVO();
|
||||||
|
pw.setPage(23);
|
||||||
|
pw.setSize(255);
|
||||||
|
list.add(pw);
|
||||||
|
PagerVO p = new PagerVO();
|
||||||
|
p.setPage(555);
|
||||||
|
list.add(p);
|
||||||
|
return Resps.success("请求成功").setSucc(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = {"/6"})
|
||||||
|
public Resps h() {
|
||||||
|
return Resps.success("请求成功").setSucc(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = {"/7"})
|
||||||
|
public Resps k() {
|
||||||
|
return Resps.success("请求成功").setSucc(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = {"/8"})
|
||||||
|
public Resps l() {
|
||||||
|
return Resps.success("请求成功").setSucc(05652.154);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = {"/3"})
|
||||||
|
public Resps b(@RequestBody PagerVO pagerVO) {
|
||||||
|
System.out.println("请求参数:" + JsonUtil.obj2Json(pagerVO));
|
||||||
|
Map map = new HashMap();
|
||||||
|
map.put("page", 5);
|
||||||
|
map.put("size", 16);
|
||||||
|
map.put("content", "定制榻榻米垫竹编客厅茶几垫卧室地毯竹地毯飘窗垫日式榻榻米地毯");
|
||||||
|
map.put("content2", map.get("content"));
|
||||||
|
map.put("content3", map.get("content"));
|
||||||
|
map.put("content4", map.get("content"));
|
||||||
|
map.put("content5", map.get("content"));
|
||||||
|
map.put("content6", map.get("content"));
|
||||||
|
return Resps.success().setSucc(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,18 @@
|
|||||||
server.port=8080
|
server.port=8080
|
||||||
logging.level.root=info
|
logging.level.root=info
|
||||||
|
|
||||||
security.basic.enabled=false
|
security.basic.enabled=false
|
||||||
|
|
||||||
|
yexuejc.http.filter.type=0
|
||||||
|
|
||||||
|
yexuejc.http.encrypt.private-key=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAiSo5blJ9-QJ0_QElcy5AaRTq-3oO4lJ8PvIOIt-Xr5SUFODVj3DUbiy6_0bxQYO3NiYHlXPb37UVV3jjlXJsXwIDAQABAkBE0WOJH2hGs93gRl_0vwLf9ffDfkTTdlER_73p70aad3QZRslEkinQH7G5aE_DgBm5m72TCeH-PD2FZ2lwtavBAiEAvnRown5Lpqbl0tN_OUxr_e1u9d_-8dNL_JEETO7BZCECIQC4XtY-18j0bVVLxaXPjKQ00D59yntwObihDNyRK0nAfwIgHPHEGgrnpGQo-Wl7JFIg925mNqfcLxRVsAS6CpcefQECIQCUsLdsmy6QIhTmNRJSXoSXq1KatE_05DhIekzwLs8eFQIgfMawMiu52ZxBI5_pZ7ancQZ6Dsxl45utFqJShzV1pio
|
||||||
|
yexuejc.http.encrypt.public-key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIkqOW5SffkCdP0BJXMuQGkU6vt6DuJSfD7yDiLfl6-UlBTg1Y9w1G4suv9G8UGDtzYmB5Vz29-1FVd445VybF8CAwEAAQ
|
||||||
|
yexuejc.http.encrypt.encrypt=true
|
||||||
|
yexuejc.http.encrypt.decrypt=true
|
||||||
|
|
||||||
|
|
||||||
|
#编码
|
||||||
|
spring.http.encoding.force=true
|
||||||
|
spring.http.encoding.charset=UTF-8
|
||||||
|
spring.http.encoding.enabled=true
|
||||||
|
server.tomcat.uri-encoding=UTF-8
|
Loading…
x
Reference in New Issue
Block a user