上一篇博文写的是sign签名,但是写的是form表单格式的数据,这篇写的是json格式
/**
* @param servletRequest
* @param servletResponse
* @return void
* @Description: 验证sign签名
* @Author Zangdy
* @CreateTime 2019/4/22 12:57
*/
private boolean verifySign(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
//转换成代理类 从而解决以流的方式读取,但是读取完毕之后controller里的参数就为空的问题
RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
ResponseWrapper wrapperResponse = new ResponseWrapper(httpServletResponse);
// 签名
String appKey = httpServletRequest.getParameter("appKey");
String timeStamp = httpServletRequest.getParameter("timeStamp");
String sign = httpServletRequest.getParameter("sign");
// 判断参数是否为空
if (StringUtils.isBlank(appKey) || StringUtils.isBlank(timeStamp) || StringUtils.isBlank(sign)) {
return false;
} else {
StringBuilder signSb = new StringBuilder();
// 以流的方式读取,但是读取完毕之后controller里的参数就为空了,所以需要用代理类去包装流来获取参数
if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
// 通过包装流获取参数, 并将字符串转成json对象
JSONObject bodyJObject = JSONObject.parseObject(requestWrapper.getBody());
for (String paraName : bodyJObject.keySet()) {
if (!"sign".equals(paraName)) {
signSb.append(paraName).append("=").append(bodyJObject.get(paraName)).append("&");
}
}
}
// 获取拼接后的签名
String signNew = signSb.substring(0, signSb.length() - 1);
String[] signArr = signNew.split("&");
// 将参数排序
Arrays.sort(signArr);
// 数组转字符串
signNew = arrToString(signArr);
signNew = signNew.replace("=", "");
// 拼接securityKey(自己定义的私钥)
signNew += securityKeyLocal;
// 签名不合规
if (!sign.equals(SecuritySHATool.shaEncrypt(signNew))) {
return false;
}
// 获取当前时间毫秒值
long time = System.currentTimeMillis();
try {
// 一次请求签名是否在有效时间内(时间自定义)
if ((time - timeStampToDateTime(timeStamp)) > signTime) {
return false;
}
} catch (ParseException e) {
e.printStackTrace();
}
}
return true;
}
/**
* @param objects
* @return java.lang.String
* @Description: 数组转字符串
* @Author Zangdy
* @CreateTime 2019/4/22 10:18
*/
public static String arrToString(Object[] objects) {
StringBuilder sb = new StringBuilder();
for (Object object : objects) {
sb.append(object);
}
return sb.toString();
}
/**
* @param timeStamp
* @return java.lang.Long
* @Description: 将时间戳转为毫秒值 yyyyMMddHHmmss
* @Author Zangdy
* @CreateTime 2019/4/22 11:42
*/
public static Long timeStampToDateTime(String timeStamp) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
return sdf.parse(timeStamp).getTime();
}
package com.sbkj.car.common;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;
/**
* @Description: SHA 和 MD5工具类
* @Author: 臧东运
* @CreateTime: 2019/4/22 14:10
*/
public class SecuritySHATool {
public static final String KEY_SHA = "SHA";
public static final String KEY_MD5 = "MD5";
/**
* MD5加密字节
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptMD5(byte[] data) throws Exception {
MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
md5.update(data);
return md5.digest();
}
/**
* SHA加密字节
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptSHA(byte[] data) throws Exception {
MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
sha.update(data);
return sha.digest();
}
/**
* SHA加密
*
* @param inputStr
* @return
*/
public static String shaEncrypt(String inputStr) {
byte[] inputData = inputStr.getBytes();
String returnString = "";
try {
inputData = encryptSHA(inputData);
for (int i = 0; i < inputData.length; i++) {
returnString += byteToHexString(inputData[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
return returnString;
}
private static String byteToHexString(byte ib) {
char[] digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
'b', 'c', 'd', 'e', 'f'};
char[] ob = new char[2];
ob[0] = digit[(ib >>> 4) & 0X0F];
ob[1] = digit[ib & 0X0F];
String s = new String(ob);
return s;
}
/**
* MD5加密
*
* @param inputStr
* @return
*/
public static String md5Encrypt(String inputStr) {
byte[] inputData = inputStr.getBytes();
String returnString = "";
try {
BigInteger md5 = new BigInteger(encryptMD5(inputData));
returnString = md5.toString(16);
} catch (Exception e) {
e.printStackTrace();
}
return returnString;
}
public static String dataDecrypt(Map<String, Object> serviceParams) {
StringBuilder sb = new StringBuilder();
Object[] keys = serviceParams.keySet().toArray();
Arrays.sort(keys);
for (Object key : keys) {
sb.append(key).append(serviceParams.get(key));
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(shaEncrypt("username111password111"));
System.out.println(md5Encrypt("111"));
}
}
以下就是对应的包装类,可直接复制使用
package com.shuli.filter;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}
package com.shuli.filter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer;
private ServletOutputStream out;
public ResponseWrapper(HttpServletResponse httpServletResponse) {
super(httpServletResponse);
buffer = new ByteArrayOutputStream();
out = new WrapperOutputStream(buffer);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
}
public byte[] getContent() throws IOException {
flushBuffer();
return buffer.toByteArray();
}
}
package com.shuli.filter;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class WrapperOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos;
public WrapperOutputStream(ByteArrayOutputStream bos) {
this.bos = bos;
}
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener arg0) {
// 无
}
}
细心的同志应该能发现我在最上面的注释中写了,只能读取一次,因为我的签名验证是在filter中做的,所以filter中读取完了之后controller就获取不到了。因此解决办法就是需要搞一个包装类,用来供我们去获取流。
如果发现什么问题请留言,毕竟代码都是人写的难免会出错。