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域中存儲的標識號。
在下列狀況下,服務器端將拒絕處理用戶提交的表單請求:
轉發代碼:
@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; } }