Java如何实现http接口参数和返回值加密
目录
- 参数和返回值得加密目的
- 具体实现方式
- 大致思路
- 代码实现
- 身份检验
参数和返回值得加密目的
为了保证接口不被人拦截下来恶意请求,保证程序的稳定性,我们可以使用接口加密的方法来保证参数和返回值的保密性。
具体实现方式
因为本人是写Java 的,所以这里就直接以Java代码为例。思想都是一样的,只是不同的语言都不同的实现方式。
大致思路
- 我们的参数和返回值只需要定义两个参数:ak,ct,ak存放堆成加密的秘钥,ct存放加密之后的请求内容。
- 加密方式用到AES对称加密和RSA非对称加密,将请求参数使用AES加密,HTTP的POST请求和GET请求都可以将实际请求参数的格式定义成同一种格式(比如:JSON格式),便于后台处理。AES加密使用的秘钥用RSA加密。这样我们就不需要操心参数加密的秘钥是什么了,双方都可以随时更换。
- 使用过滤器获取到请求的加密内容后,通过RSA解密将参数加密的秘钥解析出来,然后再通过解析出来的秘钥通过AES解密去获取实际的请求内容。
注意:不同的请求方式获取参数的方式不同,需要根据不同的请求方式去做不同的解析
代码实现
1. 首先需要一个过滤器,因为我们要拦截请求参数,和加密返回的数据。
因为并不是所有的接口都需要加密,过滤器不能想Spring拦截器那样直接配置拦截和不拦截的类,所以定义了一个ALLOWED_PATHS 集合,用于过滤不需要加解密的请求。然后根据不同的请求方式去解析不同的请求内容,我这里只处理了get请求和post请求这两种常用的请求方式。
package com.pay.filter; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * 请求信息加解密 */ @Log4j2 @Component @WebFilter(filterName = "EncryptionFilter", urlPatterns = {"/*"}) public class EncryptionFilter implements Filter { private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>( Arrays.asList("/pay/notify"))); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if(request instanceof HttpServletRequest) { HttpServletRequest hRequest = (HttpServletRequest) request; String servletPath = hRequest.getServletPath(); log.info("request_path:path="+servletPath); boolean allow = false; for (String allowedPath : ALLOWED_PATHS) { if(servletPath.contains(allowedPath)) { allow = true; break; } } if(allow) { chain.doFilter(request, response); log.info("no Encryption"); return; } MyHttpServletResponseWrapper responseWrapper = new MyHttpServletResponseWrapper((HttpServletResponse) response); if(hRequest.getMethod().equalsIgnoreCase("post")) { MyHttpServletRequestWrapper requestWrapper = null; try { requestWrapper = new MyHttpServletRequestWrapper(hRequest); } catch (Exception e) { request.getRequestDispatcher("404.html").forward(request,response); } chain.doFilter(requestWrapper, responseWrapper); }else if(!hRequest.getMethod().equalsIgnoreCase("options")){ log.info("收到 potions请求"); } else { //其余默认get请求 ParameterRequestWrapper requestWrapper = null; try { requestWrapper = new ParameterRequestWrapper(hRequest); } catch (Exception e) { request.getRequestDispatcher("404.html").forward(request,response); } chain.doFilter(requestWrapper, responseWrapper); } String resp = responseWrapper.getTextContent(); //获取接口返回内容 //加密处理返回 response.getOutputStream().write(HttpEncryptUtil.serverEncrypt(resp).getBytes(StandardCharsets.UTF_8)); } } }
2. 对于POST请求,定义一个解析request中参数的类 MyHttpServletRequestWrapper继承HttpServletRequestWrapper ,去解析请求参数。
具体实现方式如下:
readBody:实际解析请求参数
package com.pay.filter; import com.alipay.api.internal.util.file.IOUtils; import com.pay.util.HttpEncryptUtil; import org.apache.commons.lang3.StringUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { private String requestBody = null; public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); requestBody = readBody(request); } @Override public BufferedReader getReader() { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() { final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8)); return 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 bais.read(); } }; } private static String readBody(ServletRequest request) throws IOException { String param = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8); if(StringUtils.isNotBlank(param)) { //解密请求参数 return HttpEncryptUtil.serverDecrypt(param); } return param; } }
3. 对于get请求,定义一个参数类ParameterRequestWrapper,继承HttpServletRequestWrapper 去解析URL上的参数
具体方式如下:
重载构造方法获取加密之后的参数,去解析出来
ParameterRequestWrapper(HttpServletRequest request)
重写getParameter和setParameter等获取参数和设置参数的方法
package com.pay.filter; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.pay.util.HttpEncryptUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.util.HashMap; import java.util.Map; public class ParameterRequestWrapper extends HttpServletRequestWrapper { private Map<String , String[]> params = new HashMap<String, String[]>(); @SuppressWarnings("unchecked") public ParameterRequestWrapper(HttpServletRequest request) { // 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似 super(request); //将参数表,赋予给当前的Map以便于持有request中的参数 //解析 String ak = request.getParameter("ak"); String ct = request.getParameter("ct"); JSONObject jsonObject = new JSONObject(); jsonObject.put("ak",ak); jsonObject.put("ct",ct); String s = HttpEncryptUtil.serverDecrypt(jsonObject.toJSONString()); addAllParameters(JSON.parseObject(s)); } //重载一个构造方法 public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) { this(request); addAllParameters(extendParams);//这里将扩展参数写入参数表 } @Override public String getParameter(String name) {//重写getParameter,代表参数从当前类中的map获取 String[]values = params.get(name); if(values == null || values.length == 0) { return null; } return values[0]; } public String[] getParameterValues(String name) {//同上 return params.get(name); } public void addAllParameters(Map<String , Object>otherParams) {//增加多个参数 for(Map.Entry<String , Object>entry : otherParams.entrySet()) { addParameter(entry.getKey() , entry.getValue()); } } public void addParameter(String name , Object value) {//增加参数 if(value != null) { if(value instanceof String[]) { params.put(name , (String[])value); }else if(value instanceof String) { params.put(name , new String[] {(String)value}); }else { params.put(name , new String[] {String.valueOf(value)}); } } } }
注意:为了便于区分我才把获取post请求内容和get请求内容的类写成了两个,其实可以尝试着去将两个解析类合成一个
4. 加解密工具类 HttpEncryptUtil ,和两个网上百度的AESUtil,RSAUtil
package com.pay.util; import com.alibaba.fastjson.JSONObject; import com.pay.common.Constant; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.interfaces.RSAPublicKey; public class HttpEncryptUtil { //解密请求内容 public static String serverDecrypt(String content) { JSONObject result = JSONObject.parseObject(content); String encryptAesKeyStr = result.getString("ak"); String encryptContent = result.getString("ct"); //使用私钥解密 获取解密内容的AES密钥 // encryptAesKeyStr = decodeBase64String(encryptAesKeyStr); //base64解码 PrivateKey privateKey = RSAUtil.getPrivateKey(HTTP_PRIVATE_KEY); String aesKey = RSAUtil.decryptByPrivate(decodeBase64String(encryptAesKeyStr), privateKey); //使用解密出来的key解密content:使用AES return AESUtil.decryptAES(decodeBase64String(encryptContent), aesKey); } public static String serverDecryptByPublic(String content) { JSONObject result = JSONObject.parseObject(content); String encryptAesKeyStr = result.getString("ak"); String encryptContent = result.getString("ct"); //使用私钥解密 获取解密内容的AES密钥 // encryptAesKeyStr = decodeBase64String(encryptAesKeyStr); //base64解码 RSAPublicKey publicKey = RSAUtil.getPublicKey(HTTP_PUBLIC_KEY); String aesKey = RSAUtil.decryptByPublic(decodeBase64String(encryptAesKeyStr), publicKey); //使用解密出来的key解密content:使用AES return AESUtil.decryptAES(decodeBase64String(encryptContent), aesKey); } //加密返回内容 public static String serverEncrypt(String content) { String aesKey = HTTP_CONTENT_KEY; //使用私钥加密AES的key PrivateKey privateKey = RSAUtil.getPrivateKey(HTTP_PRIVATE_KEY); String ak = RSAUtil.encryptByPrivate(aesKey.getBytes(StandardCharsets.UTF_8), privateKey); String ct = AESUtil.encryptAES(content, aesKey); JSONObject result = new JSONObject(); result.put("ak",encodeBase64String(ak)); result.put("ct",encodeBase64String(ct)); return result.toJSONString(); } public static String serverEncryptByPublic(String content) { String aesKey = HTTP_CONTENT_KEY; //使用公钥钥加密AES的key RSAPublicKey publicKey = RSAUtil.getPublicKey(HTTP_PUBLIC_KEY); String ak = RSAUtil.encryptByPublic(aesKey.getBytes(StandardCharsets.UTF_8), publicKey); String ct = AESUtil.encryptAES(content, aesKey); JSONObject result = new JSONObject(); result.put("ak",encodeBase64String(ak)); result.put("ct",encodeBase64String(ct)); return result.toJSONString(); } public static String encodeBase64String(String content) { if(StringUtils.isBlank(content)) { return null; } return Base64.encodeBase64String(content.getBytes(StandardCharsets.UTF_8)); } public static String decodeBase64String(String content) { if(StringUtils.isBlank(content)) { return null; } return new String(Base64.decodeBase64(content), StandardCharsets.UTF_8); } }
package com.pay.util; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; public class AESUtil { public static final Charset CHARSET = StandardCharsets.UTF_8; public static final String ALGORITHMS_MD5 = "MD5"; public static final String SHA = "SHA1PRNG"; public static final String ALGORITHM = "AES"; /** * 加密 * * @param content 需要加密的内容 * @param password 加密密码 * @return */ public static byte[] encrypt(String content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM); SecureRandom random = SecureRandom.getInstance(SHA); random.setSeed(password.getBytes()); kgen.init(128, random); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM);// 创建密码器 byte[] byteContent = content.getBytes(CHARSET); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 return cipher.doFinal(byteContent); // 加密 } catch (Exception e) { e.printStackTrace(); } return null; } /** * 解密 * * @param content 待解密内容 * @param password 解密密钥 * @return */ public static byte[] decrypt(byte[] content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM); SecureRandom random = SecureRandom.getInstance(SHA); random.setSeed(password.getBytes()); kgen.init(128, random); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM);// 创建密码器 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化 return cipher.doFinal(content); // 加密 } catch (Exception e) { e.printStackTrace(); } return null; } /** * 将二进制转换成16进制 * * @param buf * @return */ public static String parseByte2HexStr(byte buf[]) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /** * 将16进制转换为二进制 * * @param hexStr * @return */ public static byte[] parseHexStr2Byte(String hexStr) { if (hexStr.length() < 1) return null; byte[] result = new byte[hexStr.length() / 2]; for (int i = 0; i < hexStr.length() / 2; i++) { int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16); result[i] = (byte) (high * 16 + low); } return result; } private static byte[] doSign(String algorithms, String inputStr) throws NoSuchAlgorithmException { MessageDigest messageDigest = MessageDigest.getInstance(algorithms); messageDigest.update(inputStr.getBytes(CHARSET)); return messageDigest.digest(); } private static String signToHexStr(String algorithms, String inputStr) { try { return parseByte2HexStr(doSign(algorithms, inputStr)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } } public static String sign(String key, String sourceStr) { String md5Str1 = signToHexStr(ALGORITHMS_MD5, sourceStr); String sourceStr2 = md5Str1 + key; return signToHexStr(ALGORITHMS_MD5, sourceStr2); } public static String toDecryptParam(String sourceStr, String password){ try { byte[] sourceByte = parseHexStr2Byte(sourceStr); byte[] bytes = decrypt(sourceByte, password); return new String(bytes,CHARSET); }catch (Exception e){ e.printStackTrace(); } return ""; } public static String toEncryptResult(Object sourceStr, String password){ String parameterJson = JSONObject.toJSONString(sourceStr); byte[] encode = encrypt(parameterJson, password); return parseByte2HexStr(encode); } public static String encryptAES(String content, String password) { try { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); byte[] byteContent = content.getBytes("utf-8"); cipher.init(1, getSecretKey(password)); byte[] result = cipher.doFinal(byteContent); return Base64.encodeBase64String(result); } catch (Exception var5) { return null; } } public static String decryptAES(String content, String password) { try { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(2, getSecretKey(password)); byte[] result = cipher.doFinal(Base64.decodeBase64(content)); return new String(result, "utf-8"); } catch (Exception var4) { return null; } } private static SecretKeySpec getSecretKey(String password) { KeyGenerator kg = null; try { kg = KeyGenerator.getInstance("AES"); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.setSeed(password.getBytes()); kg.init(128, random); SecretKey secretKey = kg.generateKey(); return new SecretKeySpec(secretKey.getEncoded(), "AES"); } catch (NoSuchAlgorithmException var4) { return null; } }
package com.pay.util; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.*; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.*; /** * RSA算法加密/解密工具类 */ @Slf4j public class RSAUtil { /** 算法名称 */ private static final String ALGORITHM = "RSA"; /** 默认密钥大小 */ private static final int KEY_SIZE = 1024; /** 用来指定保存密钥对的文件名和存储的名称 */ private static final String PUBLIC_KEY_NAME = "publicKey"; private static final String PRIVATE_KEY_NAME = "privateKey"; private static final String PUBLIC_FILENAME = "publicKey.properties"; private static final String PRIVATE_FILENAME = "privateKey.properties"; /** 密钥对生成器 */ private static KeyPairGenerator keyPairGenerator = null; private static KeyFactory keyFactory = null; /** 缓存的密钥对 */ private static KeyPair keyPair = null; /** Base64 编码/解码器 JDK1.8 */ private static Base64.Decoder decoder = Base64.getDecoder(); private static Base64.Encoder encoder = Base64.getEncoder(); /** 初始化密钥工厂 */ static{ try { keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM); keyFactory = KeyFactory.getInstance(ALGORITHM); } catch (NoSuchAlgorithmException e) { log.error("RSAUtil[ERROR]:e={}",e); } } /** 私有构造器 */ private RSAUtil(){} /** * 生成密钥对 * 将密钥分别用Base64编码保存到#publicKey.properties#和#privateKey.properties#文件中 * 保存的默认名称分别为publicKey和privateKey */ public static synchronized Map<String, Object> generateKeyPair(){ try { keyPairGenerator.initialize(KEY_SIZE,new SecureRandom(UUID.randomUUID().toString().replaceAll("-","").getBytes())); keyPair = keyPairGenerator.generateKeyPair(); } catch (InvalidParameterException e){ log.error("KeyPairGenerator does not support a key length of " + KEY_SIZE + ".",e); } catch (NullPointerException e){ log.error("RSAUtil#key_pair_gen is null,can not generate KeyPairGenerator instance.",e); } RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate(); String publicKeyString = encoder.encodeToString(rsaPublicKey.getEncoded()); String privateKeyString = encoder.encodeToString(rsaPrivateKey.getEncoded()); storeKey(publicKeyString,PUBLIC_KEY_NAME,PUBLIC_FILENAME); storeKey(privateKeyString,PRIVATE_KEY_NAME,PRIVATE_FILENAME); Map<String, Object> keyPair = new HashMap<>(); keyPair.put("public", publicKeyString); keyPair.put("private", privateKeyString); return keyPair; } /** * 将指定的密钥字符串保存到文件中,如果找不到文件,就创建 * @param keyString 密钥的Base64编码字符串(值) * @param keyName 保存在文件中的名称(键) * @param fileName 目标文件名 */ private static void storeKey(String keyString,String keyName,String fileName){ Properties properties = new Properties(); //存放密钥的绝对地址 String path = null; try{ path = RSAUtil.class.getClassLoader().getResource(fileName).toString(); path = path.substring(path.indexOf(":") + 1); }catch (NullPointerException e){ //如果不存#fileName#就创建 log.warn("storeKey()# " + fileName + " is not exist.Begin to create this file."); String classPath = RSAUtil.class.getClassLoader().getResource("").toString(); String prefix = classPath.substring(classPath.indexOf(":") + 1); String suffix = fileName; File file = new File(prefix + suffix); try { file.createNewFile(); path = file.getAbsolutePath(); } catch (IOException e1) { log.error(fileName +" create fail.",e1); } } try(OutputStream out = new FileOutputStream(path)){ properties.setProperty(keyName,keyString); properties.store(out,"There is " + keyName); } catch (FileNotFoundException e) { log.error("ModulusAndExponent.properties is not found.",e); } catch (IOException e) { log.error("OutputStream output failed.",e); } } /** * 获取密钥字符串 * @param keyName 需要获取的密钥名 * @param fileName 密钥所在文件 * @return Base64编码的密钥字符串 */ private static String getKeyString(String keyName,String fileName){ if (RSAUtil.class.getClassLoader().getResource(fileName) == null){ log.warn("getKeyString()# " + fileName + " is not exist.Will run #generateKeyPair()# firstly."); generateKeyPair(); } try(InputStream in = RSAUtil.class.getClassLoader().getResource(fileName).openStream()){ Properties properties = new Properties(); properties.load(in); return properties.getProperty(keyName); } catch (IOException e) { log.error("getKeyString()#" + e.getMessage(),e); } return null; } /** * 从文件获取RSA公钥 * @return RSA公钥 * @throws InvalidKeySpecException */ public static RSAPublicKey getPublicKey(){ try { byte[] keyBytes = decoder.decode(getKeyString(PUBLIC_KEY_NAME,PUBLIC_FILENAME)); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes); return (RSAPublicKey)keyFactory.generatePublic(x509EncodedKeySpec); }catch (InvalidKeySpecException e) { log.error("getPublicKey()#" + e.getMessage(),e); } return null; } public static RSAPublicKey getPublicKey(String publicKeyString) { if(StringUtils.isBlank(publicKeyString)) { return null; } try { byte[] keyBytes = decoder.decode(publicKeyString); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes); return (RSAPublicKey)keyFactory.generatePublic(x509EncodedKeySpec); }catch (InvalidKeySpecException e) { log.error("getPublicKey()#" + e.getMessage(),e); } return null; } /** * 从文件获取RSA私钥 * @return RSA私钥 * @throws InvalidKeySpecException */ public static RSAPrivateKey getPrivateKey(){ try { byte[] keyBytes = decoder.decode(getKeyString(PRIVATE_KEY_NAME,PRIVATE_FILENAME)); PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes); return (RSAPrivateKey)keyFactory.generatePrivate(pkcs8EncodedKeySpec); } catch (InvalidKeySpecException e) { log.error("getPrivateKey()#" + e.getMessage(),e); } return null; } public static RSAPrivateKey getPrivateKey(String privateKeyString) { if(StringUtils.isBlank(privateKeyString)) { return null; } try { byte[] keyBytes = decoder.decode(privateKeyString); PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes); return (RSAPrivateKey)keyFactory.generatePrivate(pkcs8EncodedKeySpec); } catch (InvalidKeySpecException e) { log.error("getPrivateKey()#" + e.getMessage(),e); } return null; } /** * RSA公钥加密 * @param content 等待加密的数据 * @param publicKey RSA 公钥 if null then getPublicKey() * @return 加密后的密文(16进制的字符串) */ public static String encryptByPublic(byte[] content,PublicKey publicKey){ if (publicKey == null){ publicKey = getPublicKey(); } try { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE,publicKey); //该密钥能够加密的最大字节长度 int splitLength = ((RSAPublicKey)publicKey).getModulus().bitLength() / 8 -11; byte[][] arrays = splitBytes(content,splitLength); StringBuffer stringBuffer = new StringBuffer(); for (byte[] array : arrays){ stringBuffer.append(bytesToHexString(cipher.doFinal(array))); } return stringBuffer.toString(); } catch (NoSuchAlgorithmException e) { log.error("encrypt()#NoSuchAlgorithmException",e); } catch (NoSuchPaddingException e) { log.error("encrypt()#NoSuchPaddingException",e); } catch (InvalidKeyException e) { log.error("encrypt()#InvalidKeyException",e); } catch (BadPaddingException e) { log.error("encrypt()#BadPaddingException",e); } catch (IllegalBlockSizeException e) { log.error("encrypt()#IllegalBlockSizeException",e); } return null; } /** * RSA私钥加密 * @param content 等待加密的数据 * @param privateKey RSA 私钥 if null then getPrivateKey() * @return 加密后的密文(16进制的字符串) */ public static String encryptByPrivate(byte[] content,PrivateKey privateKey){ if (privateKey == null){ privateKey = getPrivateKey(); } try { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE,privateKey); //该密钥能够加密的最大字节长度 int splitLength = ((RSAPrivateKey)privateKey).getModulus().bitLength() / 8 -11; byte[][] arrays = splitBytes(content,splitLength); StringBuffer stringBuffer = new StringBuffer(); for(byte[] array : arrays){ stringBuffer.append(bytesToHexString(cipher.doFinal(array))); } return stringBuffer.toString(); } catch (NoSuchAlgorithmException e) { log.error("encrypt()#NoSuchAlgorithmException",e); } catch (NoSuchPaddingException e) { log.error("encrypt()#NoSuchPaddingException",e); } catch (InvalidKeyException e) { log.error("encrypt()#InvalidKeyException",e); } catch (BadPaddingException e) { log.error("encrypt()#BadPaddingException",e); } catch (IllegalBlockSizeException e) { log.error("encrypt()#IllegalBlockSizeException",e); } return null; } /** * RSA私钥解密 * @param content 等待解密的数据 * @param privateKey RSA 私钥 if null then getPrivateKey() * @return 解密后的明文 */ public static String decryptByPrivate(String content,PrivateKey privateKey){ if (privateKey == null){ privateKey = getPrivateKey(); } try { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE,privateKey); //该密钥能够加密的最大字节长度 int splitLength = ((RSAPrivateKey)privateKey).getModulus().bitLength() / 8; byte[] contentBytes = hexStringToBytes(content); byte[][] arrays = splitBytes(contentBytes,splitLength); StringBuffer stringBuffer = new StringBuffer(); String sTemp = null; for (byte[] array : arrays){ stringBuffer.append(new String(cipher.doFinal(array))); } return stringBuffer.toString(); } catch (NoSuchAlgorithmException e) { log.error("encrypt()#NoSuchAlgorithmException",e); } catch (NoSuchPaddingException e) { log.error("encrypt()#NoSuchPaddingException",e); } catch (InvalidKeyException e) { log.error("encrypt()#InvalidKeyException",e); } catch (BadPaddingException e) { log.error("encrypt()#BadPaddingException",e); } catch (IllegalBlockSizeException e) { log.error("encrypt()#IllegalBlockSizeException",e); } return null; } /** * RSA公钥解密 * @param content 等待解密的数据 * @param publicKey RSA 公钥 if null then getPublicKey() * @return 解密后的明文 */ public static String decryptByPublic(String content,PublicKey publicKey){ if (publicKey == null){ publicKey = getPublicKey(); } try { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE,publicKey); //该密钥能够加密的最大字节长度 int splitLength = ((RSAPublicKey)publicKey).getModulus().bitLength() / 8; byte[] contentBytes = hexStringToBytes(content); byte[][] arrays = splitBytes(contentBytes,splitLength); StringBuffer stringBuffer = new StringBuffer(); String sTemp = null; for (byte[] array : arrays){ stringBuffer.append(new String(cipher.doFinal(array))); } return stringBuffer.toString(); } catch (NoSuchAlgorithmException e) { log.error("encrypt()#NoSuchAlgorithmException",e); } catch (NoSuchPaddingException e) { log.error("encrypt()#NoSuchPaddingException",e); } catch (InvalidKeyException e) { log.error("encrypt()#InvalidKeyException",e); } catch (BadPaddingException e) { log.error("encrypt()#BadPaddingException",e); } catch (IllegalBlockSizeException e) { log.error("encrypt()#IllegalBlockSizeException",e); } return null; } /** * 根据限定的每组字节长度,将字节数组分组 * @param bytes 等待分组的字节组 * @param splitLength 每组长度 * @return 分组后的字节组 */ public static byte[][] splitBytes(byte[] bytes,int splitLength){ //bytes与splitLength的余数 int remainder = bytes.length % splitLength; //数据拆分后的组数,余数不为0时加1 int quotient = remainder != 0 ? bytes.length / splitLength + 1:bytes.length / splitLength; byte[][] arrays = new byte[quotient][]; byte[] array = null; for (int i =0;i<quotient;i++){ //如果是最后一组(quotient-1),同时余数不等于0,就将最后一组设置为remainder的长度 if (i == quotient -1 && remainder != 0){ array = new byte[remainder]; System.arraycopy(bytes,i * splitLength,array,0,remainder); } else { array = new byte[splitLength]; System.arraycopy(bytes,i*splitLength,array,0,splitLength); } arrays[i] = array; } return arrays; } /** * 将字节数组转换成16进制字符串 * @param bytes 即将转换的数据 * @return 16进制字符串 */ public static String bytesToHexString(byte[] bytes){ StringBuffer sb = new StringBuffer(bytes.length); String temp = null; for (int i = 0;i< bytes.length;i++){ temp = Integer.toHexString(0xFF & bytes[i]); if(temp.length() <2){ sb.append(0); } sb.append(temp); } return sb.toString(); } /** * 将16进制字符串转换成字节数组 * @param hex 16进制字符串 * @return byte[] */ public static byte[] hexStringToBytes(String hex){ int len = (hex.length() / 2); hex = hex.toUpperCase(); byte[] result = new byte[len]; char[] chars = hex.toCharArray(); for (int i= 0;i<len;i++){ int pos = i * 2; result[i] = (byte)(toByte(chars[pos]) << 4 | toByte(chars[pos + 1])); } return result; } /** * 将char转换为byte * @param c char * @return byte */ private static byte toByte(char c){ return (byte)"0123456789ABCDEF".indexOf(c); }
身份检验
除了参数加密之外,我们还可以做一个简单的身份校验。这里就需要使用到Spring的拦截器了。
可以在header中放一个身份token,拦截器里面校验
具体实现:
一个拦截器 TokenInterceptor.java 和配置类 WebConfig.java
package com.pay.filter; import com.alibaba.fastjson.JSONObject; import com.pay.common.ErrorCode; import com.pay.common.GlobalEnums; import com.pay.entity.SourceConfig; import com.pay.service.SourceConfigService; import com.pay.util.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component @Slf4j public class TokenInterceptor implements HandlerInterceptor { @Autowired private SourceConfigService sourceConfigService; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { String token = httpServletRequest.getHeader("token"); httpServletResponse.setContentType("text/html;charset=utf-8"); httpServletResponse.setCharacterEncoding("utf-8"); ServletOutputStream outputStream = httpServletResponse.getOutputStream(); JSONObject jsonObject =new JSONObject(); if(StringUtils.isBlank(token)) { jsonObject.put("code", ErrorCode.Ax000014.getCode()); jsonObject.put("message", ErrorCode.Ax000014.getMessage()); outputStream.write(jsonObject.toJSONString().getBytes()); return false; } SourceConfig sourceConfig = sourceConfigService.selectBySource(token); if(sourceConfig == null) { jsonObject.put("code", ErrorCode.Ax000014.getCode()); jsonObject.put("message", ErrorCode.Ax000014.getMessage()); outputStream.write(jsonObject.toJSONString().getBytes()); return false; } if(sourceConfig.getStatus().equals(GlobalEnums.EnableEnum.FALSE.getEnable())) { jsonObject.put("code", ErrorCode.Ax000014.getCode()); jsonObject.put("message", ErrorCode.Ax000014.getMessage()); outputStream.write(jsonObject.toJSONString().getBytes()); return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3) throws Exception { RequestTokenHolder.remove(); } }
package com.pay.config; import com.pay.filter.TokenInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Resource; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { } @Resource private TokenInterceptor tokenInterceptor; /** * 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效 * * @param registry "" */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor) .addPathPatterns("/**") .excludePathPatterns( "/pay/notify/**"//登录接口 ) // 过滤swagger .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"); } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。