Commit 549330dd authored by 夏敏伟's avatar 夏敏伟

Merge branch 'master' of http://192.168.168.218/wcyuee/szpt

parents 92a11966 7be11259
package com.zksy.szpt.aspect;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zksy.szpt.domain.CommonRequestDTO;
import com.zksy.szpt.util.EncryptUtil;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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.RequestBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
@RestControllerAdvice(basePackages = "com.zksy.szpt.controller")
@ConditionalOnBean(name = "signatureVerificationFilter2")
public class RequestBodyAnalysis implements RequestBodyAdvice {
@Resource
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return methodParameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
return new HttpInputMessage() {
@Override
public HttpHeaders getHeaders() {
return httpInputMessage.getHeaders();
}
@Override
public InputStream getBody() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(httpInputMessage.getBody(), baos);
CommonRequestDTO commonRequestDTO = objectMapper.readValue(new String(baos.toByteArray(), StandardCharsets.UTF_8), CommonRequestDTO.class);
String content = EncryptUtil.getInstance().AESDecode(commonRequestDTO.getContent(), "c81e728d9d4c2f636f067f89cc14862c");//todo
return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
}
};
}
@Override
public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return o;
}
@Override
public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return o;
}
}
package com.zksy.szpt.config;
import com.zksy.szpt.filter.SignatureVerificationFilter;
import com.zksy.szpt.filter.SignatureVerificationFilter1;
import com.zksy.szpt.filter.SignatureVerificationFilter2;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Component
public class WebConfig implements WebMvcConfigurer {
......@@ -55,6 +55,7 @@ public class WebConfig implements WebMvcConfigurer {
* @return
*/
@Bean
@ConditionalOnBean(name = "signatureVerificationFilter")
public FilterRegistrationBean getFilter1Registration(@Qualifier("signatureVerificationFilter") SignatureVerificationFilter signatureVerificationFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(signatureVerificationFilter);
......@@ -66,4 +67,29 @@ public class WebConfig implements WebMvcConfigurer {
return filterRegistrationBean;
}
@Bean
@ConditionalOnBean(name = "signatureVerificationFilter1")
public FilterRegistrationBean getFilter1Registration(@Qualifier("signatureVerificationFilter1") SignatureVerificationFilter1 signatureVerificationFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(signatureVerificationFilter);
//设置过滤器名称和路径,在过滤器类写了的话,这里不用重复写
filterRegistrationBean.setName("filter");
filterRegistrationBean.addUrlPatterns("/rest/*");
//设置过滤器执行顺序,数字越小,越早进行过滤,也可设置为负数
filterRegistrationBean.setOrder(1);
return filterRegistrationBean;
}
@Bean
@ConditionalOnBean(name = "signatureVerificationFilter2")
public FilterRegistrationBean getFilter1Registration(@Qualifier("signatureVerificationFilter2") SignatureVerificationFilter2 signatureVerificationFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(signatureVerificationFilter);
//设置过滤器名称和路径,在过滤器类写了的话,这里不用重复写
filterRegistrationBean.setName("filter");
filterRegistrationBean.addUrlPatterns("/rest/*");
//设置过滤器执行顺序,数字越小,越早进行过滤,也可设置为负数
filterRegistrationBean.setOrder(1);
return filterRegistrationBean;
}
}
package com.zksy.szpt.domain;
public class CommonRequestDTO {
private String deptCode;
private String content;
private String appId;
private String nonce;
private String timestamp;
private String sign;
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getNonce() {
return nonce;
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getDeptCode() {
return deptCode;
}
public void setDeptCode(String deptCode) {
this.deptCode = deptCode;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
......@@ -6,9 +6,6 @@ public class CurrentUserInfo {
private String deptCode;
private String ip;
public CurrentUserInfo() {
}
private CurrentUserInfo(CurrentUserInfoBuilder builder) {
this.userId = builder.userId;
this.userName = builder.userName;
......
package com.zksy.szpt.filter;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* 该类用于包装HttpServletRequest,以便在读取请求体后仍可重复读取
*/
public class ReusableBodyRequestWrapper1 extends HttpServletRequestWrapper {
//参数字节数组,用于存储请求体的字节数据
private byte[] requestBody;
/**
* 构造函数,初始化包装类
* @param request 原始HttpServletRequest对象
* @throws IOException 如果读取请求体时发生IO错误
*/
public ReusableBodyRequestWrapper1(HttpServletRequest request, String body) throws IOException {
super(request);
this.requestBody = body.getBytes(StandardCharsets.UTF_8);
}
// 构造方法:从请求中读取 body 并缓存
public ReusableBodyRequestWrapper1(HttpServletRequest request) throws IOException {
super(request);
this.requestBody = request.getReader().lines()
.reduce("", (accumulator, actual) -> accumulator + actual)
.getBytes(StandardCharsets.UTF_8);
}
/**
* 重写getInputStream方法,实现请求体的重复读取
* @return 包含请求体数据的ServletInputStream对象
* @throws IOException 如果读取请求体时发生IO错误
*/
@Override
public ServletInputStream getInputStream() throws IOException {
//创建一个 ByteArrayInputStream 对象,用于重复读取requestBody
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public boolean isFinished() {
//始终返回false,表示数据流未完成
return false;
}
@Override
public boolean isReady() {
//始终返回false,表示数据流未准备好
return false;
}
@Override
public void setReadListener(ReadListener listener) {
//不执行任何操作,因为该数据流不支持异步操作
}
@Override
public int read() {
//从ByteArrayInputStream中读取数据
return bais.read();
}
};
}
/**
* 重写getReader方法,返回一个基于getInputStream的BufferedReader
* @return 包含请求体数据的BufferedReader对象
* @throws IOException 如果读取请求体时发生IO错误
*/
@Override
public BufferedReader getReader() throws IOException {
//基于getInputStream创建BufferedReader
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
// 获取请求体的字符串
public String getBody() {
return new String(requestBody, StandardCharsets.UTF_8);
}
}
\ No newline at end of file
......@@ -33,6 +33,9 @@ import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* body {单位, 房间, 人员信息数组} 请求头加nonce timestamp appId sign(body[遍历按照key按照顺序&拼接] 加 nonece timestamp appId 用$符号拼接)
*/
@Component
public class SignatureVerificationFilter extends OncePerRequestFilter {
......@@ -171,9 +174,5 @@ public class SignatureVerificationFilter extends OncePerRequestFilter {
CurrentUserInfo currentUserInfo = new CurrentUserInfo.CurrentUserInfoBuilder().userId(userId).userName(userId).ip(ip).build();
UserContextHolder.set(currentUserInfo);
}
private void checkDeptCode(String deptCode, AppStore appStore) {
}
}
package com.zksy.szpt.filter;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zksy.szpt.domain.CommonRequestDTO;
import com.zksy.szpt.domain.CurrentUserInfo;
import com.zksy.szpt.domain.HttpResult;
import com.zksy.szpt.domain.HttpResultState;
import com.zksy.szpt.domain.po.AppStore;
import com.zksy.szpt.service.AppStoreService;
import com.zksy.szpt.util.EncryptUtil;
import com.zksy.szpt.util.RedisKeyValidator;
import com.zksy.szpt.util.SignatureUtil;
import com.zksy.szpt.util.UserContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
/**
* 请求body { appkey , method , hotelCode , content(根据密钥[md5]对dto的json加密aes), sign(前面的参数一起哈希运算)} 。 无其他参数
* 数据获取到json解密后转发到对应的controller
*/
//@Component(value = "signatureVerificationFilter1")
public class SignatureVerificationFilter1 extends OncePerRequestFilter {
public Logger logger = LoggerFactory.getLogger(SignatureVerificationFilter1.class);
@Resource
ObjectMapper objectMapper;
AppStore appStore;
@Resource
private RedisTemplate<String, Object> redisTemplate;
private final AppStoreService appStoreService;
public SignatureVerificationFilter1(AppStoreService appStoreService) {
this.appStoreService = appStoreService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 对request进行包装,支持重复读取body
ReusableBodyRequestWrapper1 requestWrapper = new ReusableBodyRequestWrapper1(request);
String body = requestWrapper.getBody();
String result = this.verifySignature(body);
// 校验签名
if (result != null) {
this.write(response, result);
} else {
CommonRequestDTO commonRequestDTO = objectMapper.readValue(body, CommonRequestDTO.class);
String content = EncryptUtil.getInstance().AESDecode(commonRequestDTO.getContent(), appStore.getAppSecret());
ReusableBodyRequestWrapper1 requestWrapper1 = new ReusableBodyRequestWrapper1(request, content);
addUserInfo(requestWrapper1);
filterChain.doFilter(requestWrapper1, response);
}
}
/**
* 校验签名
*
* @return 签名验证结果
* @throws IOException 如果读取请求体失败
*/
public String verifySignature(String body) throws IOException {
// post请求body
CommonRequestDTO commonRequestDTO = objectMapper.readValue(body, CommonRequestDTO.class);
// 签名
String appId = commonRequestDTO.getAppId();
// 签名
String sign = commonRequestDTO.getSign();
// 随机数
String nonce = commonRequestDTO.getNonce();
// 时间戳
String timestampStr = commonRequestDTO.getTimestamp();
String deptCode = commonRequestDTO.getDeptCode();
if (!StringUtils.hasText(appId) || !StringUtils.hasText(sign) || !StringUtils.hasText(nonce) || !StringUtils.hasText(timestampStr)) {
logger.warn("缺少参数appId:{},sign:{},nonce{},timestampStr:{}", appId, sign, nonce, timestampStr);
return "请求缺少参数,appId:" + appId + ",sign:" + sign + ",nonce:" + nonce + ",timestamp:" + timestampStr;
}
// 验证nonce和timestamp合法性
if (!RedisKeyValidator.isValidString(nonce)) {
return "不是合法的由数字和字母以及下划线组成的nonce:" + nonce;
}
if (!RedisKeyValidator.isValidTimestamp(timestampStr)) {
return "不是合法的十位秒级时间戳timestamp:" + timestampStr;
}
// timestamp 10分钟内有效
long timestamp = Long.parseLong(timestampStr);
long currentTimestamp = System.currentTimeMillis() / 1000;
if (Math.abs(currentTimestamp - timestamp) > 600) {
return "请求已过期";
}
// 防止请求重放,nonce只能用一次,放在redis中,有效期 20分钟
String nonceKey = "api_signature:nonce:" + nonce;
if (Boolean.FALSE.equals(this.redisTemplate.opsForValue().setIfAbsent(nonceKey, "1", 20, TimeUnit.MINUTES))) {
return "nonce无效:" + nonce;
}
// 校验appId
appStore = this.appStoreService.getAppSecretInfo(appId);
String appSecret = appStore.getAppSecret();
if (!StringUtils.hasText(appSecret)) {
return "appId无效:" + appId;
}
//验证单位
if (deptCode != null && !deptCode.startsWith(appStore.getDeptCode().replaceAll("0+$", ""))) {
return "AppId和传入的单位不匹配,appId:" + appId + ",deptCode:" + deptCode;
}
String data = String.format("%s%s%s%s%s", appId, nonce, timestampStr, commonRequestDTO.getContent(), deptCode);
String generatedSignature = DigestUtil.md5Hex(data);
if (!generatedSignature.equals(sign)) {
logger.warn("签名有误,generatedSignature:{},sign:{},appId:{},nonce:{},timestampStr:{},deptCode:{}", generatedSignature, sign, appId, nonce, timestampStr, deptCode);
return "签名有误,sign: " + sign + ",appId:" + appId + ",nonce:" + nonce + ",timestamp:" + timestampStr + ",deptCode:" + deptCode;
}
// 签名验证通过
return null;
}
/**
* 向客户端写入响应信息
*
* @param response HTTP响应
* @param msg 响应信息
* @throws IOException 如果写入失败
*/
private void write(HttpServletResponse response, String msg) throws IOException {
HttpResultState httpResultState = HttpResultState.INVALID_SIGNATURE;
httpResultState.setMessage(msg);
HttpResult<String> httpResult = new HttpResult<>(httpResultState);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.getWriter().write(JSONUtil.toJsonStr(httpResult));
}
/**
* 增加用户信息
*
* @param request
*/
private void addUserInfo(HttpServletRequest request) {
// 增加用户信息
String userId = request.getHeader(SignatureUtil.APPID);
String ip = request.getRemoteAddr();
CurrentUserInfo currentUserInfo = new CurrentUserInfo.CurrentUserInfoBuilder().userId(userId).userName(userId).ip(ip).build();
UserContextHolder.set(currentUserInfo);
}
}
package com.zksy.szpt.filter;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zksy.szpt.domain.CommonRequestDTO;
import com.zksy.szpt.domain.CurrentUserInfo;
import com.zksy.szpt.domain.HttpResult;
import com.zksy.szpt.domain.HttpResultState;
import com.zksy.szpt.domain.po.AppStore;
import com.zksy.szpt.service.AppStoreService;
import com.zksy.szpt.util.RedisKeyValidator;
import com.zksy.szpt.util.SignatureUtil;
import com.zksy.szpt.util.UserContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
/**
* 请求body { appkey , method , hotelCode , content(根据密钥[md5]对dto的json加密aes), sign(前面的参数一起哈希运算)} 。 无其他参数
* 请求数据直接转发到后端服务,用requestbodyanalysis进行解密
*/
//@Component(value = "signatureVerificationFilter2")
public class SignatureVerificationFilter2 extends OncePerRequestFilter {
public Logger logger = LoggerFactory.getLogger(SignatureVerificationFilter2.class);
@Resource
ObjectMapper objectMapper;
AppStore appStore;
@Resource
private RedisTemplate<String, Object> redisTemplate;
private final AppStoreService appStoreService;
public SignatureVerificationFilter2(AppStoreService appStoreService) {
this.appStoreService = appStoreService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 对request进行包装,支持重复读取body
ReusableBodyRequestWrapper1 requestWrapper = new ReusableBodyRequestWrapper1(request);
String body = requestWrapper.getBody();
String result = this.verifySignature(body);
// 校验签名
if (result != null) {
this.write(response, result);
} else {
addUserInfo(request);
filterChain.doFilter(requestWrapper, response);
}
}
/**
* 校验签名
*
* @return 签名验证结果
* @throws IOException 如果读取请求体失败
*/
public String verifySignature(String body) throws IOException {
// post请求body
CommonRequestDTO commonRequestDTO = objectMapper.readValue(body, CommonRequestDTO.class);
// 签名
String appId = commonRequestDTO.getAppId();
// 签名
String sign = commonRequestDTO.getSign();
// 随机数
String nonce = commonRequestDTO.getNonce();
// 时间戳
String timestampStr = commonRequestDTO.getTimestamp();
String deptCode = commonRequestDTO.getDeptCode();
if (!StringUtils.hasText(appId) || !StringUtils.hasText(sign) || !StringUtils.hasText(nonce) || !StringUtils.hasText(timestampStr)) {
logger.warn("缺少参数appId:{},sign:{},nonce{},timestampStr:{}", appId, sign, nonce, timestampStr);
return "请求缺少参数,appId:" + appId + ",sign:" + sign + ",nonce:" + nonce + ",timestamp:" + timestampStr;
}
// 验证nonce和timestamp合法性
if (!RedisKeyValidator.isValidString(nonce)) {
return "不是合法的由数字和字母以及下划线组成的nonce:" + nonce;
}
if (!RedisKeyValidator.isValidTimestamp(timestampStr)) {
return "不是合法的十位秒级时间戳timestamp:" + timestampStr;
}
// timestamp 10分钟内有效
long timestamp = Long.parseLong(timestampStr);
long currentTimestamp = System.currentTimeMillis() / 1000;
if (Math.abs(currentTimestamp - timestamp) > 600) {
return "请求已过期";
}
// 防止请求重放,nonce只能用一次,放在redis中,有效期 20分钟
String nonceKey = "api_signature:nonce:" + nonce;
if (Boolean.FALSE.equals(this.redisTemplate.opsForValue().setIfAbsent(nonceKey, "1", 20, TimeUnit.MINUTES))) {
return "nonce无效:" + nonce;
}
// 校验appId
appStore = this.appStoreService.getAppSecretInfo(appId);
String appSecret = appStore.getAppSecret();
if (!StringUtils.hasText(appSecret)) {
return "appId无效:" + appId;
}
//验证单位
if (deptCode != null && !deptCode.startsWith(appStore.getDeptCode().replaceAll("0+$", ""))) {
return "AppId和传入的单位不匹配,appId:" + appId + ",deptCode:" + deptCode;
}
String data = String.format("%s%s%s%s%s", appId, nonce, timestampStr, commonRequestDTO.getContent(), deptCode);
String generatedSignature = DigestUtil.md5Hex(data);
if (!generatedSignature.equals(sign)) {
logger.warn("签名有误,generatedSignature:{},sign:{},appId:{},nonce:{},timestampStr:{},deptCode:{}", generatedSignature, sign, appId, nonce, timestampStr, deptCode);
return "签名有误,sign: " + sign + ",appId:" + appId + ",nonce:" + nonce + ",timestamp:" + timestampStr + ",deptCode:" + deptCode;
}
// 签名验证通过
return null;
}
/**
* 向客户端写入响应信息
*
* @param response HTTP响应
* @param msg 响应信息
* @throws IOException 如果写入失败
*/
private void write(HttpServletResponse response, String msg) throws IOException {
HttpResultState httpResultState = HttpResultState.INVALID_SIGNATURE;
httpResultState.setMessage(msg);
HttpResult<String> httpResult = new HttpResult<>(httpResultState);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.getWriter().write(JSONUtil.toJsonStr(httpResult));
}
/**
* 增加用户信息
*
* @param request
*/
private void addUserInfo(HttpServletRequest request) {
// 增加用户信息
String userId = request.getHeader(SignatureUtil.APPID);
String ip = request.getRemoteAddr();
CurrentUserInfo currentUserInfo = new CurrentUserInfo.CurrentUserInfoBuilder().userId(userId).userName(userId).ip(ip).build();
UserContextHolder.set(currentUserInfo);
}
}
......@@ -17,6 +17,8 @@ import java.util.List;
*/
@Component
public class SzptMetaObjectHandler implements MetaObjectHandler {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SzptMetaObjectHandler.class);
@Override
public void insertFill(MetaObject metaObject) {
// 新增时自动填充 create_time, update_time 字段
......@@ -24,6 +26,10 @@ public class SzptMetaObjectHandler implements MetaObjectHandler {
// this.strictInsertFill(metaObject, "cjsj", Date.class, new Date());
CurrentUserInfo currentUserInfo = UserContextHolder.get();
if (currentUserInfo == null) {
log.error("当前用户信息为空,无法自动填充 create_time, update_time 字段");
}
List<StrictFill> fields = Arrays.asList(
StrictFill.of("createTime", Date.class, new Date()),
StrictFill.of("cjsj", Date.class, new Date()),
......@@ -51,6 +57,9 @@ public class SzptMetaObjectHandler implements MetaObjectHandler {
@Override
public void updateFill(MetaObject metaObject) {
CurrentUserInfo currentUserInfo = UserContextHolder.get();
if (currentUserInfo == null) {
log.error("当前用户信息为空,无法自动填充 update_time 字段");
}
List<StrictFill> fields = Arrays.asList(
StrictFill.of("updateTime", Date.class, new Date()),
StrictFill.of("gxrid", String.class, "1"),
......
......@@ -151,7 +151,7 @@ public class EncryptUtil {
* @param res 需要解密的密文
* @param key 秘钥
*/
private String AESDecode(String res, String key) {
public String AESDecode(String res, String key) {
return keyGeneratorES(res, AES, key, keySizeAES, false);
}
......
package com.zksy.szpt;
import cn.hutool.crypto.digest.DigestUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zksy.szpt.domain.dto.*;
import com.zksy.szpt.util.EncryptUtil;
import com.zksy.szpt.util.SignatureUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
@SpringBootTest()
public class TestAppStore {
String nonce = "2";
String timestampStr = "21";
String appId = "1872576325743943682";
String appSecret = "2";
@Resource
private ObjectMapper objectMapper;
/**
* 新增AppId
*/
@Test
@DisplayName("新增AppId")
public void addAppId() {
timestampStr = String.valueOf(System.currentTimeMillis() / 1000);
nonce = String.valueOf(System.currentTimeMillis() / 1000);
appSecret = DigestUtil.md5Hex(appSecret);
Assertions.assertNotNull(appId, "appId不存在");//断言appId存在,为空直接抛出异常不进行下一步测试,提高测试效率
//请求参数
AppStoreDTO appStoreDTO = new AppStoreDTO();
appStoreDTO.setAppKey("us");
appStoreDTO.setAppKey("us2");
appStoreDTO.setAppSecret(DigestUtil.md5Hex("us"));
appStoreDTO.setDeptCode("123456");
String json = null;
try {
json = objectMapper.writeValueAsString(appStoreDTO);
} catch (JsonProcessingException e) {
Assertions.fail("json序列化失败");
}
//请求体加密
json = EncryptUtil.getInstance().AESEncode(json, appSecret);
//签名appId+nonce+timestampStr+aes(body)
String data = String.format("%s%s%s%s", appId, nonce, timestampStr, json);
String generatedSignature = DigestUtil.md5Hex(data);
//请求
WebClient webClient = WebClient.builder()
.baseUrl("http://localhost:8086")
.defaultHeader("Content-Type", "application/json")
.build();
String response = webClient.post().uri("/rest/appStore/insertAppStore")
.header(SignatureUtil.APPID, appId)
.header(SignatureUtil.NONCE, nonce)
.header(SignatureUtil.TIMESTAMP, timestampStr)
.header(SignatureUtil.SIGNATURE, generatedSignature)
.body(Mono.just(appStoreDTO), XxRwwcqkDTO.class)
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println(response);
TestHttpUtil.signatureAndRequest("/rest/appStore/insertAppStore", appStoreDTO, AppStoreDTO.class);
// TestHttpUtil.signatureAndRequest("/rest/appStore/insertAppStore", appStoreDTO);
}
@Test
@DisplayName("更新AppId的密钥")
public void updateAppIdSecret() {
timestampStr = String.valueOf(System.currentTimeMillis() / 1000);
nonce = String.valueOf(System.currentTimeMillis() / 1000);
appSecret = DigestUtil.md5Hex(appSecret);
Assertions.assertNotNull(appId, "appId不存在");//断言appId存在,为空直接抛出异常不进行下一步测试,提高测试效率
//请求参数
AppStoreDTO appStoreDTO = new AppStoreDTO();
appStoreDTO.setAppKey("us");
appStoreDTO.setAppSecret(DigestUtil.md5Hex("us11"));
String json = null;
try {
json = objectMapper.writeValueAsString(appStoreDTO);
} catch (JsonProcessingException e) {
Assertions.fail("json序列化失败");
}
//请求体加密
json = EncryptUtil.getInstance().AESEncode(json, appSecret);
//签名appId+nonce+timestampStr+aes(body)
String data = String.format("%s%s%s%s", appId, nonce, timestampStr, json);
String generatedSignature = DigestUtil.md5Hex(data);
//请求
WebClient webClient = WebClient.builder()
.baseUrl("http://localhost:8086")
.defaultHeader("Content-Type", "application/json")
.build();
String response = webClient.post().uri("/rest/appStore/updateAppIdSecret")
.header(SignatureUtil.APPID, appId)
.header(SignatureUtil.NONCE, nonce)
.header(SignatureUtil.TIMESTAMP, timestampStr)
.header(SignatureUtil.SIGNATURE, generatedSignature)
.body(Mono.just(appStoreDTO), XxRwwcqkDTO.class)
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println(response);
appStoreDTO.setAppKey("us2");
appStoreDTO.setAppSecret(DigestUtil.md5Hex("asdfasdfadf11"));
TestHttpUtil.signatureAndRequest("/rest/appStore/updateAppIdSecret", appStoreDTO, AppStoreDTO.class);
}
}
package com.zksy.szpt;
import cn.hutool.crypto.digest.DigestUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zksy.szpt.domain.CommonRequestDTO;
import com.zksy.szpt.util.EncryptUtil;
import com.zksy.szpt.util.SignatureUtil;
import org.junit.jupiter.api.Assertions;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class TestHttpUtil {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TestHttpUtil.class);
static String nonce = "2";
static String timestampStr = "21";
static String appId = "1872576325743943682";
static String appSecret = "2";
static String deptCode = "330102";
private static final ObjectMapper objectMapper = new ObjectMapper();
//body {单位, 房间, 人员信息数组} 请求头加nonce timestamp appId sign(body[遍历按照key按照顺序&拼接] 加 nonece timestamp appId 用$符号拼接)
public static <E, T> void signatureAndRequest(String path, T source, Class<E> destinationClass) {
Assertions.assertNotNull(path, "path不能为空");
Assertions.assertNotNull(source, "请求参数不能为空");
timestampStr = String.valueOf(System.currentTimeMillis() / 1000);
nonce = String.valueOf(System.currentTimeMillis() / 1000);
appSecret = DigestUtil.md5Hex(appSecret);
String json = null;
try {
json = objectMapper.writeValueAsString(source);
} catch (JsonProcessingException e) {
Assertions.fail("json序列化失败");
}
//请求体加密
json = EncryptUtil.getInstance().AESEncode(json, appSecret);
//签名appId+nonce+timestampStr+aes(body)
String data = String.format("%s%s%s%s", appId, nonce, timestampStr, json);
String generatedSignature = DigestUtil.md5Hex(data);
//请求
WebClient webClient = WebClient.builder()
.baseUrl("http://localhost:8086")
.defaultHeader("Content-Type", "application/json")
.build();
String response = webClient.post().uri(path)
.header(SignatureUtil.APPID, appId)
.header(SignatureUtil.NONCE, nonce)
.header(SignatureUtil.TIMESTAMP, timestampStr)
.header(SignatureUtil.SIGNATURE, generatedSignature)
.body(Mono.just(source), destinationClass)
.retrieve()
.bodyToMono(String.class)
.block();
log.info("response: {}", response);
}
//body { appkey , method , hotelCode , content(根据密钥[md5]对dto的json加密aes), sign(前面的参数一起哈希运算)} 。 无其他参数
public static <T> void signatureAndRequest(String path, T source) {
Assertions.assertNotNull(path, "path不能为空");
Assertions.assertNotNull(source, "请求参数不能为空");
timestampStr = String.valueOf(System.currentTimeMillis() / 1000);
nonce = String.valueOf(System.currentTimeMillis() / 1000);
appSecret = DigestUtil.md5Hex(appSecret);
String json = null;
try {
json = objectMapper.writeValueAsString(source);
} catch (JsonProcessingException e) {
Assertions.fail("json序列化失败");
}
//请求体加密
//请求体加密
json = EncryptUtil.getInstance().AESEncode(json, appSecret);
//签名appId+nonce+timestampStr+aes(body)+deptCode
String data = String.format("%s%s%s%s%s", appId, nonce, timestampStr, json, deptCode);
String generatedSignature = DigestUtil.md5Hex(data);
CommonRequestDTO commonRequestDTO = new CommonRequestDTO();
commonRequestDTO.setAppId(appId);
commonRequestDTO.setNonce(nonce);
commonRequestDTO.setTimestamp(timestampStr);
commonRequestDTO.setDeptCode(deptCode);
commonRequestDTO.setContent(json);
commonRequestDTO.setSign(generatedSignature);
//请求
WebClient webClient = WebClient.builder()
.baseUrl("http://localhost:8086")
.defaultHeader("Content-Type", "application/json")
.build();
String response = webClient.post().uri(path)
.body(Mono.just(commonRequestDTO), CommonRequestDTO.class)
.retrieve()
.bodyToMono(String.class)
.block();
log.info("response: {}", response);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment