基於RFC6265 (HTTP狀態管理協議)實現簡單的登陸系統

該協議主要是闡述如何利用HTTP Cookie與SetCookie頭字段來實現對HTTP Requestjava

態的跟蹤與管理,這個在用戶行爲分析,登陸系統設計等方法有着很重要的應用。對web

大多數現代的瀏覽器都支持RFC6265.spring

基本原理:瀏覽器

RFC6265闡述經過設置SetCookie不一樣值在HTTPResponse中,來告訴瀏覽器客戶端在服務器

接下來的每次請求Request Header中都帶上Response中指定的值與行爲。直到服務器cookie

配置的session過時爲止。經過Tomcat配置session過時時間爲30分鐘,在web.xml配置session

文件中可以重寫該屬性值。同時當用戶關閉瀏覽器之後,在客戶端內存中對於該站點app

的cookie內容將會自動銷燬。也許這樣不太方便用戶,因而不少網站提供了記住本身賬ide

號的緣由,其實就是經過將cookie寫到本地文件中。網站

系統訪問與重定向到登陸頁面

首先當瀏覽器客戶端發起一個Request請求訪問指定URL或者Web Server時,服務器

端通過檢查請求頭是否含有Cookie字段,以及Cookie字段中的內容來設別訪問者是否

爲登陸或者未登陸用戶,若是是未登陸頁面,則將URL重定向到登陸頁面便可。用戶

登陸之後服務器端發送一個HTTP Response + Set-Cookie內容到客戶端瀏覽器,那麼

在隨後全部發往該Domain的URL都將會帶上Set-Cookie中指定的內容,HTTP

Request + Cookie到服務器端,服務器端經過檢查Request 頭中Cookie內容實現對

用戶的狀態追蹤。從而將無狀態HTTP Request變成一個有狀態的HTTP鏈接加以

管理。登陸處理基本的流程圖以下:

SouthEast


服務器與客戶端HTTP Request發送與接受狀態:

20140324010834703


用戶退出系統與Request狀態終止

當客戶端關閉瀏覽器時候,客戶端Cookie將會被自動從內存中丟棄,當客戶端再次打

開瀏覽器請求服務器端資源時候,將被要求再次登陸到服務器端創建新的可跟蹤的

Request 會話當超過服務器端配置的會話時間時,一樣會要求用戶再次登陸系統。

當用戶使用系統退出功能正常退出時,當退出時候經過設置Max-Age : 0來remove

當前cookie內容實現對客戶端狀態的清零。只要在HTTP Response中加上Cookie過

期屬性同時設置一個過去的時間。例子以下:

20140324010934328

RFC6265中關於Cookie與SetCookie的屬性與使用詳解

Cookie

SetCookie

包含於HTTP Request Header,用戶客戶端向服務器端發送驗證信息與其它有用信息,主要用來跟蹤客戶端狀態與分析用戶行爲

在HTTP Response中設置,主要用於服務器端向客戶端發送指定的狀態信息,創建與客戶端的聯繫。經過設置HTTPOnly屬性與Secure屬性還能夠保護客戶端Cookie數據,減小惡意讀取用戶Cookie信息發生。

RFC6265中一個簡單例子:

== Server -> UserAgent == // 服務器發送到客戶端

Set-Cookie:SID=31d4d96e407aad42

== User Agent ->Server == // 每一個請求中都會帶上SID信息,實現追蹤用戶狀態

Cookie: SID=31d4d96e407aad42

要求客戶端的全部請求路徑都要帶上SID信息,經過發送Path=/實現

== Server -> UserAgent ==

Set-Cookie:SID=31d4d96e407aad42; Path=/; Domain=example.com

== User Agent ->Server ==

Cookie: SID=31d4d96e407aad42

刪除客戶端Request Cookie中SID信息,取當前時間之前的一個任意時間。

== Server -> UserAgent ==

Set-Cookie: SID=;Expires=Sun, 06 Nov 1994 08:49:37 GMT

最後來看一看我抓取的CSDN登陸之後的Cookie信息:

20140324010916015

J2EE 當從HTTP Servlet Request中調用獲取SessionID方法之後會自動把JSESSIONID

做爲Cookie設置到Response頭中。因此無需顯式再次調用!


根據RFC6265的內容,基於Spring3 MVC我本身也實現了一個簡單的登陸系統設計

能夠幫助你們更好的理解協議。只有兩個頁面,兩個Controller類與一個ServletFilter

各個類的做用大體以下:

ServletFilter類:實現對HTTP Request頭的檢查,跟蹤用戶狀態

兩個Controller類:一個用來管理用戶登入登出,一個是簡單的獲取主頁面信息

其中ServletFilter類代碼以下:

package com.edinme.exam.filter; 
            
import java.io.IOException; 
import java.util.Date; 
            
import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
            
import com.editme.exam.util.FilterUtil; 
            
public class SingleSignOnFilter implements Filter{  
    @Override 
    public void destroy() { 
        // TODO Auto-generated method stub 
    } 
            
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, 
            FilterChain chain) throws IOException, ServletException { 
        HttpServletRequest httpRequest = (HttpServletRequest) request; 
        HttpServletResponse httpResponse = (HttpServletResponse) response;   
        String ipAddress = httpRequest.getRemoteAddr(); 
                    
        // get URI resource path 
        String uriPath = httpRequest.getRequestURI(); 
        String contextPath = httpRequest.getContextPath(); 
        String cookie = httpRequest.getHeader("Cookie"); 
        String sessionId = httpRequest.getSession().getId(); // enable SetCookie header in HTTP Response 
        if(FilterUtil.validURLRequest(uriPath, cookie, contextPath, sessionId)) 
        { 
            System.out.println("Request URI : " + uriPath); 
            System.out.println("IP "+ipAddress + ", Time " + new Date().toString()); 
            chain.doFilter(request, response); 
        } 
        else 
        {        
            System.out.println(httpRequest.getProtocol() + httpRequest.getLocalPort() + httpRequest.getContextPath()); 
            httpResponse.sendRedirect("/exam/user.do"); 
        } 
    } 
            
    @Override 
    public void init(FilterConfig config) throws ServletException { 
        // TODO Auto-generated method stub 
                    
    } 
            
}


用戶登入登出Controller:  

package com.edinme.exam.controller; 
      
import javax.servlet.http.Cookie; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
      
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.RequestParam; 
import org.springframework.web.bind.annotation.ResponseBody; 
import org.springframework.web.servlet.ModelAndView; 
      
import com.edinme.exam.dto.MockUpDataFactory; 
import com.edinme.exam.dto.UserDto; 
import com.editme.exam.util.FilterUtil; 
      
@Controller 
@RequestMapping(value = "/user") 
public class UserLoginController { 
          
    @RequestMapping(method = RequestMethod.GET) 
    public ModelAndView goLoginPage() 
    { 
        System.out.println("Dispaly SSO Page"); 
        ModelAndView view = new ModelAndView("user"); 
        return view; 
    } 
          
    @RequestMapping(value = "signIn", method = RequestMethod.GET) 
    @ResponseBody 
    public UserDto login(@RequestParam String userId, @RequestParam String password, /*HttpServletRequest httpRequest,*/ HttpServletResponse response) 
    { 
        System.out.println("User Name = " + userId); 
        MockUpDataFactory dataFactory = new MockUpDataFactory(); 
        response.addHeader("Set-Cookie", "userId=" + userId + "; Path=" + FilterUtil.CONTEXT_PATH + "; HttpOnly"); 
        return dataFactory.getUserById(userId); 
    } 
          
    @RequestMapping(value = "signOut", method = RequestMethod.GET) 
    @ResponseBody 
    public UserDto logout(@RequestParam String userId, HttpServletRequest httpRequest, HttpServletResponse response) 
    { 
        MockUpDataFactory dataFactory = new MockUpDataFactory();     
        //  Set-Cookie:JSESSIONID=delete; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/exam/ 
        //  Set-Cookie:userId=delete; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/exam/ 
        Cookie[] cs = httpRequest.getCookies(); 
        for(Cookie c : cs) 
        { 
            c.setMaxAge(0); // set expire attribute 
            c.setValue("delete"); 
            c.setPath(FilterUtil.CONTEXT_PATH); // set path, must be same as login context path 
            response.addCookie(c);           
        } 
        response.setHeader("Expires", "Thu, 19 Nov 1981 08:52:00 GMT"); // must be GTM format 
        return dataFactory.getUserById(userId); 
    } 
//   
//  public static void main(String[] args) 
//  { 
//      SimpleDateFormat sdf = new SimpleDateFormat("E dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH); 
//      sdf.setTimeZone(TimeZone.getTimeZone("GMT")); 
//      System.out.println(sdf.format(new Date())); 
//  } 
      
}

所有源代碼下載點擊這裏:


以爲好請頂一下啊!!,謝謝!!

相關文章
相關標籤/搜索