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;
}
}
此次写篇博客记录一下,既是积累也是分享。