mirror of
https://github.com/abcv7/sfbx-cloud.git
synced 2026-08-16 11:47:00 +00:00
功能:在保险、规则、交易、积分和短信模块中实现初始功能和基础组件。
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>sfbx-cloud</artifactId>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!--三方sdk接口封装-->
|
||||
<artifactId>sfbx-apache-httpclient</artifactId>
|
||||
<name>sfbx-apache-httpclient</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import com.itheima.sfbx.auth.*;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.execchain.ClientExecChain;
|
||||
|
||||
/**
|
||||
* @ClassName BoleeHttpClientBuilder.java
|
||||
* @Description 保乐三方请求HttpClient对象构建
|
||||
*/
|
||||
public class BoleeHttpClientBuilder extends HttpClientBuilder {
|
||||
|
||||
//系统相关属性
|
||||
private static final String OS = System.getProperty("os.name") + "/" + System.getProperty("os.version");
|
||||
private static final String VERSION = System.getProperty("java.version");
|
||||
|
||||
//凭证构建
|
||||
private Credentials credentials;
|
||||
|
||||
//校验者
|
||||
private Validator validator;
|
||||
|
||||
|
||||
public static BoleeHttpClientBuilder create() {
|
||||
return new BoleeHttpClientBuilder();
|
||||
}
|
||||
|
||||
private BoleeHttpClientBuilder() {
|
||||
String userAgent = String.format("sfbx-Apache-HttpClient/%s (%s) Java/%s", this.getClass().getPackage().getImplementationVersion(), OS, VERSION == null ? "Unknown" : VERSION);
|
||||
this.setUserAgent(userAgent);
|
||||
}
|
||||
|
||||
public BoleeHttpClientBuilder withCredentials(String appId,String privateKey) {
|
||||
this.credentials = new BoleeCredentials(appId, new PrivateKeySigner(privateKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BoleeHttpClientBuilder withValidator(String publicKey) {
|
||||
this.validator = new BoleeValidator(new PublicKeyVerifier(publicKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloseableHttpClient build() {
|
||||
if (this.credentials == null) {
|
||||
throw new IllegalArgumentException("缺少身份认证信息");
|
||||
} else if (this.validator == null) {
|
||||
throw new IllegalArgumentException("缺少签名验证信息");
|
||||
} else {
|
||||
return super.build();
|
||||
}
|
||||
}
|
||||
|
||||
protected ClientExecChain decorateProtocolExec(ClientExecChain requestExecutor) {
|
||||
return new DecorateClientExecChain(this.credentials, this.validator, requestExecutor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName Credentials.java
|
||||
* @Description 认证凭证创建处理
|
||||
*/
|
||||
public interface Credentials {
|
||||
|
||||
/***
|
||||
* @description 创建请求认证凭证
|
||||
* @param request 请求对象
|
||||
* @return: java.lang.String 凭证字符串
|
||||
*/
|
||||
String createCredentials(HttpRequestWrapper request) throws IOException;
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpExecutionAware;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.entity.BufferedHttpEntity;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.execchain.ClientExecChain;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.apache.http.HttpHeaders.AUTHORIZATION;
|
||||
import static org.apache.http.HttpStatus.SC_MULTIPLE_CHOICES;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
|
||||
/**
|
||||
* @ClassName SignatureExec.java
|
||||
* @Description 请求执行者
|
||||
*/
|
||||
@Slf4j
|
||||
public class DecorateClientExecChain implements ClientExecChain {
|
||||
|
||||
private final ClientExecChain mainExec;
|
||||
|
||||
private final Credentials credentials;
|
||||
|
||||
private final Validator validator;
|
||||
|
||||
protected DecorateClientExecChain(Credentials credentials, Validator validator, ClientExecChain mainExec) {
|
||||
this.credentials = credentials;
|
||||
this.validator = validator;
|
||||
this.mainExec = mainExec;
|
||||
}
|
||||
|
||||
protected void convertToRepeatableResponseEntity(CloseableHttpResponse response) throws IOException {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
response.setEntity(new BufferedHttpEntity(entity));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloseableHttpResponse execute(HttpRoute httpRoute, HttpRequestWrapper httpRequestWrapper,
|
||||
HttpClientContext httpClientContext, HttpExecutionAware httpExecutionAware) throws IOException, HttpException {
|
||||
//请求头添加认证令牌
|
||||
httpRequestWrapper.addHeader(AUTHORIZATION,credentials.createCredentials(httpRequestWrapper));
|
||||
//执行请求
|
||||
CloseableHttpResponse response = mainExec.execute(httpRoute, httpRequestWrapper, httpClientContext, httpExecutionAware);
|
||||
//对成功应答验签
|
||||
StatusLine statusLine = response.getStatusLine();
|
||||
if (statusLine.getStatusCode() >= SC_OK && statusLine.getStatusCode() < SC_MULTIPLE_CHOICES) {
|
||||
convertToRepeatableResponseEntity(response);
|
||||
if (!validator.validate(response)) {
|
||||
throw new HttpException("应答的签名验证失败");
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import com.itheima.sfbx.dto.EncodeDTO;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface Encryptor {
|
||||
|
||||
|
||||
/***
|
||||
* @description 加密body体消息的方法
|
||||
* @param body 请求消息体
|
||||
* @return: boolean 校验结果
|
||||
*/
|
||||
boolean encode(EncodeDTO body) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.itheima.sfbx.auth.BoleeEncryptor;
|
||||
import com.itheima.sfbx.constants.BoleeSecurityConstant;
|
||||
import com.itheima.sfbx.dto.CredentialsDTO;
|
||||
import com.itheima.sfbx.dto.EncodeDTO;
|
||||
import com.itheima.sfbx.utils.SecurityUtil;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* RequestTemplate
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: 对外统一发送模板--组装各个模组
|
||||
* 加密模组
|
||||
* 解密模组组装
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Data
|
||||
public class RequestTemplate {
|
||||
|
||||
private String privateKey;
|
||||
|
||||
private String publicKey;
|
||||
|
||||
private String appId;
|
||||
|
||||
private URIBuilder uriBuilder;
|
||||
|
||||
private RequestConfig requestConfig;
|
||||
|
||||
@Builder
|
||||
public RequestTemplate(String privateKey, String publicKey, String appId, URIBuilder uriBuilder,RequestConfig requestConfig) {
|
||||
this.privateKey = privateKey;
|
||||
this.publicKey = publicKey;
|
||||
this.appId = appId;
|
||||
this.uriBuilder = uriBuilder;
|
||||
this.requestConfig = requestConfig;
|
||||
}
|
||||
|
||||
|
||||
public <T> T doRequest(Object params,Class<T> t) throws URISyntaxException, IOException {
|
||||
CloseableHttpClient httpclient = BoleeHttpClientBuilder.create()
|
||||
.withCredentials(appId, privateKey)
|
||||
.withValidator(publicKey)
|
||||
.build();
|
||||
//=============基础配置===================
|
||||
RequestConfig requestConfig = null;
|
||||
if(ObjectUtil.isNull(requestConfig)) {
|
||||
requestConfig = RequestConfig.custom()
|
||||
.setSocketTimeout(100000)
|
||||
.setConnectTimeout(100000)
|
||||
.build();
|
||||
}else{
|
||||
requestConfig = this.requestConfig;
|
||||
}
|
||||
try {
|
||||
URI url = uriBuilder.build();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(requestConfig);
|
||||
EncodeDTO encodeDTO = new EncodeDTO();
|
||||
JSONObject body = JSONUtil.parseObj(params);
|
||||
encodeDTO.setBody(body.toString());
|
||||
if(new BoleeEncryptor(privateKey).encode(encodeDTO)){
|
||||
StringEntity stringEntity = new StringEntity(encodeDTO.getEncodeBody(), "utf-8");
|
||||
httpPost.setEntity(stringEntity);
|
||||
httpPost.addHeader(BoleeSecurityConstant.HEAD_NAME_BODY_KEY,encodeDTO.getHeadAESSecurityKey());
|
||||
httpPost.addHeader("Content-Type","application/json;charset=utf-8");
|
||||
CloseableHttpResponse apiRes = httpclient.execute(httpPost);
|
||||
//验签完毕后需要对数据进行解密
|
||||
HttpEntity entity = apiRes.getEntity();
|
||||
String content = EntityUtils.toString(entity);
|
||||
String responseJson = decryptRequestBody(content, apiRes.getFirstHeader(BoleeSecurityConstant.HEAD_NAME_BODY_KEY).getValue());
|
||||
if(StrUtil.isEmpty(responseJson)){
|
||||
throw new RuntimeException("参数解析为空");
|
||||
}
|
||||
return JSONUtil.toBean(responseJson, t);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密请求body体
|
||||
*
|
||||
* @param dody 返回数据对象
|
||||
* @param aesKeyRSA 获取请求头中加密后的AES密钥
|
||||
* @return
|
||||
*/
|
||||
private String decryptRequestBody(String dody, String aesKeyRSA) {
|
||||
try {
|
||||
//先使用rsa解密aes秘钥
|
||||
RSA rsa = SecureUtil.rsa(null, publicKey);
|
||||
String aesKey = SecurityUtil.decryptFromStringRSAPublicKey(rsa, aesKeyRSA);
|
||||
//使用aes进行解密
|
||||
// 使用解密后的AES密钥进行AES解密请求体数据
|
||||
String requestBody = SecurityUtil.decryptFromStringAES(dody, Mode.CBC, Padding.ZeroPadding,aesKey);
|
||||
return requestBody;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 解密失败,可以根据你的需求进行异常处理
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求参数
|
||||
* 1694953272
|
||||
* xHct36JgHbj6tXBx
|
||||
* Modified: {"msg":"ok","code":"100","data":null}
|
||||
*
|
||||
* @param authorization
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
private CredentialsDTO buildRequestParam(String authorization, CloseableHttpResponse response) {
|
||||
try {
|
||||
authorization = authorization.replaceAll("\"", "");
|
||||
String[] authorizations = authorization.split(",");
|
||||
Map<String, String> authMap = new HashMap<>();
|
||||
for (String authorizationIndex : authorizations) {
|
||||
String[] split = authorizationIndex.split("=");
|
||||
authMap.put(split[0], split[1].substring(0, split[1].length()));
|
||||
}
|
||||
CredentialsDTO credentialsDTO = new CredentialsDTO();
|
||||
credentialsDTO.setNonce(authMap.get("nonce_str"));
|
||||
credentialsDTO.setTimestamp(authMap.get("timestamp"));
|
||||
credentialsDTO.setSignature(authMap.get("signature"));
|
||||
// credentialsDTO.setSecurityBody(getPostData(req));
|
||||
return credentialsDTO;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
/**
|
||||
* @ClassName Signer.java
|
||||
* @Description 签名加密者
|
||||
*/
|
||||
public interface Signer {
|
||||
|
||||
String sign(byte[] message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import com.itheima.sfbx.notify.NotifyRequest;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName Validator.java
|
||||
* @Description 签名验证处理
|
||||
*/
|
||||
public interface Validator {
|
||||
|
||||
/***
|
||||
* @description 验证应答签名
|
||||
* @param response 响应结果
|
||||
* @return: boolean 校验结果
|
||||
*/
|
||||
boolean validate(CloseableHttpResponse response) throws IOException;
|
||||
|
||||
/***
|
||||
* @description 验证通知请求签名
|
||||
* @param notifyRequest 响应结果
|
||||
* @return: boolean 校验结果
|
||||
*/
|
||||
boolean validateNotify(NotifyRequest notifyRequest);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @ClassName Verifier.java
|
||||
* @Description 验签解密者
|
||||
*/
|
||||
public interface Verifier {
|
||||
|
||||
boolean verify(byte[] data,byte[] signature);
|
||||
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import com.itheima.sfbx.Credentials;
|
||||
import com.itheima.sfbx.Signer;
|
||||
import com.itheima.sfbx.utils.ClientUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName BoleeCredentials.java
|
||||
* @Description 认证凭证创建处理实现
|
||||
*/
|
||||
@Slf4j
|
||||
public class BoleeCredentials implements Credentials {
|
||||
|
||||
//应用id
|
||||
protected final String appId;
|
||||
|
||||
//签名者
|
||||
protected final Signer signer;
|
||||
|
||||
public BoleeCredentials(String appId, Signer signer) {
|
||||
this.appId = appId;
|
||||
this.signer = signer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createCredentials(HttpRequestWrapper request) throws IOException {
|
||||
//临时字符串
|
||||
String nonceStr = ClientUtils.generateNonceStr();
|
||||
//时间戳
|
||||
long timestamp = ClientUtils.generateTimestamp();
|
||||
//请求数据
|
||||
String message = ClientUtils.requestMessage(nonceStr, timestamp, request);
|
||||
log.debug("authorization data:{}", message);
|
||||
//生成签名字符串
|
||||
String signature = signer.sign(Base64.decodeBase64(message));
|
||||
//返回认证令牌对象
|
||||
String credentials = "appId=\"" + this.appId + "\","
|
||||
+ "nonce_str=\"" + nonceStr + "\","
|
||||
+ "timestamp=\"" + timestamp + "\","
|
||||
+ "signature=\"" + signature + "\"";
|
||||
log.debug("authorization credentials=[{}]", credentials);
|
||||
return credentials;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import com.itheima.sfbx.Encryptor;
|
||||
import com.itheima.sfbx.constants.BoleeSecurityConstant;
|
||||
import com.itheima.sfbx.dto.EncodeDTO;
|
||||
import com.itheima.sfbx.utils.ClientUtils;
|
||||
import com.itheima.sfbx.utils.SecurityUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* BoleeEncryptor
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: TODO
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Slf4j
|
||||
public class BoleeEncryptor implements Encryptor {
|
||||
|
||||
//私钥签名
|
||||
protected String privateKeyBase64;
|
||||
|
||||
|
||||
public BoleeEncryptor(String privateKey) {
|
||||
this.privateKeyBase64 = privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对数据进行加密
|
||||
* @param body 请求消息体
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public boolean encode(EncodeDTO body){
|
||||
//获取AES加密秘钥
|
||||
String bodySecurity = ClientUtils.generateNonceStr();
|
||||
//使用AES加密 body体里的数据
|
||||
String aesSecurity = SecurityUtil.encryptFromStringAES(body.getBody(), Mode.CBC, Padding.ZeroPadding,bodySecurity);
|
||||
body.setEncodeBody(aesSecurity);
|
||||
//使用RSA加密AES的对称加密秘钥
|
||||
RSA rsa = new RSA(privateKeyBase64, null);
|
||||
String securityToken = SecurityUtil.encryptFromStringRSAPrivateKey(rsa, bodySecurity);
|
||||
body.setHeadAESSecurityKey(securityToken);
|
||||
//将新生产的AES的body返回
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import com.itheima.sfbx.Validator;
|
||||
import com.itheima.sfbx.Verifier;
|
||||
import com.itheima.sfbx.notify.NotifyRequest;
|
||||
import com.itheima.sfbx.utils.ClientUtils;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @ClassName BoleeValidator.java
|
||||
* @Description 签名验证处理实现
|
||||
*/
|
||||
public class BoleeValidator implements Validator {
|
||||
|
||||
protected final Verifier verifier;
|
||||
|
||||
public BoleeValidator(Verifier verifier) {
|
||||
this.verifier = verifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(CloseableHttpResponse response) throws IOException {
|
||||
//校验请求参数
|
||||
ClientUtils.validateParameters(response);
|
||||
//构建响应体信息
|
||||
String message = ClientUtils.responseMessage(response);
|
||||
//获得签名信息
|
||||
String signature = response.getFirstHeader("signature").getValue();
|
||||
//验证签名
|
||||
return verifier.verify(message.getBytes(StandardCharsets.UTF_8), Base64.decodeBase64(signature));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateNotify(NotifyRequest notifyRequest) {
|
||||
//校验请求参数
|
||||
ClientUtils.validateParametersNotify(notifyRequest);
|
||||
//构建响应体信息
|
||||
String message = ClientUtils.notifyMessage(notifyRequest);
|
||||
//获得签名信息
|
||||
String signature = notifyRequest.getSignature();
|
||||
//验证签名
|
||||
return verifier.verify(message.getBytes(StandardCharsets.UTF_8), Base64.decodeBase64(signature));
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import com.itheima.sfbx.Signer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @ClassName PrivateKeySigner.java
|
||||
* @Description TODO
|
||||
*/
|
||||
@Slf4j
|
||||
public class PrivateKeySigner implements Signer {
|
||||
|
||||
protected final String privateKey;
|
||||
|
||||
public PrivateKeySigner(String privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String sign(byte[] message) {
|
||||
Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA,Base64.decodeBase64(privateKey),null);
|
||||
return Base64.encodeBase64String(sign.sign(message));
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.itheima.sfbx.auth;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import com.itheima.sfbx.Verifier;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @ClassName PublicKeyVerifier.java
|
||||
* @Description 公钥验证
|
||||
*/
|
||||
public class PublicKeyVerifier implements Verifier {
|
||||
|
||||
protected final String publicKey;
|
||||
|
||||
public PublicKeyVerifier(String publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(byte[] data,byte[] signature){
|
||||
Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA,null, Base64.decodeBase64(publicKey));
|
||||
return sign.verify(data,signature);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.itheima.sfbx.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* SecurityConfig
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: 客户端秘钥配置
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "itheima.security")
|
||||
@Data
|
||||
public class SecurityConfig {
|
||||
|
||||
/**
|
||||
* 客户appId
|
||||
*/
|
||||
@Value("${itheima.security.client.appid}")
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 客户端私钥
|
||||
*/
|
||||
@Value("${itheima.security.client.privateKey}")
|
||||
private String privateKey;
|
||||
|
||||
|
||||
/**
|
||||
* 服务端公钥
|
||||
*/
|
||||
@Value("${itheima.security.server.publicKey}")
|
||||
private String publicKey;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.itheima.sfbx.constants;
|
||||
|
||||
/**
|
||||
* BoleeSecurityConstant
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: 宝乐保险秘钥常量
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
public class BoleeSecurityConstant {
|
||||
|
||||
/**
|
||||
* 宝乐保险中body体中的信息
|
||||
*/
|
||||
public static String HEAD_NAME_BODY_KEY = "bolee_security_key";
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.itheima.sfbx.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* RequestParamDTO
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: TODO
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Data
|
||||
public class CredentialsDTO {
|
||||
|
||||
/**
|
||||
* 请求方式
|
||||
*/
|
||||
private String methodd;
|
||||
|
||||
/**
|
||||
* 接口路径
|
||||
*/
|
||||
private String uriPath;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private String timestamp;
|
||||
|
||||
/**
|
||||
* 签名数据
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* 密文数据
|
||||
*/
|
||||
private String securityBody;
|
||||
|
||||
/**
|
||||
* 随机字符串
|
||||
*/
|
||||
private String nonce;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 构建签名字符串
|
||||
* return request.getRequestLine().getMethod() + "\n"
|
||||
* + canonicalUrl + "\n"
|
||||
* + timestamp + "\n"
|
||||
* + nonce + "\n"
|
||||
* + body + "\n";
|
||||
* @return
|
||||
*/
|
||||
public String buildSignBody(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(methodd + "\n");
|
||||
sb.append(uriPath + "\n");
|
||||
sb.append(timestamp + "\n");
|
||||
sb.append(nonce + "\n");
|
||||
sb.append(securityBody + "\n");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.itheima.sfbx.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* EncodeDTO
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: TODO
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Data
|
||||
public class EncodeDTO {
|
||||
|
||||
/**
|
||||
* 原始body参数
|
||||
*/
|
||||
private String body;
|
||||
|
||||
/**
|
||||
* 加密后的密文
|
||||
*/
|
||||
private String encodeBody;
|
||||
|
||||
/**
|
||||
* head中rsa加密后的aes密钥
|
||||
*/
|
||||
private String headAESSecurityKey;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.itheima.sfbx.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ResponseDTO
|
||||
*
|
||||
* @author: wgl
|
||||
* @describe: 返回结果对象
|
||||
* @date: 2022/12/28 10:10
|
||||
*/
|
||||
@Data
|
||||
public class ResponseDTO {
|
||||
|
||||
private String msg;
|
||||
|
||||
private String code;
|
||||
|
||||
private Map data;
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.itheima.sfbx.notify;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.itheima.sfbx.Validator;
|
||||
import com.itheima.sfbx.Verifier;
|
||||
import com.itheima.sfbx.auth.BoleeValidator;
|
||||
import com.itheima.sfbx.auth.PublicKeyVerifier;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName NotifiyHandler.java
|
||||
* @Description 通知验证及解析出来
|
||||
*/
|
||||
public class NotifiyHandler {
|
||||
|
||||
//校验者
|
||||
private Validator validator;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public NotifiyHandler(String publicKey) {
|
||||
this.validator = new BoleeValidator(new PublicKeyVerifier(publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析通知请求结果
|
||||
* @param notifyRequest 微信支付通知请求
|
||||
* @return 微信支付通知报文解密结果@StreamListener(FileSink.FILE_INPUT)
|
||||
* public void onMessage(@Payload MqMessage message,
|
||||
* @Header(AmqpHeaders.CHANNEL) Channel channel,
|
||||
* @Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag) throws IOException {
|
||||
* String jsonConten = message.getcontent();
|
||||
* log.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message);
|
||||
* FileVO fileVO= JSONObject.parseObject(jsonConten,FileVO.class);
|
||||
* Boolean responseWrap = fileBusinessFeign.clearFileById(fileVO.getId());
|
||||
* channel.basicAck(deliveryTag,false);
|
||||
*
|
||||
* }
|
||||
*/
|
||||
public NotifyResponse parse(NotifyRequest notifyRequest) throws IOException {
|
||||
// 验签
|
||||
boolean validate = validator.validateNotify(notifyRequest);
|
||||
if (!validate){
|
||||
throw new RuntimeException("验证签名失败");
|
||||
}
|
||||
// 解析请求体
|
||||
return parseBody(notifyRequest.getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析请求体
|
||||
*
|
||||
* @param body 请求体
|
||||
* @return 解析结果
|
||||
* @throws IOException 解析body失败
|
||||
*/
|
||||
private NotifyResponse parseBody(String body) throws IOException {
|
||||
ObjectReader objectReader = objectMapper.reader();
|
||||
NotifyResponse notifyResponse = objectReader.readValue(body, NotifyResponse.class);
|
||||
return notifyResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.itheima.sfbx.notify;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @ClassName NotificationRequest.java
|
||||
* @Description 通知请求对象
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class NotifyRequest {
|
||||
|
||||
//时间戳
|
||||
private String timestamp;
|
||||
//随机字符串
|
||||
private String nonce;
|
||||
//签名
|
||||
private String signature;
|
||||
//请求体
|
||||
private String body;
|
||||
|
||||
@Builder
|
||||
public NotifyRequest(String timestamp, String nonce, String signature, String body) {
|
||||
this.timestamp = timestamp;
|
||||
this.nonce = nonce;
|
||||
this.signature = signature;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package com.itheima.sfbx.notify;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* @ClassName NotifyResponse.java
|
||||
* @Description TODO
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class NotifyResponse {
|
||||
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
@JsonProperty("create_time")
|
||||
private String createTime;
|
||||
@JsonProperty("event_type")
|
||||
private String eventType;
|
||||
@JsonProperty("resource_type")
|
||||
private String resourceType;
|
||||
@JsonProperty("summary")
|
||||
private String summary;
|
||||
@JsonProperty("")
|
||||
private Resource resource;
|
||||
private String decryptData;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Notification{" +
|
||||
"id='" + id + '\'' +
|
||||
", createTime='" + createTime + '\'' +
|
||||
", eventType='" + eventType + '\'' +
|
||||
", resourceType='" + resourceType + '\'' +
|
||||
", decryptData='" + decryptData + '\'' +
|
||||
", summary='" + summary + '\'' +
|
||||
", resource=" + resource +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public String getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public String getDecryptData() {
|
||||
return decryptData;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setDecryptData(String decryptData) {
|
||||
this.decryptData = decryptData;
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Resource {
|
||||
|
||||
@JsonProperty("algorithm")
|
||||
private String algorithm;
|
||||
@JsonProperty("ciphertext")
|
||||
private String ciphertext;
|
||||
@JsonProperty("associated_data")
|
||||
private String associatedData;
|
||||
@JsonProperty("nonce")
|
||||
private String nonce;
|
||||
@JsonProperty("original_type")
|
||||
private String originalType;
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public String getCiphertext() {
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
public String getAssociatedData() {
|
||||
return associatedData;
|
||||
}
|
||||
|
||||
public String getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public String getOriginalType() {
|
||||
return originalType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Resource{" +
|
||||
"algorithm='" + algorithm + '\'' +
|
||||
", ciphertext='" + ciphertext + '\'' +
|
||||
", associatedData='" + associatedData + '\'' +
|
||||
", nonce='" + nonce + '\'' +
|
||||
", originalType='" + originalType + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.itheima.sfbx.utils;
|
||||
|
||||
import com.itheima.sfbx.notify.NotifyRequest;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* @ClassName ClientUtils.java
|
||||
* @Description 客户端工具类
|
||||
*/
|
||||
public class ClientUtils {
|
||||
|
||||
//随机字符串处理
|
||||
protected static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
//随机函数
|
||||
protected static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
/***
|
||||
* @description 时间戳创建
|
||||
* @return: long 返回时间戳
|
||||
*/
|
||||
public static long generateTimestamp() {
|
||||
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 随机字符串
|
||||
* @return: java.lang.String 字符串
|
||||
*/
|
||||
public static String generateNonceStr() {
|
||||
char[] nonceChars = new char[16];
|
||||
for (int index = 0; index < nonceChars.length; ++index) {
|
||||
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
|
||||
}
|
||||
return new String(nonceChars);
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 构建请求信息: 请求方式+请求路径+请求时间戳+随机字符串+请求体
|
||||
* @param nonce 随机字符串
|
||||
* @param timestamp 时间戳
|
||||
* @param request 请求对象
|
||||
* @return: java.lang.String 请求信息
|
||||
*/
|
||||
public static String requestMessage(String nonce, long timestamp, HttpRequestWrapper request) throws IOException {
|
||||
URI uri = request.getURI();
|
||||
String canonicalUrl = uri.getRawPath();
|
||||
if (uri.getQuery() != null) {
|
||||
canonicalUrl += "?" + uri.getRawQuery();
|
||||
}
|
||||
String body = EntityUtils.toString(((HttpEntityEnclosingRequest) request).getEntity(), StandardCharsets.UTF_8);
|
||||
return request.getRequestLine().getMethod() + "\n"
|
||||
+ canonicalUrl + "\n"
|
||||
+ timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 检测应答参数
|
||||
* @param response 应答对象
|
||||
*/
|
||||
public static final void validateParameters(CloseableHttpResponse response) {
|
||||
//返回头中存在的内容:签名字符串,随机字符串,时间戳
|
||||
String[] headers = {"signature","nonce_str","timestamp"};
|
||||
Header header = null;
|
||||
for (String headerName : headers) {
|
||||
header = response.getFirstHeader(headerName);
|
||||
if (header == null) {
|
||||
throw new RuntimeException("验证返回参数"+headerName+"不全");
|
||||
}
|
||||
}
|
||||
//获得最后时间戳,判断应答时间戳到当前时间超过5分钟则认为应答失败
|
||||
String timestampStr = header.getValue();
|
||||
try {
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
|
||||
// 拒绝过期应答
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) {
|
||||
throw new RuntimeException("应答超时");
|
||||
}
|
||||
} catch (DateTimeException | NumberFormatException e) {
|
||||
throw new RuntimeException("应答时间处理异常");
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 构建应答信息
|
||||
* @param response 应答结果
|
||||
* @return: java.lang.String 应答信息
|
||||
*/
|
||||
public static String responseMessage(CloseableHttpResponse response) throws IOException {
|
||||
String timestamp = response.getFirstHeader("timestamp").getValue();
|
||||
String nonce = response.getFirstHeader("nonce_str").getValue();
|
||||
HttpEntity entity = response.getEntity();
|
||||
String body = (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";;
|
||||
return timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 构建notify信息
|
||||
* @param notifyRequest 推送请求结果
|
||||
* @return: java.lang.String 应答信息
|
||||
*/
|
||||
public static String notifyMessage(NotifyRequest notifyRequest) {
|
||||
String timestamp = notifyRequest.getTimestamp();
|
||||
String nonce = notifyRequest.getNonce();
|
||||
String body = notifyRequest.getBody();
|
||||
return timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
public static void validateParametersNotify(NotifyRequest notifyRequest) {
|
||||
//请求中必须存在的内容:签名字符串,随机字符串,时间戳
|
||||
String signature = notifyRequest.getSignature();
|
||||
if (signature==null){
|
||||
throw new RuntimeException("签名字符串为空");
|
||||
}
|
||||
String nonce = notifyRequest.getNonce();
|
||||
if (nonce==null){
|
||||
throw new RuntimeException("随机字符串为空");
|
||||
}
|
||||
String timestamp = notifyRequest.getTimestamp();
|
||||
if (timestamp==null){
|
||||
throw new RuntimeException("时间戳为空");
|
||||
}
|
||||
//获得最后时间戳,判断应答时间戳到当前时间超过5分钟则认为应答失败
|
||||
try {
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp));
|
||||
// 拒绝过期应答
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) {
|
||||
throw new RuntimeException("应答超时");
|
||||
}
|
||||
} catch (DateTimeException | NumberFormatException e) {
|
||||
throw new RuntimeException("应答时间处理异常");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.itheima.sfbx.utils;
|
||||
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @ClassName SymmetricCryptoUtil.java
|
||||
* @Description TODO
|
||||
*/
|
||||
public class SecurityUtil {
|
||||
|
||||
/**
|
||||
* 16字节
|
||||
*/
|
||||
private static final String ENCODE_KEY = "1234567812345678";
|
||||
private static final String IV_KEY = "0000000000000000";
|
||||
|
||||
public static void main(String[] args) {
|
||||
String password = "zdm321123.";
|
||||
String encryptData = encryptFromStringAES(password, Mode.CBC, Padding.ZeroPadding);
|
||||
System.out.println("AES加密:" + encryptData);
|
||||
String decryptData = decryptFromStringAES(encryptData, Mode.CBC, Padding.ZeroPadding);
|
||||
System.out.println("AES解密:" + decryptData);
|
||||
RSA rsa = new RSA();
|
||||
encryptData = encryptFromStringRSA(rsa,password);
|
||||
System.out.println("RSA加密:"+encryptData);
|
||||
System.out.println("================用于网络传输===========");
|
||||
decryptData = decryptFromStringRSA(rsa,encryptData);
|
||||
//解密内容生成字符串
|
||||
System.out.println("RSA解密:" + decryptData);
|
||||
|
||||
}
|
||||
|
||||
public static String encryptFromStringAES(String data, Mode mode, Padding padding) {
|
||||
AES aes;
|
||||
if (Mode.CBC == mode) {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"),
|
||||
new IvParameterSpec(IV_KEY.getBytes()));
|
||||
} else {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"));
|
||||
}
|
||||
return aes.encryptBase64(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String encryptFromStringAES(String data, Mode mode, Padding padding,String securityKey) {
|
||||
AES aes;
|
||||
if (Mode.CBC == mode) {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"),
|
||||
new IvParameterSpec(securityKey.getBytes()));
|
||||
} else {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"));
|
||||
}
|
||||
return aes.encryptBase64(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String encryptFromStringRSA(RSA rsa,String password) {
|
||||
//byte[]内容加密
|
||||
byte[] encrypt = rsa.encrypt(StrUtil.bytes(password, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
|
||||
//加密后base64处理用于网络传输
|
||||
return Base64.encodeBase64String(encrypt);
|
||||
}
|
||||
|
||||
public static String encryptFromStringRSAPrivateKey(RSA rsa,String password) {
|
||||
//byte[]内容加密
|
||||
byte[] encrypt = rsa.encrypt(StrUtil.bytes(password, CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
|
||||
//加密后base64处理用于网络传输
|
||||
return Base64.encodeBase64String(encrypt);
|
||||
}
|
||||
|
||||
public static String decryptFromStringAES(String data, Mode mode, Padding padding) {
|
||||
AES aes;
|
||||
if (Mode.CBC == mode) {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"),
|
||||
new IvParameterSpec(IV_KEY.getBytes()));
|
||||
} else {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"));
|
||||
}
|
||||
byte[] decryptDataBase64 = aes.decrypt(data);
|
||||
return new String(decryptDataBase64, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decryptFromStringAES(String data, Mode mode, Padding padding,String aesKey) {
|
||||
AES aes;
|
||||
if (Mode.CBC == mode) {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"),
|
||||
new IvParameterSpec(aesKey.getBytes()));
|
||||
} else {
|
||||
aes = new AES(mode, padding,
|
||||
new SecretKeySpec(ENCODE_KEY.getBytes(), "AES"));
|
||||
}
|
||||
byte[] decryptDataBase64 = aes.decrypt(data);
|
||||
return new String(decryptDataBase64, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decryptFromStringRSA(RSA rsa, String encryptData) {
|
||||
//对byte数组进行解密
|
||||
byte[] decrypt = rsa.decrypt(Base64.decodeBase64(encryptData), KeyType.PrivateKey);
|
||||
return new String(decrypt, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decryptFromStringRSAPublicKey(RSA rsa, String encryptData) {
|
||||
//对byte数组进行解密
|
||||
byte[] decrypt = rsa.decrypt(Base64.decodeBase64(encryptData), KeyType.PublicKey);
|
||||
return new String(decrypt, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.itheima.sfbx.config.SecurityConfig
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.itheima.sfbx;
|
||||
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.itheima.sfbx.auth.BoleeEncryptor;
|
||||
import com.itheima.sfbx.auth.PrivateKeySigner;
|
||||
import com.itheima.sfbx.auth.PublicKeyVerifier;
|
||||
import com.itheima.sfbx.constants.BoleeSecurityConstant;
|
||||
import com.itheima.sfbx.dto.EncodeDTO;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName BoleeHttpClientTest.java
|
||||
* @Description TODO
|
||||
*/
|
||||
public class BoleeHttpClientTest {
|
||||
|
||||
private static String privateKeyBase64 = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAILdGdmrbwdn0tkD5I1RYB/whGEUsAOYprsy/F0cCdRFcUfNT6Q4BcErwcJAmdXPDuSYSbPsImKA0EbLN+pGh/wwlrYtBueItE4zGV2GdMNhAU3P6CquIAm5H58UFeDmVEUnE81laylXjaiaZduXBVTiLgTPVcpSxus70ZJQQirBAgMBAAECgYAg0sBHHn7MxrfWAunyoDSSDkvF5eB4JnO7hIBUAlJc0cYmElMlh3+6AfWpeXaccED2CVSDMnk1Z8XV2+b8dhBpTo3IHG+IQwZvz89bfMcwhhpRT9eXpC5YkG5UHlh11Ftm7byAo7s0HG/JynquxWZDRifWnvrA2bYE2sk6oN2zuQJBAMxlxcDDcSIdsZbXH7zTFJ6c0WEYlsXndRJhT1OfmxzwNitN+8PicbyJh6GfDcM+PR+13rqGZ4x+yGyKSjsp9XMCQQCj5tRLINjRUcNalPrJnvvUMhlJ0yrxsKReX81L1lwDXjbFpcMsy9eZddgdLjGXRgH7K9tYmLGZ6hflRGTcbrH7AkEAt/HDFOYOU1iLsKbrDgCcJt4T5CC/11ykVCU0wZn6ewGGjlRBBhksqDLQ19ePCC1jzrzas9wvJhYXAu81PKdXFwJAUftq4u1aJlFcgtmUG/efBUPN7GRozZ3Kib4nxTBCtBiTEwfX+Xc4r3UHlYj+mykUYptMSyONamxyaWZtgOkJswJBAMLjyUR61F4UPjoyGGg5G4eRTM3fJ1w7ulHYYxlEy5u2UAC/jBtd07wv030lsqPSA7abTdK+6iNM2v0gA7KODKk=";
|
||||
|
||||
private static String publicKeyBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCC3RnZq28HZ9LZA+SNUWAf8IRhFLADmKa7MvxdHAnURXFHzU+kOAXBK8HCQJnVzw7kmEmz7CJigNBGyzfqRof8MJa2LQbniLROMxldhnTDYQFNz+gqriAJuR+fFBXg5lRFJxPNZWspV42ommXblwVU4i4Ez1XKUsbrO9GSUEIqwQIDAQAB";
|
||||
|
||||
@Test
|
||||
public void testTemplate() throws URISyntaxException, IOException {
|
||||
URIBuilder urlBuilder = new URIBuilder()
|
||||
.setScheme("http")
|
||||
.setHost("localhost:8080")
|
||||
.setPath("/insure");
|
||||
RequestTemplate requestTemplate = RequestTemplate.builder()
|
||||
.appId("10001")
|
||||
.privateKey(privateKeyBase64)
|
||||
.publicKey(publicKeyBase64)
|
||||
.uriBuilder(urlBuilder)
|
||||
.build();
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("name","张三");
|
||||
params.put("age","18");
|
||||
params.put("sex","男");
|
||||
Map map = requestTemplate.doRequest(params, Map.class);
|
||||
System.out.println(map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void BoleeHttpClientBuilderTest(){
|
||||
CloseableHttpClient httpclient = BoleeHttpClientBuilder.create()
|
||||
.withCredentials("10001", privateKeyBase64)
|
||||
.withValidator(publicKeyBase64)
|
||||
.build();
|
||||
//=============基础配置===================
|
||||
URIBuilder urlBuilder = new URIBuilder()
|
||||
.setScheme("http")
|
||||
.setHost("localhost:8080")
|
||||
.setPath("/insure");
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setSocketTimeout(1000)
|
||||
.setConnectTimeout(1000)
|
||||
.build();
|
||||
try {
|
||||
URI url = urlBuilder.build();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(requestConfig);
|
||||
EncodeDTO encodeDTO = new EncodeDTO();
|
||||
JSONObject body = JSONUtil.parseObj(new HashMap<>() {
|
||||
{
|
||||
put("age", 19);
|
||||
}
|
||||
});
|
||||
encodeDTO.setBody(body.toString());
|
||||
if(new BoleeEncryptor(privateKeyBase64).encode(encodeDTO)){
|
||||
StringEntity stringEntity = new StringEntity(encodeDTO.getEncodeBody(), "utf-8");
|
||||
httpPost.setEntity(stringEntity);
|
||||
httpPost.addHeader(BoleeSecurityConstant.HEAD_NAME_BODY_KEY,encodeDTO.getHeadAESSecurityKey());
|
||||
httpPost.addHeader("Content-Type","application/json;charset=utf-8");
|
||||
CloseableHttpResponse apiRes = httpclient.execute(httpPost);
|
||||
HttpEntity entity = apiRes.getEntity();
|
||||
String content = EntityUtils.toString(entity);
|
||||
System.out.println("接收到了返回信息:"+content);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user