Session與Cookie(自定義Session)

1、會話管理javascript

  會話管理: 管理瀏覽器客戶端 和 服務器端之間會話過程當中產生的會話數據。html

  域對象: 實現資源之間的數據共享。前端

  request域對象java

  context域對象web

  會話技術:  數據庫

    Cookie技術:會話數據保存在瀏覽器客戶端。apache

    Session技術:會話數據保存在服務器端。存放在內存裏面,客戶端與服務器端以前的通信使用SessionId後端

2、Cookie技術瀏覽器

2.1 特色:Cookie技術:會話數據保存在瀏覽器客戶端。緩存

2.2 Cookie技術核心

  Cookie類:用於存儲會話數據

    1)構造Cookie對象

      New  Cookie(java.lang.String name, java.lang.String value)

    2)設置cookie

      void setPath(java.lang.String uri)   :設置cookie的有效訪問路徑

      void setMaxAge(int expiry) : 設置cookie的有效時間

      void setValue(java.lang.String newValue) :設置cookie的值

    3)發送cookie到瀏覽器端保存

      void response.addCookie(Cookie cookie)  : 發送cookie

    4)服務器接收cookie

      Cookie[] request.getCookies()  : 接收cookie

2.3 Cookie原理

  1)服務器建立cookie對象,把會話數據存儲到cookie對象中。

    new Cookie("name","value");

  2)  服務器發送cookie信息到瀏覽器

    response.addCookie(cookie);

     舉例: set-cookie: name=eric  (隱藏發送了一個set-cookie名稱的響應頭)

  3)瀏覽器獲得服務器發送的cookie,而後保存在瀏覽器端。

  4)瀏覽器在下次訪問服務器時,會帶着cookie信息

    舉例: cookie: name=eric  (隱藏帶着一個叫cookie名稱的請求頭)

  5)服務器接收到瀏覽器帶來的cookie信息

    request.getCookies();

2.4 Coolie的細節  

  1)void setPath(java.lang.String uri)   :設置cookie的有效訪問路徑。有效路徑指的是cookie的有效路徑保存在哪裏,那麼瀏覽器在有效路徑下訪問服務器時就會帶着cookie信息,不然不帶cookie信息。

  2)void setMaxAge(int expiry) : 設置cookie的有效時間。

    正整數:表示cookie數據保存瀏覽器的緩存目錄(硬盤中),數值表示保存的時間。

    負整數:表示cookie數據保存瀏覽器的內存中。瀏覽器關閉cookie就丟失了!!

    零:表示刪除同名的cookie數據

  3)Cookie數據類型只能保存非中文字符串類型的。能夠保存多個cookie,可是瀏覽器通常只容許存放300個Cookie,每一個站點最多存放20個Cookie,每一個Cookie的大小限制爲4KB。

2.5 Cookie 問題

  1) cookie不能跨瀏覽器

  2)瀏覽器關閉,默認失效cookie

  3)127.0.0.1和localhost,操做cookie時不共享

2.6 Cookie的侷限:

  1)Cookie只能存字符串類型,不能保存對象

  2)只能存非中文

  3)1個cookie的容量不超過4KB

2.7 案例-顯示用戶上次訪問時間

@WebServlet("/LastAccessTime") public class LastAccessTime extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8");// 防止瀏覽器顯示亂碼
        String lastAccessTime = null; Cookie[] cookies = req.getCookies(); for (Cookie cookie : cookies) { String name = cookie.getName(); if (name.equals("lastAccessTime")) { lastAccessTime = cookie.getValue(); break; } } if (StringUtils.isEmpty(lastAccessTime)) { resp.getWriter().print("您是首次訪問!"); } else { resp.getWriter().print("你上次訪問時間:" + lastAccessTime); } // 保存訪問時間 // 建立cookie 將當前時間做爲cookie保存到瀏覽器
        String currenttime = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss").format(new Date()); Cookie cookie = new Cookie("lastAccessTime", currenttime); cookie.setMaxAge(60 * 60 * 24); // 發送cookie
 resp.addCookie(cookie); } String addCookie(String key, String value, HttpServletResponse resp) { return key; } }

3、Session技術

3.1 因爲Cookie的侷限性和問題,想要突破這些限制只能使用session技術!

  Session特色:會話數據保存在服務器端。(內存中)

3.2 Session 技術核心

  HTTPSession類:用於保存會話數據

  1)建立或獲得session對象

    HttpSession getSession() 

    HttpSession getSession(boolean create)   //true,沒有session,會建立。False則不會建立

  2)設置session對象

    void setMaxInactiveInterval(int interval)  : 設置session的有效時間

    void invalidate()     : 銷燬session對象

    java.lang.String getId()  : 獲得session編號

  3)保存會話數據到session對象

    void setAttribute(java.lang.String name, java.lang.Object value)  : 保存數據

    java.lang.Object getAttribute(java.lang.String name)  : 獲取數據

    void removeAttribute(java.lang.String name) : 清除數據

3.3 Session原理 

  服務器端建立session,經過響應頭將sessionId保存到客戶端本地。客戶端經過請求頭,將sessionId傳遞到服務器端。

  關閉瀏覽器只會清楚客戶端的sessionID,服務器端的session由建立時的時間失效決定。

代碼解讀:HttpSession session = request.getSession();

  1)第一次訪問建立session對象,給session對象分配一個惟一的ID,叫JSESSIONID

     new HttpSession();

  2)把JSESSIONID做爲Cookie的值發送給瀏覽器保存

    Cookie cookie = new Cookie("JSESSIONID", sessionID);

    response.addCookie(cookie);

  3)第二次訪問的時候,瀏覽器帶着JSESSIONID的cookie訪問服務器

  4)服務器獲得JSESSIONID,在服務器的內存中搜索是否存放對應編號的session對象。

    if(找到){

      return map.get(sessionID);

    }else{

      new HttpSession();

    }

  5)若是找到對應編號的session對象,直接返回該對象

  6)若是找不到對應編號的session對象,建立新的session對象,繼續走1的流程

  結論:經過JSESSION的cookie值在服務器找session對象!

3.4 Session細節

  1)java.lang.String getId()  : 獲得session編號

  2)兩個getSession方法:

    getSession(true) / getSession()  : 建立或獲得session對象。沒有匹配的session編號,自動建立新的session對象。

    getSession(false):              獲得session對象。沒有匹配的session編號,返回null

  3)void setMaxInactiveInterval(int interval)  : 設置session的有效時間

    session對象銷燬時間:

    3.1 默認狀況30分服務器自動回收

    3.2 修改session回收時間

    3.3 全局修改session有效時間

<!-- 修改session全局有效時間:分鐘 -->
    <session-config>
        <session-timeout>1</session-timeout>
    </session-config>

    3.4.手動銷燬session對象  

      void invalidate()     : 銷燬session對象

  4)如何避免瀏覽器的JSESSIONID的cookie隨着瀏覽器關閉而丟失的問題

  /**   * 手動發送一個硬盤保存的cookie給瀏覽器
  */   Cookie c = new Cookie("JSESSIONID",session.getId());   c.setMaxAge(60*60);   response.addCookie(c);

總結:

  1)會話管理: 瀏覽器和服務器會話過程當中的產生的會話數據的管理。

  2)Cookie技術:

    new Cookie("name","value")

    response.addCookie(coookie)

    request.getCookies()

  3)Session技術

    request.getSession();                                            

    setAttrbute("name","會話數據");

    getAttribute("會話數據")

4、自定義緩存

4.1定義緩存實體類

public class Cache { public Cache(String key, Object value, Long timeOut) { super(); this.key = key; this.value = value; this.timeOut = timeOut; } public Cache() { } /** * key */
    private String key; /** * 緩存數據 */
    private Object value; /** * 超時時間 */
    private Long timeOut; }

4.2 定義緩存類

/** * @classDesc: 功能描述:(緩存map) */
public class CacheManager { private Map<String, Cache> cacheMap = new HashMap<>(); /** * @methodDesc: 功能描述:(往緩存存值) */
    public void put(String key, Object oj) { put(key, oj, null); } /** * @methodDesc: 功能描述:(往緩存存值) */
    public synchronized void put(String key, Object oj, Long timeOut) { if (oj == null) { return; } Cache cache = new Cache(); cache.setKey(key); if (timeOut != null) cache.setTimeOut(timeOut + System.currentTimeMillis()); cache.setValue(oj); cacheMap.put(key, cache); } /** * @methodDesc: 功能描述:(刪除) */
    public synchronized void deleteCache(String key) { cacheMap.remove(key); } /** * @methodDesc: 功能描述:(獲取緩存中數據) */
    public synchronized Object get(String key) { Cache cache = cacheMap.get(key); Object oj = null; if (cache != null) { oj = cache.getValue(); } return oj; } /** * @methodDesc: 功能描述:(檢查數據是否在有效期內) */
    public synchronized void checkValidityData() { for (String key : cacheMap.keySet()) { Cache cache = cacheMap.get(key); Long timeOut = cache.getTimeOut(); if (timeOut == null) { return; } long currentTime = System.currentTimeMillis(); long endTime = timeOut; long result = (currentTime - endTime); if (result > 0) { System.out.println("清除:"+key); cacheMap.remove(key); } } } public static void main(String[] args) throws InterruptedException { CacheManager cacheManager = new CacheManager(); // cacheManager.put("lisi", 0);
        cacheManager.put("zhangsan", "jj", 5000l); // 多線程,驗證session的有效時間
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); scheduledThreadPool.schedule(new Runnable() { public void run() { cacheManager.checkValidityData(); } }, 5000, TimeUnit.MILLISECONDS); Thread.sleep(5000); System.out.println(cacheManager.get("zhangsan")); } }

5、自定義Token

5.1 什麼是token

  token其實就是一個令牌,具備隨機性,相似於sessionId。在對接一些第三方平臺的時候,爲了可以保證數據安全性,一般會使用一些令牌進行交互。

5.2 如何自定義token

  token生成規則,只要保證token生成一個不重複的惟一字符串便可。使用jdk自帶的uuid生成規則。

5.3 什麼是UUID

  UUID含義是通用惟一識別碼 (Universally Unique Identifier),這是一個軟件建構的標準,也是被開源軟件基金會 (Open Software Foundation, OSF) 的組織應用在分佈式計算環境 (Distributed Computing Environment, DCE) 領域的一部分。

  UUID 的目的,是讓分佈式系統中的全部元素,都能有惟一的辨識資訊,而不須要透過中央控制端來作辨識資訊的指定。如此一來,每一個人均可以創建不與其它人衝突的 UUID。

5.4 UUID的組成

  UUID保證對在同一時空中的全部機器都是惟一的。一般平臺會提供生成的API。按照開放軟件基金會(OSF)制定的標準計算,用到了以太網卡地址、納秒級時間、芯片ID碼和許多可能的數字
  UUID由如下幾部分的組合:
    (1)當前日期和時間,UUID的第一個部分與時間有關,若是你在生成一個UUID以後,過幾秒又生成一個UUID,則第一個部分不一樣,其他相同。
    (2)時鐘序列。
    (3)全局惟一的IEEE機器識別號,若是有網卡,從網卡MAC地址得到,沒有網卡以其餘方式得到。
  UUID的惟一缺陷在於生成的結果串會比較長。關於UUID這個標準使用最廣泛的是微軟的GUID(Globals Unique Identifiers)。在ColdFusion中能夠用CreateUUID()函數很簡單地生成UUID,其格式爲:xxxxxxxx-xxxx- xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每一個 x 是 0-9 或 a-f 範圍內的一個十六進制的數字。而標準的UUID格式爲:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)。

5.5 UUID代碼

  UUID。randomUUID().toString()

6、表單重複提交解決方案(防止http重複提交) 

6.1 場景描述:

  網絡延時:在平時開發中,若是網速比較慢的狀況下,用戶提交表單後,發現服務器半天都沒有響應,那麼用戶可能會覺得是本身沒有提交表單,就會再點擊提交按鈕重複提交表單,咱們在開發中必須防止表單重複提交。

  重複刷新:表單提交後用戶點擊【刷新】按鈕致使表單重複提交。

  後退再提交:點擊瀏覽器的【後退】按鈕回退到表單頁面後進行再次提交

6.2 解決方案

  6.2.1 使用javascript 解決

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表單</title>
<script type="text/javascript">
    var isFlag = false; //表單是否已經提交標識,默認爲false
    function submitFlag() { if (isFlag == false) { isFlag = true; return true; } else { return false; } } </script>
</head>
<body>
    <form action="${pageContext.request.contextPath}/DoFormServlet" method="post" onsubmit="return submitFlag()"> 用戶名:<input type="text" name="userName"> <input type="submit" value="提交" id="submit">
    </form>
</body>
</html>

  6.2.2 使用後端提交解決

  對於【重複刷新】、【後退再提交】致使表單重複提交的問題,前端沒法解決,只能在服務器端解決。

  在服務器端生成一個惟一的隨機標識號,專業術語稱爲Token(令牌),同時在當前用戶的Session域中保存這個Token。而後將Token發送到客戶端的Form表單中,在Form表單中使用隱藏域來存儲這個Token,表單提交的時候連同這個Token一塊兒提交到服務器端,而後在服務器端判斷客戶端提交上來的Token與服務器端生成的Token是否一致,若是不一致,那就是重複提交了,此時服務器端就能夠不處理重複提交的表單。若是相同則處理表單提交,處理完後清除當前用戶的Session域中存儲的標識號。

  在下列狀況下,服務器端將拒絕處理用戶提交的表單請求:

  1. 存儲Session域中的Token(令牌)與表單提交的Token(令牌)不一樣。
  2. 當前用戶的Session中不存在Token(令牌)
  3. 用戶提交的表單數據中沒有Token(令牌)

  轉發代碼:

@WebServlet("/ForwardServlet") public class ForwardServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getSession().setAttribute("sesionToken", TokenUtils.getToken()); req.getRequestDispatcher("form.jsp").forward(req, resp); } }

  轉發頁面:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表單</title>

</head>

<body>
    <form action="${pageContext.request.contextPath}/DoFormServlet" method="post" onsubmit="return dosubmit()">
        <input type="hidden" name="token" value="${sesionToken}"> 用戶名:<input type="text" name="userName"> <input type="submit" value="提交" id="submit">
    </form>
</body>
</html>

  後端代碼:

@WebServlet("/DoFormServlet") public class DoFormServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); boolean flag = isFlag(req); if (!flag) { resp.getWriter().write("已經提交..."); System.out.println("數據已經提交了.."); return; } String userName = req.getParameter("userName"); try { Thread.sleep(300); } catch (Exception e) { } System.out.println("往數據庫插入數據...." + userName); resp.getWriter().write("success"); } public boolean isFlag(HttpServletRequest request) { HttpSession session = request.getSession(); String sesionToken = (String) session.getAttribute("sesionToken"); String token = request.getParameter("token"); if (!(token.equals(sesionToken))) { return false; } session.removeAttribute("sesionToken"); return true; } }

7、Servlet之Filter

7.1 什麼是Filter

  Filter 也稱之爲過濾器,它是 Servlet 技術中最實用的技術,Web 開發人員經過 Filter 技術,對 web 服務器管理的全部 web 資源:例如 Jsp, Servlet, 靜態圖片文件或靜態 html 文件等進行攔截,從而實現一些特殊的功能。例如實現 URL 級別的權限訪問控制、過濾敏感詞彙、壓縮響應信息等一些高級功能。

  它主要用於對用戶請求進行預處理,也能夠對 HttpServletResponse 進行後處理。使用 Filter 的完整流程:Filter 對用戶請求進行預處理,接着將請求交給 Servlet 進行處理並生成響應,最後 Filter 再對服務器響應進行後處理。

7.2 Fileter使用

import java.io.IOException; import java.util.Map; 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.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 使用Filter 打印參數 * */
public class FilterDemo implements Filter { public FilterDemo(){ System.out.println("FilterDemo 構造函數被執行..."); } /** * 銷燬 */
    public void destroy() {   System.out.println("destroy"); } public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain) throws IOException, ServletException { System.out.println("doFilter"); HttpServletRequest request = (HttpServletRequest) paramServletRequest; HttpServletResponse response = (HttpServletResponse) paramServletResponse; // 請求地址
        String requestURI = request.getRequestURI(); System.out.println("requestURI:"+requestURI); // 參數
        Map<String, String[]> parameterMap = request.getParameterMap(); for (String key : parameterMap.keySet()) { String[] arr=parameterMap.get(key); } } /** * 初始化 */
    public void init(FilterConfig paramFilterConfig) throws ServletException { System.out.println("init"); } }

  web.xml配置

<filter>
      <filter-name>FilterDemo</filter-name>
      <filter-class>com.stu.servlet.FilterDemo</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>FilterDemo</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

7.3 使用Filter防止XSS攻擊

  7.3.1 什麼是XSS攻擊

    XSS攻擊使用Javascript腳本注入進行攻擊,例如在表單中注入: <script>location.href='http://www.baidu.com'</script>

  fromToXss.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="XssDemo" method="post">
        <input type="text" name="userName"> <input type="submit">
    </form>
</body>
</html>
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/XssDemo") public class XssDemo extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String userName = req.getParameter("userName"); req.setAttribute("userName", userName); req.getRequestDispatcher("showUserName.jsp").forward(req, resp); } }

  show.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>userName:${userName} </body>
</html>

  73..2 解決方案:使用Filter過濾器過濾注入標籤

import java.io.IOException; import java.util.Map; 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.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 
public class FilterDemo implements Filter { public FilterDemo() { System.out.println("FilterDemo 構造函數被執行..."); } /** * 銷燬 */
    public void destroy() { System.out.println("destroy"); } public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain) throws IOException, ServletException { System.out.println("doFilter"); HttpServletRequest request = (HttpServletRequest) paramServletRequest; XssAndSqlHttpServletRequestWrapper xssRequestWrapper = new XssAndSqlHttpServletRequestWrapper(request); // HttpServletResponse response = (HttpServletResponse) // paramServletResponse; // // 請求地址 // String requestURI = request.getRequestURI(); // System.out.println("requestURI:" + requestURI); // // 參數 // Map<String, String[]> parameterMap = request.getParameterMap(); // for (String key : parameterMap.keySet()) { // String[] arr = parameterMap.get(key); // System.out.print("key:"); // for (String string : arr) { // System.out.println(string); // } // }
 paramFilterChain.doFilter(xssRequestWrapper, paramServletResponse); } /** * 初始化 */
    public void init(FilterConfig paramFilterConfig) throws ServletException { System.out.println("init"); } }

  XssAndSqlHttpServletRequestWrapper

import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; /** * 防止XSS攻擊 */
public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper { HttpServletRequest request; public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) { super(request); this.request = request; } public String getParameter(String name) { String value = request.getParameter(name); System.out.println("name:" + name + "," + value); if (!StringUtils.isEmpty(value)) { // 轉換Html
            value = StringEscapeUtils.escapeHtml4(value); } return value; } }
相關文章
相關標籤/搜索