如果系統明文傳輸的數據會被不明身份的人用抓包工具抓取,從而威脅系統和數據的安全性,那么就要使用加密的方式來對數據接口進行加密
加密方式:
摘要算法:加密方式不需要秘鑰,加密后的數據無法被解密,只有輸入相同的明文數據經過相同的消息摘要算法才能得到相同密文
對稱加密:明文數據通過秘鑰進行加密,傳輸到后台,在通過相同的密碼進行解密操作,缺點就是如何把秘鑰安全的傳輸到解密者
非對稱加密:數字簽名使用私鑰生成簽名,使用公鑰驗證簽名 通過公鑰加密傳輸,通過私鑰進行解密操作
秘鑰生成工具:OpenSSlL
使用OpenSSl生成rsa密鑰對,密鑰長度介於 512 - 65536 之間(JDK 中默認長度是1024),且必須是64 的倍數。密鑰的常用文件格式有pem(文本存儲)或者der(二進制存儲),當使用Java API生成RSA密鑰對時,公鑰以X.509格式編碼,私鑰以PKCS#8格式編碼,RSA使用pkcs協議定義密鑰的存儲結構等內容
通過OpenssL生成的私鑰還要經過OpenSSL進行PKCS#8的編碼操作,JAVA需要使用的私鑰需要經過PKCS#8編碼
生成秘鑰對的方法可以參考這篇帖子
猛點我!
一般直接通過URL直接訪問后端接口,實不可取的,通過Zuul進行解密轉發,從而保證安全性
Zuul
YML配置文件
server:
port: #Zuul的端口號
spring:
application:
name: #Zuul的name
zuul:
routes:
user_name: #user微服務的名稱
path: /user/** #配置請求URL的請求規則
serviceId: user_name #指定Eureka注冊中心中的服務id
strip-prefix: true
sentiviteHeaders:
customSensitiveHeaders: true #讓zuul處理cookie和重定向
#Eurkea注冊發現中心
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6868/eureka/
instance:
prefer-ip-address: true
Zuul.pom文件配置
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies>
ZuuL啟動類
@SpringBootApplication @EnableEurekaClient //注冊發現中心 @EnableZuulProxy //網關過濾器 public class EncryptApplication { public static void main(String[] args) { SpringApplication.run(EncryptApplication.class); } }
//秘鑰
public class RsaKeys { //服務器公鑰 private static final String serverPubKey = "使用OpenSSL生成的公鑰"; //服務器私鑰(經過pkcs8格式處理) private static final String serverPrvKeyPkcs8 = "使用OpenSSL生成的私鑰經過pkcs8格式處理"; public static String getServerPubKey() { return serverPubKey; } public static String getServerPrvKeyPkcs8() { return serverPrvKeyPkcs8; }
}
//RSA解碼加密,網上搜一下,很多
@Service("RsaService") public class RsaServiceImpl implements RsaService { /*** * RSA解密 * * @param encryptData * @return * @throws Exception */ public String RSADecryptDataPEM(String encryptData, String prvKey) throws Exception { byte[] encryptBytes = encryptData.getBytes(); byte[] prvdata = RSA.decryptByPrivateKey(Base64Utils.decode(encryptData), prvKey); String outString = new String(prvdata, "UTF-8"); return outString; } @Override public String RSADecryptDataBytes(byte[] encryptBytes, String prvKey) throws Exception { // TODO Auto-generated method stub byte[] prvdata = RSA.decryptByPrivateKey(encryptBytes, prvKey); String outString = new String(prvdata, "utf-8"); return outString; } /*** * RSA加密 * * @param data * @return * @throws Exception */ public String RSAEncryptDataPEM(String data, String pubKey) throws Exception { byte[] pubdata = RSA.encryptByPublicKey(data.getBytes("UTF-8"), pubKey); String outString = new String(Base64Utils.encode(pubdata)); return outString; } @Override public String getRsaAlgorithm() { // TODO Auto-generated method stub KeyFactory keyFactory = null; try { keyFactory = KeyFactory.getInstance("RSA"); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } return keyFactory.getAlgorithm(); } }
創建filter過濾器繼承ZuulFilter
@Component public class RSARequestFilter extends ZuulFilter { //RSA加密解密接口
@Autowired private RsaService rsaService; @Override public String filterType() { //過濾器在什么時候執行,在解密之前執行 return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { //執行順序 return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1; } @Override public boolean shouldFilter() { //是否使用過濾器 return true; } @Override public Object run() throws ZuulException { //獲取Conext對象應用上下文, 從中獲取req,res對象 RequestContext cxt = RequestContext.getCurrentContext(); HttpServletRequest request = cxt.getRequest(); HttpServletResponse response = cxt.getResponse(); //存放加密數據的變量 String requestData = null; String decodeData = null; try { //從req中獲取請求的內容,獲取通過公鑰加密后的數據, ServletInputStream inputStream = request.getInputStream(); //通過流的工具類,吧流的信息轉換成string類型的,指定字符集utf-8,得到公鑰加密后的數據 requestData = StreamUtils.copyToString(inputStream, Charsets.UTF_8); System.out.println("加密后的:" + requestData); if (!Strings.isNullOrEmpty(requestData)) { //如果得到的數據不為空,進行解密操作 decodeData = rsaService.RSADecryptDataPEM(requestData, RsaKeys.getServerPrvKeyPkcs8()); System.out.println("解密后的:" + decodeData); } //吧解密后的數據轉發給對應服務request if (!Strings.isNullOrEmpty(decodeData)) { byte[] bytes = decodeData.getBytes(); cxt.setRequest(new HttpServletRequestWrapper(request) { @Override public ServletInputStream getInputStream() { return new ServletInputStreamWrapper(bytes); } @Override public int getContentLength() { return bytes.length; } @Override public long getContentLengthLong() { return bytes.length; } }); } //進行轉碼操作 cxt.addZuulRequestHeader("Content-Type" , MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8"); } catch (Exception e) { e.printStackTrace(); } return null; } }
在POSTMan測試前,可以先成加密后的接口密文, 比如http://127.0.0.1:9002/user/user/2 本機的9002是你的Zuul網關端口,訪問配置文件中要攔截的接口,解密后,轉發到user微服務查詢id 等於2的用戶
測試類
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = EncryptApplication.class) public class EncryptTest { @Autowired private RsaService rsaService; @Test public void genEncryptDataByPubKey() { String data = "{\"需要加密的明文\":\"需要加密的明文\"}"; try { String encData = rsaService.RSAEncryptDataPEM(data, RsaKeys.getServerPubKey()); } catch (Exception e) { e.printStackTrace(); } } @Test public void pojie() throws Exception { String mima = "明文加密后的秘鑰"; //調用解密 String s = rsaService.RSADecryptDataPEM(mima, RsaKeys.getServerPrvKeyPkcs8()); System.out.println(s); } }
寫的不是特別好,主要是自己學習用