Hey,朋友。你不一定要逆风翻盘,但请一定要向阳而生。
一、开场白
如果系统间涉及2端调用 [调用方、被调用方] 是分离的 ,无论是后端对后端 还是前端对后端 或者终端对后端 正常情况都需要结合实际业务场景设计鉴权方案,数据会有加解的过程。这样设计的好处是:
1、有助于防止外部请求非法获得系统数据
2、有助于防止数据内容传输过程中被窃取、篡改,伪造
3、有助于防止外部通过非法手段对系统发起攻击
本文的例子主要是针对云云对接中 “后端对后端” 的Token鉴权方案,技术实现主要基于RSA公私钥加解密方案实现系统间调用。
关键词:鉴权业务发生场景、RSA、公私钥解密,AOP、HTTP协议、时序图
二、鉴权方案设计核心点:
序号 | 核心点 | 说明 |
1 | 实现公钥接口 | 用户生成秘钥对(公钥、私钥),基于RSA实现,公钥返回给客户端,私钥放在缓存中。 |
2 | 实现令牌接口 | 1、客户端通过公钥加密,获取令牌 2、令牌接口通过私钥解对客户端请求时携带的密文进行解密,同时校验公钥的合法性和有效性。 3、解密通过后生成令牌(Token)并返回给客户端 |
3 | 实现AOP切面鉴权 | 定义切面注解,切点,切面,连接点,通知(环绕)可参考我写的另外一篇:手把手教学切面编程实战之Spring AOP基于注解实现 |
4 | 实现业务功能接口 | 在需要鉴权的函数上添加切面注解进行鉴权,实现业务接口查询、计算、封装业务数据并返回 |
三、鉴权方案时序图
通过这个时序图你能非常清晰的了解系统鉴权方案中各个环节的核心参与者,接口调用顺序,各个核心参与者需要完成的事情。
四、鉴权方案中RSA工具源码展示
package com.demo.util;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* 功能描述:RSA加密工具类
* @author youyun.xu
*/
public class RSAUtil {
private static final Logger logger = LoggerFactory.getLogger(RSAUtil.class);
/**
* 公钥对象KEY
*/
public static final String PUBLIC_KEY = "xuyouyun";
/**
* 私钥对象KEY
*/
public static final String PRIVATE_KEY = "xuyouyun";
/**
* 生成公私钥对
* @param keySize 公私钥对大小
* @return 公私钥对象和Base64编码后的公私钥String字符串
* @throws NoSuchAlgorithmException
* @since 10.15
*/
public static Map<String, Key> createKey(int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keySize, new SecureRandom());
KeyPair key = keyGen.generateKeyPair();
PublicKey pubKey = key.getPublic();
PrivateKey priKey = key.getPrivate();
Map<String, Key> map = new HashMap<String, Key>();
map.put(PUBLIC_KEY, pubKey);
map.put(PRIVATE_KEY, priKey);
return map;
}
/**
* 公钥或私钥转为字符串
*/
public static String encode(Key key) {
return Base64.encodeBase64String(key.getEncoded());
}
/**
* 通过字符串转为私钥
*/
public static PrivateKey decodePrivateKey(String privateKeyData) {
PrivateKey privateKey = null;
try {
byte[] decodeKey = Base64.decodeBase64(privateKeyData); //将字符串Base64解码
PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec(decodeKey); //创建pkcs8证书封装类
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); //指定RSA
privateKey = keyFactory.generatePrivate(pkcs8); //生成私钥
} catch (Exception e) {
logger.error("通过字符串转为私钥异常,privateKeyData=" + privateKeyData, e);
}
return privateKey;
}
/**
* 通过字符串转为公钥
*/
public static PublicKey decodePublicKey(String publicKeyData) {
PublicKey publicKey = null;
try {
byte[] decodeKey = Base64.decodeBase64(publicKeyData);
X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodeKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(x509);
} catch (Exception e) {
logger.error("通过字符串转为公钥异常,publicKeyData=" + publicKeyData, e);
}
return publicKey;
}
/**
* RSA加密<br>
* 注意:此方法支持公钥加密私钥解密或者私钥加密公钥解密
*
* @param key 公钥或者私钥
* @param message 准备加密的原文
* @return 加密后的密文
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public static String encrypt(Key key, String message) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] miwen = cipher.doFinal(message.getBytes());
return Base64.encodeBase64String(miwen);
}
/**
* RSA解密<br>
* 注意:此方法支持公钥加密私钥解密或者私钥加密公钥解密
*
* @param key 私钥或者公钥
* @param message 已加密的密文
* @return 解密后的原文
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public static String decrypt(Key key, String message) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] miwen = Base64.decodeBase64(message);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(miwen);
return new String(result);
}
/**
* 测试主函数
*/
public static void main(String[] args) throws Exception {
//1、生成密钥对(公钥、私钥)
long start = System.currentTimeMillis();
Map<String, Key> map = RSAUtil.createKey(2048);
long createKey = System.currentTimeMillis();
System.out.println("生成密码时间:" + (createKey - start));
//2、打印公私钥信息
System.out.println("PUBLIC_KEY:"+RSAUtil.encode(map.get(RSAUtil.PUBLIC_KEY)));
System.out.println("PRIVATE_KEY:"+RSAUtil.encode(map.get(RSAUtil.PRIVATE_KEY)));
}
}
- 公钥(PUBLIC_KEY ) :根据实际业务进行设定,客户端会通过颁发的公钥对明文进行加密,然后把加密的内容发送到服务端。
- 私钥(PRIVATE_KEY ):根据实际业务进行设定,服务端会通过私钥对客户端发送过来的密文进行解密。
工具类中我提供非常完整的注释,你可以马上通过上面的工具类数进行测试:生成一个秘钥对,并且打印查看秘钥对信息。在实际业务中可以参考文中设计方案,结合Redis,AOP,RSA 进行系统鉴权方案设计。
Hi,既然来都来啦,给点个赞吧。