java验证签名_简单API接⼝签名验证
前⾔
后端在写对外的API接⼝时,⼀般会对参数进⾏签名来保证接⼝的安全性,在设计签名算法的时候,主要考虑的是这⼏个问题:
1. 请求的来源是否合法
2. 请求参数是否被篡改
3. 请求的唯⼀性
我们的签名加密也是主要针对这⼏个问题来实现
设计
基于上述的⼏个问题,我们来通过已下步骤来实现签名加密:
1. 通过分配给APP对应的app_key和app_secret来验证⾝份
2. 通过将请求的所有参数按照字母先后顺序排序后拼接再MD5加密⽼保证请求参数不被篡改
3. 请求⾥携带时间戳参数⽼保证请求的唯⼀和过期,重复的请求在指定时间(可配置)内有效
实现
签名⽣成:
⽣成当前时间戳timestamp=now
按照请求参数名的字母升序排列⾮空请求参数(包含accessKey)
stringA="AccessKey=access&home=world&name=hello&work=java×tamp=now&nonce=random";
拼接密钥accessSecret
stringSignTemp="AccessKey=access&home=world&name=hello&work=java×tamp=now&nonce=random&accessSecret=secr
MD5并转换为⼤写⽣成签名 sign=MD5(stringSignTemp).toUpperCase();
JAVA代码如下:params是从request⾥⾯获取的所有参数map,accessSecret是加密密钥
private String createSign(Map params, String accessSecret) throws UnsupportedEncodingException {
Set keysSet = params.keySet();
Object[] keys = Array();
Arrays.sort(keys);
StringBuilder temp = new StringBuilder();
boolean first = true;
for (Object key : keys) {
if (first) {
first = false;
} else {
temp.append("&");
}
temp.append(key).append("=");
Object value = (key);
String valueString = "";
if (null != value) {
valueString = String.valueOf(value);
}
temp.append(valueString);
}
temp.append("&").append(ACCESS_SECRET).append("=").append(accessSecret);
return MD5Util.String()).toUpperCase();
}
签名校验:
参数格式校验
超时校验
验证签名
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Map result = new HashMap();
String timestamp = Parameter(TIMESTAMP_KEY);
String accessKey = Parameter(ACCESS_KEY);
String accessSecret = (accessKey);
if (!org.apachemons.lang.StringUtils.isNumeric(timestamp)) {
result.put("code", 1000);
result.put("msg", "请求时间戳不合法");
WebUtils.writeJsonByObj(result, response, request);
return false;
}
// 检查KEY是否合理
if (StringUtils.isEmpty(accessKey) || StringUtils.isEmpty(accessSecret)) {
result.put("code", 1001);
result.put("msg", "加密KEY不合法");
WebUtils.writeJsonByObj(result, response, request);java源代码加密
return false;
}
Long ts = Long.valueOf(timestamp);
// 禁⽌超时签名
if (System.currentTimeMillis() - ts > SIGN_EXPIRED_TIME) {
result.put("code", 1002);
result.put("msg", "请求超时");
WebUtils.writeJsonByObj(result, response, request);
return false;
}
if (!verificationSign(request, accessKey, accessSecret)) {
result.put("code", 1003);
result.put("msg", "签名错误");
WebUtils.writeJsonByObj(result, response, request);
return false;
}
return true;
}
校验签名:
private boolean verificationSign(HttpServletRequest request, String accessKey, String accessSecret) throws UnsupportedEncodingException {
Enumeration> pNames = ParameterNames();
Map params = new HashMap();
while (pNames.hasMoreElements()) {
String pName = (String) Element();
if (SIGN_KEY.equals(pName)) continue;
Object pValue = Parameter(pName);
params.put(pName, pValue);
}
String originSign = Parameter(SIGN_KEY);
String sign = createSign(params, accessSecret);
return sign.equals(originSign);
}
完整代码:
这⾥通过来实现接⼝拦截,可⾃⾏替换
package pmon.web.interceptor;
import onf.ZKClient;
import pmon.web.util.MD5Util;
import pmon.web.util.WebUtils;
import keeper.KeeperException;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.*;
import urrent.ConcurrentHashMap;
/**
* Author: Kelin
* Date: 2018/5/16
* Description:
*/
@SuppressWarnings("SuspiciousMethodCalls")
public class SimpleApiSignInterceptor extends HandlerInterceptorAdapter {
// 签名超时时长,默认时间为5分钟,ms
private static final int SIGN_EXPIRED_TIME = 5 * 60 * 1000;
private static final String API_SIGN_KEY_CONFIG_PATH = "/mop/common/system/api_sign_key_mapping.properties"; private static final String SIGN_KEY = "sign";
private static final String TIMESTAMP_KEY = "timestamp";
private static final String ACCESS_KEY = "accessKey";
private static final String ACCESS_SECRET = "accessSecret";
private static Map map = new ConcurrentHashMap();
static {
// 从zk加载key映射到内存⾥⾯
try {
String data = ().getStringData(API_SIGN_KEY_CONFIG_PATH);
Properties properties = new Properties();
properties.load(new StringReader(data));
for (Object key : properties.keySet()) {
map.put(String.valueOf(key), Property(String.valueOf(key)));
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Map result = new HashMap();
String timestamp = Parameter(TIMESTAMP_KEY);
String accessKey = Parameter(ACCESS_KEY);
String accessSecret = (accessKey);
if (!org.apachemons.lang.StringUtils.isNumeric(timestamp)) {
result.put("code", 1000);
result.put("msg", "请求时间戳不合法");
WebUtils.writeJsonByObj(result, response, request);
return false;
}
// 检查KEY是否合理
if (StringUtils.isEmpty(accessKey) || StringUtils.isEmpty(accessSecret)) {
result.put("code", 1001);
result.put("msg", "加密KEY不合法");
WebUtils.writeJsonByObj(result, response, request);
return false;
}
Long ts = Long.valueOf(timestamp);
// 禁⽌超时签名
if (System.currentTimeMillis() - ts > SIGN_EXPIRED_TIME) {
result.put("code", 1002);
result.put("msg", "请求超时");
WebUtils.writeJsonByObj(result, response, request);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论