第六章 企業項目開發--cookie

注:本章代碼基於《第五章 企業項目開發--mybatis註解與xml並用》的代碼,連接以下:html

http://www.cnblogs.com/java-zhao/p/5120792.htmljava

在實際項目中,咱們會存儲用戶狀態信息,基本使用兩種手段:cookie和sessionmysql

一、cookie:web

1.一、流程:算法

  • 服務端將cookie的屬性值設置好以後,經過HttpServletResponse將cookie寫入響應頭;
  • 服務端從請求頭中經過HttpServletRequest將全部cookie(當前被訪問頁面的全部cookie)讀取出來,遍歷尋找指定name的cookie便可。

流程與下邊的例子對應一下,馬上就清楚了。spring

1.二、包含選項:(當前使用最多的就是version0版本的cookie,這裏僅說version0的幾個最經常使用的屬性)sql

  • name:cookie名
  • value:cookie值
  • domain:cookie寫在哪個域下(例如:aaa.com)
  • path:cookie寫在哪個uri下(例如:/admin)
  • expires:cookie過時時間,超過某個指定時間點cookie就消失,固然設成-1的話,關閉瀏覽器cookie就會消失;指定了時間點的話,該cookie就會存在硬盤上

對於domain,一個例子解釋清楚:chrome

假設:www.baidu.com和e.baidu.com兩個域,path="/",數據庫

1)當domain="baidu.com"時,兩個域均可以訪問生成的cookieapache

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>
View Code

說明:

  • 引入了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 }
View Code

說明:

關於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());

2.1.三、CookieUtil:(cookie的基本操做:增刪查,注意沒有改)

 1 package com.xxx.util;
 2 
 3 import javax.servlet.http.Cookie;
 4 import javax.servlet.http.HttpServletRequest;
 5 import javax.servlet.http.HttpServletResponse;
 6 
 7 /**
 8  * cookie操做相關類
 9  */
10 public class CookieUtil {
11     
12     /**
13      * 增長cookie
14      * @param name        cookie名
15      * @param value        cookie值
16      * @param domain    指定cookie寫在哪一個域下
17      * @param path        指定cookie存在那個路徑下(其實就是一個uri)
18      * @param expiry    指定cookie過時時間
19      */
20     public static void addCookie(String name,
21                                  String value,
22                                  String domain,
23                                  String path,
24                                  int expiry,
25                                  HttpServletResponse response){
26         Cookie cookie = new Cookie(name, value);
27         
28         cookie.setDomain(domain);
29         cookie.setPath(path);
30         cookie.setMaxAge(expiry);
31         
32         response.addCookie(cookie);
33     }
34     
35     /**
36      * 獲取cookie
37      * @param request
38      * @param key    將要查找的cookie的名
39      * @return        返回cookie的值
40      */
41     public static String getCookie(HttpServletRequest request, String key){
42         Cookie[] cookies = request.getCookies();
43         /*
44          * 注意在執行循環以前,不須要判斷cookies是否爲空,由於iterator會在循環前執行hasNext;
45          * 可是,最好判斷一下,這樣若是cookies爲null的話,咱們就能夠直接返回,不須要執行循環,
46          * 這樣就不須要平白的建立一個iterator對象並執行一次hasNext。
47          * 
48          * 下邊的判斷也能夠換成這樣CollectionUtils.isEmpty(Arrays.asList(cookies));
49          */
50         if(cookies == null || cookies.length == 0){
51             return null;
52         }
53         
54         for(Cookie cookie : cookies){
55             if(cookie.getName().equals(key)){
56                 return cookie.getValue();
57             }
58         }
59         return null;
60     }
61     
62     /**
63      * 清空指定cookie
64      * 值得注意的是,清空cookie不是隻將相應的cookie的value置空便可,其餘信息依舊設置,
65      * 最後加在響應頭中,去覆蓋瀏覽器端的相應的cookie
66      */
67     public static void removeCookie(String name,
68                                     String domain,
69                                     String path,
70                                     HttpServletResponse response){
71         Cookie cookie = new Cookie(name, null);
72         
73         cookie.setMaxAge(0);
74         cookie.setDomain(domain);
75         cookie.setPath(path);
76         
77         response.addCookie(cookie);
78     }
79 }
View Code

注意:

  • 在使用response將cookie寫入響應頭後,咱們能夠在瀏覽器查看響應頭中的Set-Cookie信息,每添加一條cookie,就會有一個Set-Cookie;
  • 在使用request從請求頭中獲取cookie的時候,咱們能夠在瀏覽器查看請求頭中的Cookie信息,全部cookie信息會以key=value的形式、多個key-value對之間以分號隔開的形式存成一條。
  • 在刪除cookie的時候,必定要注意咱們不是隻將相應的cookie的value置空便可,其餘信息依舊設置,最後加在響應頭中,去覆蓋瀏覽器端的相應的cookie

前兩條注意點能夠查看後邊的截圖來驗證。

2.1.四、Admin:

 1 package com.xxx.model.userManagement;
 2 
 3 import com.alibaba.fastjson.JSON;
 4 
 5 /**
 6  * 管理員 
 7  */
 8 public class Admin {
 9     private int id;
10     private String username;
11     private String password;
12 
13     public int getId() {
14         return id;
15     }
16 
17     public void setId(int id) {
18         this.id = id;
19     }
20 
21     public String getUsername() {
22         return username;
23     }
24 
25     public void setUsername(String username) {
26         this.username = username;
27     }
28 
29     public String getPassword() {
30         return password;
31     }
32 
33     public void setPassword(String password) {
34         this.password = password;
35     }
36     
37     //將json串轉爲Admin
38     public static Admin parseJsonToAdmin(String jsonStr){
39         try {
40             return JSON.parseObject(jsonStr, Admin.class);
41         } catch (Exception e) {
42             e.printStackTrace();
43             return null;
44         }
45     }
46     
47     //將當前實例轉化爲json串
48     public String toJson(){
49         return JSON.toJSONString(this);
50     }
51 }
View Code

說明:在Admin中,增長了兩個方法,一個是將當前實例轉化爲json串,一個是將json串轉化爲Admin;這是由於cookie的傳輸只能傳遞字符串而不能傳遞對象

 

2.二、ssmm0-userManagement:

2.2.一、pom.xml

注意:這個類沒有改動,之因此列出來,是要提醒去注意compile的傳遞性與provided的不可傳遞性

2.2.二、AdminCookieUtil

 1 package com.xxx.util.admin;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 import javax.servlet.http.HttpServletResponse;
 5 
 6 import org.apache.commons.codec.binary.Base64;
 7 import org.apache.commons.lang.StringUtils;
 8 
 9 import com.xxx.model.userManagement.Admin;
10 import com.xxx.util.AESUtil;
11 import com.xxx.util.CookieUtil;
12 
13 /**
14  * Admin的cookie操做類
15  */
16 public class AdminCookieUtil {
17     private static final String COOKILE_NAME = "allinfo";
18     private static final String USER_NAME = "username";
19     private static final String DOMAIN = "";//when working on localhost the cookie-domain must be set to "" or NULL or FALSE
20     private static final String PATH = "/";
21     private static final int EXPIRY = -1;//關閉瀏覽器,則cookie消失
22     
23     private static final String ENCRYPT_KEY = "gEfcsJx8VUT406qI4r6/3104noOzI/YAaS98cToY+nI=";//加解密密鑰
24      
25     public static void addLoginCookie(Admin admin, HttpServletResponse response){
26         try{
27             CookieUtil.addCookie(COOKILE_NAME, AESUtil.encrypt(admin.toJson(), Base64.decodeBase64(ENCRYPT_KEY)), DOMAIN, PATH, EXPIRY, response);
28             CookieUtil.addCookie(USER_NAME, admin.getUsername(), DOMAIN, PATH, EXPIRY, response);
29         }catch (Exception e) {
30             e.printStackTrace();
31         }
32     }
33     
34     public static Admin getLoginCookie(HttpServletRequest request){
35         String json = CookieUtil.getCookie(request, COOKILE_NAME);
36         if(StringUtils.isNotBlank(json)){
37             try{
38                 return Admin.parseJsonToAdmin(AESUtil.decrypt(json, Base64.decodeBase64(ENCRYPT_KEY)));
39             }catch (Exception e) {
40                 e.printStackTrace();
41                 return null;
42             }
43         }
44         return null;
45     }
46 }
View Code

注意點:

  • 在localhost下設置的域,必須是""或null或false

步驟:(這就是cookie的使用流程)

  • 登陸成功後,寫兩個cookie,一個是username=value,一個是allinfo=一個加密串,這個加密串是先將admin實例轉化爲json串,而後經過AES進行加密。
  • 在執行findAdmin方法時,假設前提是須要用戶處於登陸狀態(即存在cookie),這個時候讀cookie,先從request中獲取全部cookie,而後遍歷這些cookie,找出其中是否存在name爲allinfo的cookie,若是沒有,沒登陸,若是有,就登陸了,將allinfo的cookie的value先解密再轉化爲Admin,以後使用該實例作一些事兒。

2.2.三、AdminController

  1 package com.xxx.web.admin;
  2 
  3 import java.util.List;
  4 
  5 import javax.servlet.http.HttpServletRequest;
  6 import javax.servlet.http.HttpServletResponse;
  7 
  8 import org.springframework.beans.factory.annotation.Autowired;
  9 import org.springframework.stereotype.Controller;
 10 import org.springframework.web.bind.annotation.RequestMapping;
 11 import org.springframework.web.bind.annotation.RequestParam;
 12 import org.springframework.web.bind.annotation.ResponseBody;
 13 import org.springframework.web.servlet.ModelAndView;
 14 
 15 import com.xxx.model.userManagement.Admin;
 16 import com.xxx.service.userManagement.AdminService;
 17 import com.xxx.util.admin.AdminCookieUtil;
 18 
 19 /**
 20  * adminController
 21  */
 22 @Controller
 23 @RequestMapping("/admin")
 24 public class AdminController {
 25     
 26     @Autowired
 27     private AdminService adminService;
 28     
 29     /**
 30      * 管理員註冊
 31      */
 32     @ResponseBody
 33     @RequestMapping("/register")
 34     public boolean register(@RequestParam("username") String username,
 35                             @RequestParam("password") String password){
 36         Admin admin = new Admin();
 37         admin.setUsername(username);
 38         admin.setPassword(password);
 39         
 40         boolean isRegisterSuccess = adminService.register(admin);
 41         
 42         return isRegisterSuccess;
 43     }
 44     
 45     /**
 46      * 管理員登陸
 47      */
 48     @RequestMapping("/login")
 49     public ModelAndView login(@RequestParam("username") String username,
 50                               @RequestParam("password") String password,
 51                               HttpServletResponse response){
 52         
 53         
 54         Admin admin = adminService.login(username, password);
 55         
 56         ModelAndView modelAndView = new ModelAndView();
 57         if(admin == null){
 58             modelAndView.addObject("message", "用戶不存在或者密碼錯誤!請從新輸入");
 59             modelAndView.setViewName("error");
 60         }else{
 61             modelAndView.addObject("admin", admin);
 62             modelAndView.setViewName("userinfo");
 63             /*
 64              * 這爲何不直接傳一個username,而傳了一個admin,
 65              * 是由於在實際開發中,你傳過去的信息可能不僅是username,還有用戶手機號、地址等等
 66              */
 67             AdminCookieUtil.addLoginCookie(admin, response);
 68         }
 69         
 70         return modelAndView;
 71     }
 72     
 73     /*****************************mybatis xml方式解決的問題*******************************/
 74     /**
 75      * 根據username或password查找List<Admin>
 76      */
 77     @ResponseBody
 78     @RequestMapping("/findAdmin")
 79     public List<Admin> findAdmin(@RequestParam(value="username",required=false) String username,
 80                                     @RequestParam(value="password",required=false) String password,
 81                                     @RequestParam("start") int start,
 82                                     @RequestParam("limit") int limit,
 83                                     HttpServletRequest request){
 84         Admin admin = AdminCookieUtil.getLoginCookie(request);
 85         if(admin == null){//未登陸
 86             return null;
 87         }
 88         List<Admin> adminList = adminService.findAdmin(username, password, start, limit);
 89         return adminList;
 90     }
 91     
 92     /**
 93      * 插入一個用戶並返回主鍵
 94      * 注意:get請求也會自動裝配(即將前臺傳入的username和password傳入admin)
 95      */
 96     @ResponseBody
 97     @RequestMapping("/insert")
 98     public Admin insertAdminWithBackId(Admin admin){
 99         return adminService.insertAdminWithBackId(admin);
100     }
101 }
View Code

說明,這個類只修改了兩個方法login()和findAdmin()。

測試:

  • 向瀏覽器寫入cookie

  • 從瀏覽器讀cookie

注意:

  • 上述咱們發了兩個cookie,其中username的value沒有加密,是由於這個value不是私密數據且咱們在前臺會直接使用,這樣的話,省去了js解密的過程。
  • 對於allinfo的加密是爲了在瀏覽器端不讓用戶將cookie看的那麼明顯,保護一些私密數據;或者在整個傳輸過程當中,即便被竊聽者獲取了,也難以知道其中的cookie值。可是,若是竊聽者獲取了allinfo這個cookie後,就可使用這個cookie模擬登陸而後作一些操做,這個不知道cookie機制會怎樣解決?(答案見最下方)
  • 對於allinfo的加密而言,僅使用AES加密事實上也不太合適,由於若是竊聽者竊聽到你的cookie後,對其進行篡改,當請求頭再將這些信息傳遞過來的時候,可能通過json轉化後就會是另外一個Admin的信息了,若是須要防止這種狀況發生,須要同時對cookie值進行消息摘要加密了(具體的消息摘要加密能夠參照"Java加密與解密"系列博客),固然,在絕大多數狀況下,若是竊聽者不知道你的cookie值得話,若是他對這個值進行了隨意的篡改,那麼未來在將這個值進行解密後,對其轉化成json的過程當中就會拋出異常,由於解密後的json串極可能就不符合json的格式了,因此絕大多數狀況下,僅適用於AES是可行的。
  • 對於cookie的name實際上也應該加密,加密後瀏覽器端或竊聽者截到的cookie他就不知道是什麼意思了,這個加密很是簡單,直接在線下使用SHA256出一個字符串或者就使用上邊所講的AES的生成key的方法生成一個就好,而後寫在name中,代碼再列一遍:
    String key = Base64.encodeBase64String(AESUtil.getKey());

 

  • cookie不能夠在多種瀏覽器之間共享,由於每一個瀏覽器存cookie路徑不是同樣的
  • 同一種瀏覽器,多個標籤頁共享的話,須要再生成cookie的時候添加cookie的有效期;不然cookie爲會話cookie,這種客戶端是不會把cookie存到硬盤上的,其餘標籤也沒法獲取到cookie

 

總結:

  • 若是安全措施搞得好+cookie數量很少+cookie的總大小對帶寬的佔用能夠接受,使用cookie(對於安全措施這一塊,cookie被劫持的可能性通常不大,若是不是很是敏感的權限,可使用記住我這些功能,若是對於敏感權限,例如金錢操做,就強制從新登陸,相似於shiro
  • 對於瀏覽器禁用cookie的事情,基本能夠不考慮,絕大多數現代瀏覽器都不會禁用cookie。

疑問:

  • 若是被竊聽者劫取到你的cookie,即便你對這些cookie作了加密,使其看不到cookie中的信息,可是其也能夠經過該cookie模擬登陸行爲,而後得到一些權限,執行一些操做。(這是個疑問,記住我這個功能就是這樣作的,怎樣作到安全的呢?)

解答:方案

一、使用權限框架shiro

注意:shiroshiro保存了你的帳戶名(編碼)和可記住密碼的有效時間到cookie裏,下次會把帳戶名帶過來,能夠容許執行一些非敏感操做。根據你後臺設定的權限;固然對於敏感權限的話,必定要從新登陸才行。(固然,因爲仍是存在cookie裏,可能仍是存在會被劫持的狀況

具體看下邊圖片:("記住我"的功能)

二、https(即便被劫取到,也不能重傳)

這個是能夠解決上邊問題的,可是https沒鬧懂。

三、IP變更

1)客戶端登錄的時候,記錄客戶端IP到數據庫

2)下次登陸從request中獲取IP並與前一次記錄的IP做對比,若是IP沒有發生變化,則得直接進入已登陸狀態了;若是IP發生了變化,則從新登陸,登陸以後,修改IP

3)以後的過程如上

固然,對於以上這種方式,還有一種改進,就是將用戶登陸的IP放入緩存(固然能夠指定緩存時間),這樣每次就能夠從緩存中查IP了。

可是這種方式有個問題,就是說同一臺電腦換一個網絡環境,這時候IP就變了,就得從新登錄了。

四、數字簽名算法

數字簽名的三個做用中的一個就是"認證數據來源",感受這會是一個思路,可是具體怎麼實現還沒想好。若是你們有想好的,幫指點一下!

數字簽名算法的具體實現見"Java加密與解密"《第十四章 數字簽名算法--RSA》,連接以下:

http://www.cnblogs.com/java-zhao/p/5091363.html

 

有關於session的細節與解決方案,之後再說!

相關文章
相關標籤/搜索