做接口开发首先需要注意的是接口的安全性,接口的安全性可以用Https来做或者直接在服务器层面设置ip白名单,我用的是sign签名来确保接口的安全性。
sign签名是根据用户请求的参数和值(不包括sign),结合分配给客户端的秘钥(securityKey),通过算法生成的签名。在算签名时,首先要按照每组的key=value的字母顺序做排序(升序降序自己拿主意),然后对key和vaule(使用utf-8编码,特别注意包含中文的参数值的编码)连接成没有等于号的字符串;然后在最后加上客户端的秘钥(securityKey)然后通过算法生成签名。
在此需要注意,前端必传的字段有:
appkey(String类型,必填,调用者第三方公钥)
timeStamp(String类型,必填,当前时间戳 格式(自己拿主意,只要前后端一致就行))
sign(String类型,必填,(参数+必传参数+私钥(securityKey,))的算法加密串)
假如参数有:
String appkey=“abcd”
String timeStamp=“20190422125727”
String aaa=“aaa”
String bbb=“bbb”
该客户端的秘钥是securityKey=1234567890
按照key=value升序排序是
aaa=aaaappkey=abcdbbb=bbbtimeStamp=20190422125727
连接成没有等于号的字符串
aaaaaaappkeyabcdbbbbbbtimeStamp20190422125727
拼接客户端秘钥
aaaaaaappkeyabcdbbbbbbtimeStamp201904221257271234567890
然后使用加密算法加密
sign=(aaaaaaappkeyabcdbbbbbbtimeStamp201904221257271234567890)加密后的串
在贴代码之前需要和大家说的问题是,post请求获取参数是可能是form表单格式或者是json格式,不同的格式获取参数的方式也是不同的,这一片博文先用post请求的form表单格式写,下一篇博文再介绍json格式。
/**
* @param httpServletRequest
* @param httpServletResponse
* @return void
* @Description: 验证sign签名
* @Author Zangdy
* @CreateTime 2019/4/22 12:57
*/
private boolean verifySign(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
// 签名
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)) {
DyTool.responseWriter(StatusEnum.APPTIMESIGNISNULL, httpServletResponse); // (工具类)
return false;
} else {
StringBuilder signSb = new StringBuilder();
// 获取url里的请求参数
Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
for (String paraName : parameterMap.keySet()) {
if (!"sign".equals(paraName)) { // 拼接参数
signSb.append(paraName).append("=").append(parameterMap.get(paraName)[0]).append("&");
}
}
String signNew = signSb.substring(0, signSb.length() - 1); // 获取拼接后的参数
String[] signArr = signNew.split("&");
Arrays.sort(signArr); // 将参数排序
signNew = DyTool.arrToString(signArr); // (工具类)数组转字符串
signNew = signNew.replace("=", "");
signNew += securityKeyLocal; // 拼接securityKey
if (!sign.equals(SecuritySHATool.shaEncrypt(signNew))) {
DyTool.responseWriter(StatusEnum.SIGNUNTRUE, httpServletResponse); // (工具类)
return false;
}
long time = new Date().getTime(); // 获取当前时间毫秒值
try {
//DyTool.timeStampToDateTime 工具类,将yyyyMMddHHmmss转成毫秒值
if ((time - DyTool.timeStampToDateTime(timeStamp)) > (1000 * 60 * 5)) { // 一次请求签名在五分钟内
DyTool.responseWriter(StatusEnum.SIGNOUTTIME, httpServletResponse); // (工具类)
return false;
}
} catch (ParseException e) {
e.printStackTrace();
}
}
return true;
}
/**
* @param timeStamp yyyyMMddHHmmss格式时间戳
* @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();
}
/**
* @param statusEnum 状态枚举类
* @param response
* @return java.lang.String
* @Description: 发生错误时响应给请求者, 拦截器过滤器层面,需要HttpServletResponse
* @Author Zangdy
* @CreateTime 2019/4/22 11:09
*/
public static void responseWriter(StatusEnum statusEnum, HttpServletResponse response) throws IOException {
ResponseBody responseBody = new ResponseBody();
responseBody.setStatus(statusEnum.getStatus());
responseBody.setMessage(statusEnum.getMessage());
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(JSONObject.toJSONString(responseBody));
}
/**
* @Description: 状态枚举类
* @Author: 臧东运
* @CreateTime: 2019/4/22 10:53
*/
public enum StatusEnum {
UNLOGIN("0000","未登录"),
/**
* 状态 02 开头为成功
* */
LOGINSUCCESS("0201","登陆成功"),
LOGOUTSUCCESS("0202","注销成功"),
UPLOADSUCCESS("0203","文件上传成功"),
/**
* 状态 04 开头为失败
*/
IPBLOCKED("0400","因非法请求,已被禁止访问,请稍后重试!"),
ACCESSDENIED("0401","无权限访问"),
USERNAMEISNULL("0402","用户名不能为空!"),
PASSWORDISNULL("0402","密码不能为空!"),
USERNAMEERROR("0402","用户名错误!"),
PASSWORDERROR("0402","密码错误!"),
NOHEADER("0403","缺少头信息"),
TOKENOUTTIME("0404","token认证已过期"),
APPTIMESIGNISNULL("0405","sign认证参数为空"),
SIGNUNTRUE("0406","sign参数不正确"),
SIGNOUTTIME("0407","sign签名已过期"),
ERROR("0499","服务器异常,请稍后重试。如有问题,请联系管理员"),
CUSTOM("0500","自定义异常");
private String status;
private String message;
StatusEnum(String status, String message) {
this.status = status;
this.message = message;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
/**
* @Description: 加密算法工具类
* @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"));
}
如果发现什么问题请留言,毕竟代码都是人写的难免会出错。