此文已由做者趙計剛受權網易雲社區發佈。html
歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。java
注:本章代碼基於《第五章 企業項目開發--mybatis註解與xml並用》的代碼,連接以下:mysql
http://www.cnblogs.com/java-zhao/p/5120792.html算法
在實際項目中,咱們會存儲用戶狀態信息,基本使用兩種手段:cookie和sessionspring
一、cookie:sql
1.一、流程:chrome
服務端將cookie的屬性值設置好以後,經過HttpServletResponse將cookie寫入響應頭;apache
服務端從請求頭中經過HttpServletRequest將全部cookie(當前被訪問頁面的全部cookie)讀取出來,遍歷尋找指定name的cookie便可。api
流程與下邊的例子對應一下,馬上就清楚了。數組
1.二、包含選項:(當前使用最多的就是version0版本的cookie,這裏僅說version0的幾個最經常使用的屬性)
name:cookie名
value:cookie值
domain:cookie寫在哪個域下(例如:aaa.com)
path:cookie寫在哪個uri下(例如:/admin)
expires:cookie過時時間,超過某個指定時間點cookie就消失,固然設成-1的話,關閉瀏覽器cookie就會消失;指定了時間點的話,該cookie就會存在硬盤上
對於domain,一個例子解釋清楚:
假設:www.baidu.com和e.baidu.com兩個域,path="/",
1)當domain="baidu.com"時,兩個域均可以訪問生成的cookie
2)當domain="www.baidu.com"時,只有前一個域有生成cookie
對於path,用一個例子解釋清楚:
假設http://www.baidu.com/dev/zz/和http://www.baidu.com/dev/xx/,
1)當path="/dev",生成的cookie上邊的兩個地址共享;
2)當path="/dev/zz",生成的cookie只有第一個地址有,第二個地址沒有。
根據上邊的例子,對於path與domain是相互依賴的,共同決定某個頁面地址是否能夠生成指定的cookie
1.三、優勢:
基本不須要使用服務器空間,用戶會話信息存在了客戶端(這個只是列出來,基本不能算優勢,反而應該算是缺點,由於存在客戶端不安全)
在分佈式系統中,由於cookie存在了瀏覽器,因此不須要考慮同一個用戶怎樣定位到同一臺服務器這個問題
1.四、缺點:
cookie個數和總大小有限制,通常而言,每一個域下可存放<=50個cookie,全部cookie的總大小<=4095個字節左右,chrome瀏覽器的cookie總大小會大不少(>80000個字節)。這些數據參考自《深刻分析Java Web技術內幕》
若是cookie不少,客戶端和服務端會浪費很對帶寬,並且也會拖慢速度,通常而言,可使用壓縮算法作壓縮,可是,可能壓縮後的size也很大
不安全,若是被竊聽者劫取到你的cookie,即便你對這些cookie作了加密,使其看不到cookie中的信息,可是其也能夠經過該cookie模擬登陸行爲,而後得到一些權限,執行一些操做。(這是個疑問,記住我這個功能就是這樣作的,怎樣作到安全的呢?)(答案見最下方)
二、項目中使用cookie存儲用戶會話狀態
代碼實現:本章代碼基於上一章代碼實現,下面只列出修改或添加的一些類。
一般狀況下,若是工具類不少,咱們能夠重開一個項目,例如:ssmm0-util,而後在這個子項目中添加類,固然,若是項目自己也不大,直接將工具類放在ssmm0-data子項目下便可。
2.一、ssmm0-data:
2.1.一、pom.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4 5 <modelVersion>4.0.0</modelVersion> 6 7 <!-- 指定父模塊 --> 8 <parent> 9 <groupId>com.xxx</groupId> 10 <artifactId>ssmm0</artifactId> 11 <version>1.0-SNAPSHOT</version> 12 </parent> 13 14 <groupId>com.xxx.ssmm0</groupId> 15 <artifactId>ssmm0-data</artifactId> 16 17 <name>ssmm0-data</name> 18 <packaging>jar</packaging><!-- 只是做爲其餘模塊使用的工具 --> 19 20 <!-- 引入實際依賴 --> 21 <dependencies> 22 <!-- mysql --> 23 <dependency> 24 <groupId>mysql</groupId> 25 <artifactId>mysql-connector-java</artifactId> 26 </dependency> 27 <!-- 數據源 --> 28 <dependency> 29 <groupId>org.apache.tomcat</groupId> 30 <artifactId>tomcat-jdbc</artifactId> 31 </dependency> 32 <!-- mybatis --> 33 <dependency> 34 <groupId>org.mybatis</groupId> 35 <artifactId>mybatis</artifactId> 36 </dependency> 37 <dependency> 38 <groupId>org.mybatis</groupId> 39 <artifactId>mybatis-spring</artifactId> 40 </dependency> 41 <!-- servlet --><!-- 爲了會用cookie --> 42 <dependency> 43 <groupId>javax.servlet</groupId> 44 <artifactId>javax.servlet-api</artifactId> 45 </dependency> 46 <!-- bc-加密 --> 47 <dependency> 48 <groupId>org.bouncycastle</groupId> 49 <artifactId>bcprov-jdk15on</artifactId> 50 </dependency> 51 <!-- cc加密 --> 52 <dependency> 53 <groupId>commons-codec</groupId> 54 <artifactId>commons-codec</artifactId> 55 </dependency> 56 </dependencies> 57 </project>
說明:
引入了servlet-jar包,主要是須要HttpServletResponse向響應頭寫cookie和使用HttpServletRequest從請求頭讀取cookie;
引入commons-codec和bouncy-castle,主要是爲了實現AES加密與Base64編碼。
注意:
servlet-jar的scope是provided,不具備傳遞性,因此若是在ssmm0-userManagement中須要用到servlet-jar,則ssmm0-userManagement須要本身再引一遍
commons-codec和bouncy-castle的scope都是採用了默認的compile,具備傳遞性,因此若是在ssmm0-userManagement中須要用到commons-codec和bouncy-castle,ssmm0-userManagement不須要本身再引一遍了
2.1.二、AESUtil:(AES加密)
1 package com.xxx.util; 2 3 import java.io.UnsupportedEncodingException; 4 import java.security.InvalidAlgorithmParameterException; 5 import java.security.InvalidKeyException; 6 import java.security.Key; 7 import java.security.NoSuchAlgorithmException; 8 import java.security.NoSuchProviderException; 9 import java.security.Security; 10 import java.security.spec.InvalidKeySpecException; 11 12 import javax.crypto.BadPaddingException; 13 import javax.crypto.Cipher; 14 import javax.crypto.IllegalBlockSizeException; 15 import javax.crypto.KeyGenerator; 16 import javax.crypto.NoSuchPaddingException; 17 import javax.crypto.SecretKey; 18 import javax.crypto.spec.SecretKeySpec; 19 20 import org.apache.commons.codec.binary.Base64; 21 import org.bouncycastle.jce.provider.BouncyCastleProvider; 22 23 /** 24 * 基於JDK或BC的AES算法,工做模式採用ECB 25 */ 26 public class AESUtil { 27 private static final String ENCODING = "UTF-8"; 28 private static final String KEY_ALGORITHM = "AES";//產生密鑰的算法 29 private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//加解密算法 格式:算法/工做模式/填充模式 注意:ECB不使用IV參數 30 private static final String ENCRYPT_KEY = "WAUISIgpBh1R/+3f2Ze4csUU/tl/O8x56DbVb7mTs7w=";//這個是先運行一遍getKey()方法產生的,而後卸載了這裏。 31 32 /** 33 * 產生密鑰(線下產生好,而後配在項目中) 34 */ 35 public static byte[] getKey() throws NoSuchAlgorithmException{ 36 Security.addProvider(new BouncyCastleProvider());//在BC中用,JDK下去除 37 KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); 38 keyGenerator.init(256);//初始化密鑰長度,128,192,256(選用192和256的時候須要配置無政策限制權限文件--JDK6) 39 SecretKey key =keyGenerator.generateKey();//產生密鑰 40 return key.getEncoded(); 41 } 42 43 /** 44 * 還原密鑰:二進制字節數組轉換爲Java對象 45 */ 46 public static Key toKey(byte[] keyByte){ 47 Security.addProvider(new BouncyCastleProvider());//在BC中用,JDK下去除 48 return new SecretKeySpec(keyByte, KEY_ALGORITHM); 49 } 50 51 /** 52 * AES加密,並轉爲16進制字符串或Base64編碼字符串 53 */ 54 public static String encrypt(String data, byte[] keyByte) throws InvalidKeyException, 55 NoSuchAlgorithmException, 56 InvalidKeySpecException, 57 NoSuchPaddingException, 58 IllegalBlockSizeException, 59 BadPaddingException, 60 UnsupportedEncodingException, 61 NoSuchProviderException, 62 InvalidAlgorithmParameterException { 63 Key key = toKey(keyByte);//還原密鑰 64 //Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);//JDK下用 65 Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM,"BC");//BC下用 66 cipher.init(Cipher.ENCRYPT_MODE, key);//設置加密模式而且初始化key 67 byte[] encodedByte = cipher.doFinal(data.getBytes(ENCODING)); 68 return Base64.encodeBase64String(encodedByte);//藉助CC的Base64編碼 69 } 70 71 /** 72 * AES解密 73 * @param data 待解密數據爲字符串 74 * @param keyByte 密鑰 75 */ 76 public static String decrypt(String data, byte[] keyByte) throws InvalidKeyException, 77 NoSuchAlgorithmException, 78 InvalidKeySpecException, 79 NoSuchPaddingException, 80 IllegalBlockSizeException, 81 BadPaddingException, 82 UnsupportedEncodingException, 83 NoSuchProviderException, 84 InvalidAlgorithmParameterException { 85 Key key = toKey(keyByte);//還原密鑰 86 //Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);//JDK下用 87 Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM,"BC");//BC下用 88 cipher.init(Cipher.DECRYPT_MODE, key); 89 byte[] decryptByte = cipher.doFinal(Base64.decodeBase64(data));//注意data不能夠直接採用data.getByte()方法轉化爲字節數組,不然會拋異常 90 return new String(decryptByte, ENCODING);//這裏注意用new String() 91 } 92 93 /** 94 * 測試 95 */ 96 public static void main(String[] args) throws NoSuchAlgorithmException, 97 InvalidKeyException, 98 InvalidKeySpecException, 99 NoSuchPaddingException, 100 IllegalBlockSizeException, 101 BadPaddingException, 102 UnsupportedEncodingException, 103 NoSuchProviderException, 104 InvalidAlgorithmParameterException { 105 String data = "找一個好姑娘作老婆是個人夢 想!"; 106 /*************測試encryptAESHex()、decrypt()**************/ 107 System.out.println("原文-->"+data); 108 byte[] keyByte3 = Base64.decodeBase64(ENCRYPT_KEY); 109 System.out.println("密鑰-->"+Base64.encodeBase64String(keyByte3));//這裏將二進制的密鑰使用base64加密保存,這也是在實際中使用的方式 110 String encodedStr = AESUtil.encrypt(data, keyByte3); 111 System.out.println("加密後-->"+encodedStr); 112 System.out.println("解密String後-->"+AESUtil.decrypt(encodedStr, keyByte3)); 113 } 114 }
說明:
關於AES的具體內容,能夠參照"Java加密與解密系列的第八章",具體連接以下:
http://www.cnblogs.com/java-zhao/p/5087046.html
AESUtil這個類就是根據上邊這篇博客的AESJDK這個類進行修改封裝的。
注意:
AESUtil類的ENCRYPT_KEY變量的值是先運行了其中的getKey()方法,而後又經過Base64編碼產生的,產生這個密鑰後,咱們將這個密鑰寫在類中。簡單來講,就是線下先產生密鑰,而後寫在程序中,以後getKey()方法就再沒有用了,能夠直接刪掉。
關於生成密鑰的這段代碼以下:
String key = Base64.encodeBase64String(AESUtil.getKey());
免費領取驗證碼、內容安全、短信發送、直播點播體驗包及雲服務器等套餐
更多網易技術、產品、運營經驗分享請點擊。
相關文章:
【推薦】 揭祕醫療安全防衛戰:「咱們仍在購買不安全的醫療設備」