感想
这是我第一次要写对外的接口,一开始还是比较迷茫的,因为要考虑到数据的安全传输,很多技术我都还是比较的不熟悉,后来经过对每一个技术的了解,终于还是写出来了。
感觉一切都是从陌生到熟悉,再到亲切 哈哈哈哈哈
如有错误,希望指出, 共同学习
技术
我会把所有的封装的工具类都给出来
1: 使用Rsa非对称的加密算法,对传输的参数进行加密 (需要一个公钥和一个私钥)
2: 使用md5 进行加签,验证身份是否正确
3:使用hutool工具中的HttpUtil进行请求
4:http://web.chacuo.net/netrsakeypair 通过这个网址可以在线生成公钥和私钥的密钥对,一般1024位就足够了,我这边就随机生成了密钥如下
公钥:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRgMigO0vUZCOXcSbylyBHssPh
4qNMwZLIM9pbAmUZldATE2QfI5XAwQuEeo8LapP9Mf+TxXpQytevYs+EYcvaExOe
BhJv9T6QFe2vxV6oJ+iDP2nMFGOqCK7+MJYWh4CYyl+PvuGnu+wMCDRGBI6u+09Z
w/RnlWP6MMv7KlvhawIDAQAB
私钥:
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANGAyKA7S9RkI5dx
JvKXIEeyw+Hio0zBksgz2lsCZRmV0BMTZB8jlcDBC4R6jwtqk/0x/5PFelDK169i
z4Rhy9oTE54GEm/1PpAV7a/FXqgn6IM/acwUY6oIrv4wlhaHgJjKX4++4ae77AwI
NEYEjq77T1nD9GeVY/owy/sqW+FrAgMBAAECgYBm4g+kYHpEu0ifPnoZ+A1qKGf5
rgrzzrv+hTheKTbs8Z1VQNiSUi9gkWzOikFlIdm7JaqVLw16T6LMBoJSSJSKiAwF
4KyWtbccupplAgE5LGp2tmukt4tIR+noMH/NHnxMgZ6BMGJvQd7R66hHzvU7bZkA
rNEoG78r7vBuhDPVAQJBAOl9IGCTpHxRqheiLKFxgbcdfb/3YjcjIFw4Y70BtImn
sTsykBOdPHZvWdVwT5Gg/mwhGMG10sEFXY7/xSlRXfECQQDls6bYEJXfxoHyZwFa
++NTTTEk0C9sPOl7MW6m7rLSDkUN6TS/qjyHnkPF/05FIq2LxdgX7dqpKvGI8hfu
kYkbAkBx8w8KEwQDaBeK0SrW335ez5W/Sgw0vH/egearJ1nklGJ6cYK/D82iP1Wo
RNeQSvHf84cRgpsIeIXgdBLUx1EBAkAsEFaFREk78DylX5Um+wMUy84NndI7FURE
2aq8YzXyjcI5dO8lhWavLXEa3msJYCmikoVtsbcslIxZFPf6T/jZAkBjG/bTsxDt
7v9tb20APb5O+8wHC1UKpUQ+24iAjXB4U+BXWMeTqkoYwVMv6cB/3eXSMcM2KB+M
xpFJK39x/Y9s
实战
直接上代码
//公钥, 我这边直接写在代码里面,你也可以写在一个文件里面,再去文件里面读取
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRgMigO0vUZCOXcSbylyBHssPh
4qNMwZLIM9pbAmUZldATE2QfI5XAwQuEeo8LapP9Mf+TxXpQytevYs+EYcvaExOe
BhJv9T6QFe2vxV6oJ+iDP2nMFGOqCK7+MJYWh4CYyl+PvuGnu+wMCDRGBI6u+09Z
w/RnlWP6MMv7KlvhawIDAQAB
//私钥 我这边直接写在代码里面,你也可以写在一个文件里面,再去文件里面读取
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANGAyKA7S9RkI5dx
JvKXIEeyw+Hio0zBksgz2lsCZRmV0BMTZB8jlcDBC4R6jwtqk/0x/5PFelDK169i
z4Rhy9oTE54GEm/1PpAV7a/FXqgn6IM/acwUY6oIrv4wlhaHgJjKX4++4ae77AwI
NEYEjq77T1nD9GeVY/owy/sqW+FrAgMBAAECgYBm4g+kYHpEu0ifPnoZ+A1qKGf5
rgrzzrv+hTheKTbs8Z1VQNiSUi9gkWzOikFlIdm7JaqVLw16T6LMBoJSSJSKiAwF
4KyWtbccupplAgE5LGp2tmukt4tIR+noMH/NHnxMgZ6BMGJvQd7R66hHzvU7bZkA
rNEoG78r7vBuhDPVAQJBAOl9IGCTpHxRqheiLKFxgbcdfb/3YjcjIFw4Y70BtImn
sTsykBOdPHZvWdVwT5Gg/mwhGMG10sEFXY7/xSlRXfECQQDls6bYEJXfxoHyZwFa
++NTTTEk0C9sPOl7MW6m7rLSDkUN6TS/qjyHnkPF/05FIq2LxdgX7dqpKvGI8hfu
kYkbAkBx8w8KEwQDaBeK0SrW335ez5W/Sgw0vH/egearJ1nklGJ6cYK/D82iP1Wo
RNeQSvHf84cRgpsIeIXgdBLUx1EBAkAsEFaFREk78DylX5Um+wMUy84NndI7FURE
2aq8YzXyjcI5dO8lhWavLXEa3msJYCmikoVtsbcslIxZFPf6T/jZAkBjG/bTsxDt
7v9tb20APb5O+8wHC1UKpUQ+24iAjXB4U+BXWMeTqkoYwVMv6cB/3eXSMcM2KB+M
xpFJK39x/Y9s
//这个key是md5加密的时候用的
YUJDED15DDS65DHF12FHF125FHF5FFKF
controller对外接口
//这边使用post的请求
@PostMapping("hello")
@Transactional
public Object collectCpn(@RequestBody String body) {
//这边使用jackson,将请求的json数据转为字符串
String name = JacksonUtil.parseString(body, "name"); //名字
String createTimeStamp = JacksonUtil.parseString(body, "create_time_stamp");//时间戳
String sign = JacksonUtil.parseString(body, "sign");//签名
//判断传过来的参数是否为空
if(name.isEmpty()) {
return "参数错误"
}
//参数 ascii排序 从小到大排序(除sign外)
Map<String, Object> parameters = new TreeMap<>();
parameters.put("name", name);
parameters.put("create_time_stamp", createTimeStamp);
//排序完的字符串后面添加 key的值
String str = AsciiSortUtil.AsciiSort(parameters).append("key=" + key).toString();
Map<String, Object> data = new HashMap<>();
try {
//私钥解密 请求的时候用公钥对签名进行加密,接收的时候,就需要用私钥解密
sign = RsaUtils.decrypt(sign, rsaPrivateKey);
//验签
if(!Md5SignUtils.verifySign(str, key, sign)) {
//验签失败
return "签名错误";
}
//解密之后的数据 (私钥解密)
name = RsaUtils.decrypt(name, rsaPrivateKey);
//中间对数据库操作省略 可以对拿到的参数进行判断,处理(每个人都不同)
//请求成功
return "请求成功";
}catch (Exception e){
e.printStackTrace();
//系统内部错误
return ReturnUtil.errorSys(reqNo);
}
}
单元测试方法
private static String key = "YUJDED15DDS65DHF12FHF125FHF5FFKF";
private static String rsaPublickKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRgMigO0vUZCOXcSbylyBHssPh
4qNMwZLIM9pbAmUZldATE2QfI5XAwQuEeo8LapP9Mf+TxXpQytevYs+EYcvaExOe
BhJv9T6QFe2vxV6oJ+iDP2nMFGOqCK7+MJYWh4CYyl+PvuGnu+wMCDRGBI6u+09Z
w/RnlWP6MMv7KlvhawIDAQAB";
private static String url = "http://127.0.0.1:8080/hello"; //请求地址
@Test
public void test1() {
String name = "小小";
String create_time_stamp = DateTimeUtil.getTimeStamp(); //时间戳
try {
//参数加密
name = RsaUtils.encrypt(ordnameer_no, rsaPublickKey);
//参数 ascii排序 从小到大排序(除了sign外)
Map<String, Object> parameters = new TreeMap<>();
parameters.put("name", name);
parameters.put("create_time_stamp", create_time_stamp);
//排序完的字符串后面添加 key的值
String str = AsciiSortUtil.AsciiSort(parameters).append("key=" + key).toString();
//使用md5对拼接完的字符串进行加密
String sign = Md5SignUtils.sign(str, key).toUpperCase();
//公钥加密
sign = RsaUtils.encrypt(sign, rsaPublickKey);
Map<String, Object> data = new HashMap<>();
data.put("name", name);
data.put("create_time_stamp", create_time_stamp);
data.put("sign", sign);
//使用hutool工具包,发送post请求
String post = HttpUtil.post(url, JSON.toJSONString(data));
//这边就可以拿到请求返回的结果
String code = JacksonUtil.parseString(post, "code");
String returnSign = JacksonUtil.parseString(post, "sign");
}catch (Exception e) {
e.printStackTrace();
}
}
工具类
RsaUtils
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: Abe
* Date: 2020/9/18 15:57
*/
public class RsaUtils {
/**
* RSA公钥加密
*
* @param str
* 加密字符串
* @param publicKey
* 公钥
* @return 密文
* @throws Exception
* 加密过程中的异常信息
*/
public static String encrypt( String str, String publicKey ) throws Exception{
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
return outStr;
}
/**
* RSA私钥加密
*
* @param str
* 加密字符串
* @param privateKey
* 私钥
* @return 密文
* @throws Exception
* 加密过程中的异常信息
*/
public static String encryptPrivateKey( String str, String privateKey) throws Exception{
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, priKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
return outStr;
}
/**
* RSA私钥解密
*
* @param str
* 加密字符串
* @param privateKey
* 私钥
* @return 铭文
* @throws Exception
* 解密过程中的异常信息
*/
public static String decrypt(String str, String privateKey) throws Exception{
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
/**
* RSA公钥解密
*
* @param str
* 加密字符串
* @param publicKey
* 私钥
* @return 铭文
* @throws Exception
* 解密过程中的异常信息
*/
public static String decryptPubKey(String str, String publicKey) throws Exception{
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pubKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
}
DateTimeUtil
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/**
* 生成时间戳
*/
public class DateTimeUtil {
/**
* 生成时间戳
* 格式: yyyyMMddHH24MISS
说明:我这边加8个小时 是因为我线上环境的原因,你可以不用加
*
* @param
* @return
*/
public static String getTimeStamp() {
LocalDateTime localDateTime = LocalDateTime.now().plusHours(8);
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
String timeStamp=new SimpleDateFormat("yyyyMMddHHmmss").
format(date.getTime());
return timeStamp;
}
}
AsciiSortUtil
package org.linlinjava.litemall.core.util;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @Author: Abe
* Date: 2020/9/23 17:52
*/
public class AsciiSortUtil {
/**
* 参数名按ASCII码从小到大排序
* @return
*/
public static StringBuffer AsciiSort(Map<String,Object> parameters) {
StringBuffer sbkey = new StringBuffer();
// entrySet 所有参与传参的参数按照accsii排序(升序)
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
//空值不传递,不参与签名组串
if (null != v && !"".equals(v)) {
sbkey.append(k + "=" + v + "&");
}
}
// sbkey = sbkey.append("key=" + key);
// System.out.println(sbkey);
return sbkey;
}
}
Md5SignUtils
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: Abe
* Date: 2020/9/18 21:55
*/
public class Md5SignUtils {
static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static String MD5 = "MD5";//加签方式:MD5
/*
* @Author boy
* @Description 数据签名
* @Date 2019/8/31 1:57 PM
* @Param [data, key]
* @return java.lang.String
*/
public static String sign(String data, String key) throws Exception {
//得到明文的字节数组
byte[] btInput = (data + key).getBytes();
// 创建一个提供信息摘要算法的对象(MD5摘要算法)
MessageDigest messageDigest = MessageDigest.getInstance(MD5);
// 使用指定的字节更新摘要
messageDigest.update(btInput);
// 得到二进制的密文
byte[] encryptData = messageDigest.digest();
// 把密文转换成十六进制的字符串形式
String encryptDataStr = bytesToHex(encryptData);
return encryptDataStr;
}
/*
* @Author boy
* @Description 验签
* @Date 2019/8/31 1:57 PM
* @Param [data, key, sign][明文数据,签名key,接收到的签名]
* @return boolean
*/
public static boolean verifySign(String data, String key, String sign) throws Exception {
//调用加签方法,看加签后的签名是否和接收到的一致
String encryptData = sign(data, key);
//转为大写
encryptData = encryptData.toUpperCase();
if (encryptData.equals(sign)) {
return true;
} else {
return false;
}
}
/*
* @Author boy
* @Description 将byte数组转化为16进制字符串
* @Date 2019/8/31 1:58 PM
* @Param [bytes]
* @return java.lang.String
*/
public static String bytesToHex(byte[] bytes) {
int k = 0;
char[] hexChars = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++) {
byte byte0 = bytes[i];
hexChars[k++] = hexDigits[byte0 >>> 4 & 0xf];
hexChars[k++] = hexDigits[byte0 & 0xf];
}
return new String(hexChars);
}
}
JacksonUtil
package org.linlinjava.litemall.core.util;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
import org.linlinjava.litemall.db.domain.LitemallUser;
public class JacksonUtil {
public static String parseString(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if (leaf != null)
return leaf.asText();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static List<String> parseStringList(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if (leaf != null)
return mapper.convertValue(leaf, new TypeReference<List<String>>() {
});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Integer parseInteger(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if (leaf != null)
return leaf.asInt();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static List<Integer> parseIntegerList(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if (leaf != null)
return mapper.convertValue(leaf, new TypeReference<List<Integer>>() {
});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static LitemallUser parseLitemallUser(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if (leaf != null)
return mapper.convertValue(leaf, new TypeReference<LitemallUser>() {
});
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Boolean parseBoolean(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if (leaf != null)
return leaf.asBoolean();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Short parseShort(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if (leaf != null) {
Integer value = leaf.asInt();
return value.shortValue();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Byte parseByte(String body, String field) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
JsonNode leaf = node.get(field);
if (leaf != null) {
Integer value = leaf.asInt();
return value.byteValue();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static <T> T parseObject(String body, String field, Class<T> clazz) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = null;
try {
node = mapper.readTree(body);
node = node.get(field);
return mapper.treeToValue(node, clazz);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Object toNode(String json) {
if (json == null) {
return null;
}
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode jsonNode = mapper.readTree(json);
return jsonNode;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}