Java如何实现http接口参数和返回值加密

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

Java如何实现http接口参数和返回值加密

站在墙头上   2022-11-19 我要评论

参数和返回值得加密目的

为了保证接口不被人拦截下来恶意请求,保证程序的稳定性,我们可以使用接口加密的方法来保证参数和返回值的保密性。

具体实现方式

因为本人是写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/**");
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们