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/**");
    }
}

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

(0)

相关推荐

  • Java DefaultListableBeanFactory接口超详细介绍

    目录 前言 AliasRegistry SimpleAliasRegistry SingletonBeanRegistry DefaultSingletonBeanRegistry FactoryBeanRegistrySupport AbstractBeanFactory AbstractAutowireCapableBeanFactory BeanDefinitionRegistry ConfigurableListableBeanFactory 前言 本文,对bean工厂的接口做分析梳理具

  • Java 内置接口 Serializable示例详解

    目录 引言 Serializable 接口 Serializable 是一个标记型接口 serializable Version UID Java 序列化与JSON序列化的区别 Java序列化相较于 JSON 的优势 Java 类对象的序列化代码演示 总结 引言 上一部分我们着重讲了 Java 集合框架中在开发项目时经常会被用到的数据容器,在讲解.演示使用实践的同时,把这个过程中遇到的各种相关知识点:泛型.Lambada.Stream 操作,一并给大家做了梳理. 从这篇开始我们进入下一部分,用三

  • Java ServletContext与ServletConfig接口使用教程

    目录 ServletContext接口 1.概念 2.功能 1.获取Web应用程序的初始化参数 2.实现多个Servlet对象共享数据 3.读取Web应用下的资源文件 ServletConfig接口 1.概念 2.ServletConfig的常用方法 ServletContext接口 1.概念 当Servlet容器启动时,会为每个Web应用创建一个唯一的ServletContext对象代表当前Web应用,可以和程序的容器(服务器)来通信. 两种获取方式: 通过request对象获取 Servle

  • Java Map接口概述和常用方法详解

    目录 概述 Map常用子类 Map接口中的常用方法 Map集合遍历键找值方式 Entry键值对对象 Map集合遍历键值对方式 概述 现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射.Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map接口. 我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图. Collection中的

  • Java基础学习之接口详解

    目录 概述 定义格式 含有抽象方法 含有默认方法和静态方法 含有私有方法和私有静态方法 基本的实现 实现的概述 抽象方法的使用 默认方法的使用 静态方法的使用 私有方法的使用 接口的多实现 抽象方法 默认方法 静态方法 优先级的问题 接口的多继承 其他成员特点 概述 接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量.构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法 (JDK 9). 接

  • java中如何使用HttpClient调用接口

    目录 java使用HttpClient调用接口 HttpClient 提供的主要的功能 直接言归正传了!!!!上代码 java的HttpClient调用远程接口 使用方法 实例 java使用HttpClient调用接口 HttpClient 提供的主要的功能 (1)实现了所有 HTTP 的方法(GET,POST,PUT,DELETE 等) (2)支持自动转向 (3)支持 HTTPS 协议 (4)支持代理服务器等 直接言归正传了!!!!上代码 public static String sendPu

  • Java中的Comparable和Comparator接口

    目录 一. Comparable接口 1. Comparable简介 2. 为什么要实现Comparable接口 3. Comparable的实际应用 二. Comparator接口 1. Comparator简介 2. Comparator接口的实际运用 三. Comparable和Comparator的比较 一. Comparable接口 1. Comparable简介 Comparable是排序接口. 若一个类实现了Comparable接口,就意味着该类支持排序. 实现了Comparabl

  • Java BeanDefination接口详细讲解

    目录 功能作用 为什么这样设计 假设没有BeanDefinition 有BeanDefinition BeanDefinition 属性介绍 示例 功能作用 BeanDefinition 是定义 Bean 的配置元信息接口,包含: Bean 的类名 设置父 bean 名称.是否为 primary. Bean 行为配置信息,作用域.自动绑定模式.生命周期回调.延迟加载.初始方法.销毁方法等 Bean 之间的依赖设置,dependencies 构造参数.属性设置 BeanDefinition 子类方

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

    目录 参数和返回值得加密目的 具体实现方式 大致思路 代码实现 身份检验 参数和返回值得加密目的 为了保证接口不被人拦截下来恶意请求,保证程序的稳定性,我们可以使用接口加密的方法来保证参数和返回值的保密性. 具体实现方式 因为本人是写Java 的,所以这里就直接以Java代码为例.思想都是一样的,只是不同的语言都不同的实现方式. 大致思路 我们的参数和返回值只需要定义两个参数:ak,ct,ak存放堆成加密的秘钥,ct存放加密之后的请求内容. 加密方式用到AES对称加密和RSA非对称加密,将请求参

  • java中有无参数和返回值的方法详解

    目录 java有无参数和返回值 以下的例子要细细查看 方法的返回值和参数 1.返回值 2.参数 java有无参数和返回值 首先,定义一个土豪类(LocalTyrant) 属性:name moeney smoke 行为一:(无参数无返回值): 行为二:(无参数有返回值): 行为三:(有参数有返回值): 行为四:(有参数无返回值): 以下的例子要细细查看 慢慢分析,切不可急于求成 package cm.tr; class LocalTyrant{ String name; int money; St

  • Java陷阱之慎用入参做返回值详解

    正常情况下,在Java中入参是不建议用做返回值的.除了造成代码不易理解.语义不清等问题外,可能还埋下了陷阱等你入坑. 问题背景 比如有这么一段代码: @Named public class AService { private SupplyAssignment localSupply = new SupplyAssignment(); @Inject private BService bervice; public List<Supply> calcSupplyAssignment() Lis

  • 存储过程的输出参数,返回值与结果集

    每个存储过程都有默认的返回值,默认值为0.下面我们分别看看在management studio中如何查看输出参数,返回值以及结果集,然后我们再在ASP.NET调用存储过程中如何获得输出参数,返回值以及结果集. 首先:在sql server management studio中查看输出参数,返回值以及结果集.本示例以Northwind数据库为例. 复制代码 代码如下: create proc Employee @Rowcount int=0 output as begin SELECT * FRO

  • ajax请求后台接口数据与返回值处理js的实例讲解

    ajax的代码,用的是jquery的 ajax: $.ajax({ url: "/test.php",//后台提供的接口 type: "post", //请求方式是post data:{"type":"1", //这是你要传给后台的data值 "t":"c4552111" }, dataType: "json", //数据类型是json型 success: funct

  • Python 函数用法简单示例【定义、参数、返回值、函数嵌套】

    本文实例讲述了Python 函数用法.分享给大家供大家参考,具体如下: demo.py(函数定义): # say_hello() # 不能在定义函数之前调用函数 # Python 解释器知道下方定义了一个函数 def say_hello(): """函数的说明文档""" print("hello 1") print("hello 2") print("hello 3") print(&q

  • Python学习笔记之函数的参数和返回值的使用

    01.函数参数和返回值的作用 函数根据 有没有参数 以及 有没有返回值,可以相互结合,共有四种: 无参数 无返回值 无参数 有返回值 有参数 无返回值 有参数 有返回值 定义函数时候,是否接收参数,或者时候返回结果,是根据实际功能需求决定的. 如果函数内部处理的数据不确定,就可以将外界的数据以参数形式传递到函数内部. 如果希望一个函数执行完成后,向外界汇报执行结果,就可以增加函数的返回值. 02.函数的返回值-进阶 在程序开发中,有时候,会希望一个函数执行结束后,告诉调用者一个结果,以便调用者针

  • 对python3 中方法各种参数和返回值详解

    如下所示: # -*- coding:utf-8 -*- # Author: Evan Mi # 函数 def func1(): print('in the func1') return 0 # 过程 def func2(): print('in the func2') """ 多个值用逗号分割后返回,会分装到一个tuple中返回, 接收的时候,如果使用一个变量接收,那么这个接收变量就是一个tuple类型的 如果接收的时候也用逗号分割多个值来接收,那么可以分别对应返回tupl

  • Golang的func参数及返回值操作

    参数及返回值 参数一指定数据类型为int 参数二 (-interface{}) 可传任何多个不同类型的参数 返回值:单个返回值直接指定数据类型可以不使用 (),多个返回值需使用().各返回值之间使用逗号分隔 func main() { demo.Params(10, 20, "golang", true) } func Params(id int, params ...interface{}) (error, error) { fmt.Println(id) fmt.Println(p

  • golang数组和切片作为参数和返回值的实现

    目录 1. 数组作为参数和返回值时 1.1数组的定义 1.2数组作为参数和返回值的时候 2.切片作为参数和返回值 2.1 切片的定义初始化 2.2 切片的存储大致分为3部分 2.3 切片作为参数和返回值 2.4 append 切片动态增长的原理 2.5 copy 函数 通过赋值切片可以使得两个切片的数据不共享 3. 总结: 1. 数组作为参数和返回值时 1.1数组的定义 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型.字符串或者自定义类型 var

随机推荐