SpringBoot中拦截器的运用(验证某些请求的时间戳和RSA签名)

   SpringBoot中实现拦截器,需要继承WebMvcConfigurerAdapter类,这个类中实现了webMvcConfig接口的抽象类,其次我们可以实现该类中相应的抽象方法。

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(paramsValidInterceptor())
                .addPathPatterns("/terminalCall/syncMachine")
                .addPathPatterns("/terminalCall/channelGoodsList");
    }
    /**
     * 注册对终端调用接口进行签名调用的验证拦截器
     * @return
     */
    @Bean
    public ParamsValidInterceptor paramsValidInterceptor(){
        return new ParamsValidInterceptor();
    }

  上述这段代码做了两件事情,第一,将我们自定义拦截器给注入到bean容器中进行管理,第二,将需要拦截的请求放入拦截器中,这样,我们只需要去实现其中的具体业务逻辑即可。

  • 创建自己的拦截器类并实现 HandlerInterceptor 接口
package com.youfuli.vendor.interceptor;


import com.youfuli.vendor.constant.Messages;
import com.youfuli.vendor.utils.RSA2;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.apache.commons.codec.binary.Base64;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

import java.util.*;

/**
 * Created by 李啸天 on 2019/3/13.
 */
@Slf4j
@Component
public class ParamsValidInterceptor extends HandlerInterceptorAdapter {

    @Value("${rsa.pubKey}")
    String pubKey;

    @Value("${rsa.priKey}")
    String priKey;

    @Value("${rsa.pwd}")
    String pwd;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String times = (System.currentTimeMillis()/1000)+"";
        log.info("当前时间戳为{}",times);
        log.info("传入验证签名方法的路径为{}",request.getRequestURL());
        Map<String,String[]> maps = request.getParameterMap();
        //时间戳是否失效
            Assert.isTrue(checkTimeStamp(new Long(maps.get("timeStamp")[0])), Messages.TIMESPAN_INVALID);
        //签名数据
        String signStr = maps.get("sign")[0];
        log.info("传入签名数据为{}",signStr);
        //参与签名数据
        String returnString = key_sort(maps);
        log.info("传入参与签名数据为{}",returnString);
        //拿取公钥
        File pubfile = ResourceUtils.getFile(pubKey);
        String publicKey = txt2String(pubfile).replaceAll("(\\\r\\\n|\\\r|\\\n|\\\n\\\r)", "");

        //拿取私钥
        File prifile = ResourceUtils.getFile(priKey);
        String privateKey = txt2String(prifile).replaceAll("(\\\r\\\n|\\\r|\\\n|\\\n\\\r)", "");

        //对现有数据进行签名---本地签名校验
        byte [] signValidStr = RSA2.sign(returnString.getBytes(),privateKey);
        String signValid = Base64.encodeBase64String(signValidStr);
        boolean verfiy;
        if(signValid.equals(signStr)){
            verfiy = RSA2.verify(returnString.getBytes(),signValidStr,publicKey);
            log.info("当前验签结果为{}",verfiy);
        }else{
            verfiy=false;
        }
        if(verfiy==false){
            Assert.isTrue(verfiy,Messages.VALID_SIGN_ERROR);
        }
        return verfiy;
    }

    /**
    *排序
    **/
    public static String key_sort(Map<String, String[]> map) {
        String key_sort = "";

        TreeMap<String, String[]> map2 = new TreeMap<String, String[]>(new Comparator<String>() {
            public int compare(String obj1, String obj2) {
                // 降序排序
                return obj2.compareTo(obj1);
            }
        });
        map2 = new TreeMap<>(map);
        map2.remove("sign");

        Set<String> keySet = map2.keySet();
        Iterator<String> iter = keySet.iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            key_sort = key_sort + key + "=" + map2.get(key)[0] + "&";
        }
        return key_sort.substring(0, key_sort.length() - 1);
    }

    /**
    *判断时间戳是否在一分钟内
    **/
    private static boolean checkTimeStamp(long ts)
    {
        if (Math.abs(ts - System.currentTimeMillis() / 1000) > 60)
        {
            return false;
        }
        return true;
    }

    /**
    *读取文本文件中的数据并输出为String
    **/
    public static String txt2String(File file){
        StringBuilder result = new StringBuilder();
        try{
            BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法,一次读一行
                result.append(System.lineSeparator()+s);
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return result.toString();
    }

}

     因为把很多公共的代码给抽象出来,其中对传进来的参数进行排序后利用自身的RSA公私钥进行加解密验签操作,并且对时间戳进行了一分钟的有效性校验,校验成功则返回true继续执行下面的代码,否则返回false执行断言。

    简而言之,要实现拦截器功能需要完成2个步骤:

  • 创建自己的拦截器类并实现 HandlerInterceptor 接口
  • 重写WebMvcConfigurerAdapter类中的addInterceptors方法把自定义的拦截器类添加进来即可

   至于采用RSA加解密的规则,可以参考下面一段代码(另外一篇关于RSA加解密的博客大家也可以看看RSA证书加解密

package com.youfuli.vendor.utils;

import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by 李啸天 on 2019/3/13.
 */
public class RSA2 {
    public static final String KEY_ALGORITHM = "RSA";
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 2048;

    //获得公钥字符串
    public static String getPublicKeyStr(Map<String, Object> keyMap) throws Exception {
        //获得map中的公钥对象 转为key对象
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        //编码返回字符串
        return encryptBASE64(key.getEncoded());
    }


    //获得私钥字符串
    public static String getPrivateKeyStr(Map<String, Object> keyMap) throws Exception {
        //获得map中的私钥对象 转为key对象
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        //编码返回字符串
        return encryptBASE64(key.getEncoded());
    }

    //获取公钥
    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder()).decodeBuffer(key);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    //获取私钥
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder()).decodeBuffer(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    //解码返回byte
    public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }


    //编码返回字符串
    public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    //***************************签名和验证*******************************
    public static byte[] sign(byte[] data, String privateKeyStr) throws Exception {
        PrivateKey priK = getPrivateKey(privateKeyStr);
        Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
        sig.initSign(priK);
        sig.update(data);
        return sig.sign();
    }

    public static boolean verify(byte[] data, byte[] sign, String publicKeyStr) throws Exception {
        PublicKey pubK = getPublicKey(publicKeyStr);
        Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
        sig.initVerify(pubK);
        sig.update(data);
        return sig.verify(sign);
    }

    //************************加密解密**************************
    public static byte[] encrypt(byte[] plainText, String publicKeyStr) throws Exception {
        PublicKey publicKey = getPublicKey(publicKeyStr);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = plainText.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        int i = 0;
        byte[] cache;
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(plainText, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(plainText, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptText = out.toByteArray();
        out.close();
        return encryptText;
    }

    public static byte[] decrypt(byte[] encryptText, String privateKeyStr) throws Exception {
        PrivateKey privateKey = getPrivateKey(privateKeyStr);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        int inputLen = encryptText.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptText, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptText, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] plainText = out.toByteArray();
        out.close();
        return plainText;
    }


    public static void main(String[] args) {
        Map<String, Object> keyMap;
        byte[] cipherText;
        String input = "Hello World!";
        try {
            keyMap = initKey();
            String publicKey = getPublicKeyStr(keyMap);
            System.out.println("公钥------------------");
            System.out.println(publicKey);
            String privateKey = getPrivateKeyStr(keyMap);
            System.out.println("私钥------------------");
            System.out.println(privateKey);

            System.out.println("测试可行性-------------------");
            System.out.println("明文=======" + input);

            cipherText = encrypt(input.getBytes(), publicKey);
            //加密后的东西
            System.out.println("密文=======" + new String(cipherText));
            //开始解密
            byte[] plainText = decrypt(cipherText, privateKey);
            System.out.println("解密后明文===== " + new String(plainText));
            System.out.println("验证签名-----------");

            String str = "431f4dfd4c862122fb8a4e004c9a093b";
            System.out.println("\n原文:" + str);
            byte[] signature = sign(str.getBytes(), privateKey);
            System.out.println(Base64.encodeBase64String(signature));
            boolean status = verify(str.getBytes(), signature, publicKey);
            System.out.println("验证情况:" + status);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator
                .getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

}

此次写篇博客记录一下,既是积累也是分享。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值