會話管理-cookie,session,禁用cookie的URL重寫,token的單態設計模式,異常丟擲,UUID,MD5,base64編碼
阿新 • • 發佈:2019-01-29
1、會話
會話可簡單理解為:使用者開一個瀏覽器,點選多個超連結,訪問伺服器多個web資源,然後關閉瀏覽器,整個過程稱之為一個會話。- 會話過程中要解決的一些問題?
例如:使用者點選超連結通過一個servlet購買了一個商品,程式應該想辦法儲存使用者購買的商品,以便於使用者點結帳servlet時,結帳servlet可以得到使用者購買的商品為使用者結帳。
不能使用request或servletcontext來進行儲存
儲存會話資料的兩種技術:
- Cookie
- Session
2、cookie API
javax.servlet.http.Cookie類用於建立一個Cookie,response介面也中定義了一個addCookie方法,它用於在其響應頭中增加一個相應的Set-Cookie頭欄位。 同樣,request介面中也定義了一個getCookies方法,它用於獲取客戶端提交的Cookie。Cookie類的方法:- public Cookie(String name,String value)
- setValue與getValue方法
- setMaxAge與getMaxAge方法 :沒有設定的話,關閉瀏覽器cookie就失效了,存於快取。設定了寫入硬碟
- setPath與getPath方法 /day06:訪問day06的時候帶cookie
- setDomain與getDomain方法:域方法:https://www.taobao.com/這是主機名 .taobao.com是域名!
- getName方法 :cookie名稱
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.write("這是網站首頁!<br>"); out.write("您上次訪問時間是:"); //得到上次訪問時間 Cookie[] cookies = request.getCookies(); if (cookies == null) { out.write("初次訪問"); } else { for (int i = 0; i < cookies.length; i++) { if(cookies[i].getName().equals("LastAccessTime")){ Long time = Long.parseLong(cookies[i].getValue()); Date date = new Date(time); out.write(date.toLocaleString()); } } } //將訪問時間寫入cookie中 Cookie cookie = new Cookie("LastAccessTime",System.currentTimeMillis()+""); cookie.setMaxAge(3600); response.addCookie(cookie); }
- 一個Cookie只能標識一種資訊,它至少含有一個標識該資訊的名稱(NAME)和設定值(VALUE)。
- 一個WEB站點可以給一個WEB瀏覽器傳送多個Cookie,一個WEB瀏覽器也可以儲存多個WEB站點提供的Cookie。
- 瀏覽器一般只允許存放300個Cookie,每個站點最多存放20個Cookie,每個Cookie的大小限制為4KB。
- 如果建立了一個cookie,並將他傳送到瀏覽器,預設情況下它是一個會話級別的cookie(即儲存在瀏覽器的記憶體中),使用者退出瀏覽器之後即被刪除。若希望瀏覽器將該cookie儲存在磁碟上,則需要使用maxAge,並給出一個以秒為單位的時間。將最大時效設為0則是命令瀏覽器刪除該cookie。
- 注意,刪除cookie時,path必須一致,否則不會刪除
Cookie cookie = new Cookie("LastAccessTime",System.currentTimeMillis()+"");
cookie.setMaxAge(0);
response.addCookie(cookie);
必須設定 地址一致容器:兩種單列和雙列:collection和map。 有檢索資料的需求就用雙列,沒有就用單列。
例項:最近瀏覽過的商品
cookie3:
//首頁
public class CookieDemo3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
//1.顯示網站所有商品
out.write("本網站有如下書籍:<br><br>");
Set<Map.Entry<String, Book>> set = DB.getAll().entrySet();
for(Map.Entry<String, Book> me : set){
Book book = me.getValue();
out.write("<a href='/day07/servlet/CookieDemo4?id="+book.getId()+"' target='_blank'>"+book.getName()+"</a>");
out.write("<br/>");
}
//2.顯示使用者曾經瀏覽過的商品
out.write("<br/><br/>您曾經瀏覽過的商品:<br/>" );
Cookie cookies[] = request.getCookies();
for(int i=0;cookies!=null && i<cookies.length;i++){
Cookie cookie = cookies[i];
if(cookie.getName().equals("bookHistory")){
String bookHistory = cookie.getValue(); // 2_3
String ids[] = bookHistory.split("\\_"); //[2,3]
for(String id: ids){
Book book = (Book) DB.getAll().get(id);
out.write(book.getName() + "<br/>");
}
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
class DB
{
private static Map<String,Book> map = new LinkedHashMap();
static{
map.put("1", new Book("1","javaweb開發","老張","一本好書"));
map.put("2", new Book("2","spring開發","老黎","一本好書"));
map.put("3", new Book("3","hibernate開發","老佟","一本好書"));
map.put("4", new Book("4","struts開發","老畢","一本好書"));
map.put("5", new Book("5","ajax開發","老張","一本好書"));
}
public static Map getAll(){
return map;
}
}
class Book{
private String id;
private String name;
private String author;
private String description;
public Book() {
super();
// TODO Auto-generated constructor stub
}
public Book(String id, String name, String author, String description) {
super();
this.id = id;
this.name = name;
this.author = author;
this.description = description;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
cookie4:
ublic class CookieDemo4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
//1.根據使用者帶過來的id號,顯示商品的詳細資訊
String id = request.getParameter("id");
Book book = (Book) DB.getAll().get(id);
out.write("您要檢視的書的詳細資訊如下:<br/><br/>");
out.write(book.getId() + "<br/>");
out.write(book.getName() + "<br/>");
out.write(book.getAuthor() + "<br/>");
out.write(book.getDescription() + "<br/>");
//2.給使用者回送包含當前商品id的cookie
String bookHistory = makeHistory(request,id); // 3_2
Cookie cookie = new Cookie("bookHistory",bookHistory);
cookie.setMaxAge(1*30*24*3600);
response.addCookie(cookie);
}
private String makeHistory(HttpServletRequest request, String id) {
String bookHistory = null;
Cookie cookies[] = request.getCookies();
for(int i=0;cookies!=null&&i<cookies.length;i++){
if(cookies[i].getName().equals("bookHistory")){
bookHistory = cookies[i].getValue();
}
}
// bookHistory=null 1 bookHistory=1
// bookHistory=3_1_5 1 bookHistory=1_3_5
// bookHistory=3_2_5 1 bookHistory=1_3_2
// bookHistory=3_2 1 bookHistory=1_3_2
// bookHistory=null 1 bookHistory=1
if(bookHistory==null){
return id;
}
List l = Arrays.asList(bookHistory.split("\\_")); //[3,4] //陣列 連結
LinkedList<String> list = new LinkedList();
list.addAll(l);
if(list.contains(id)){
// bookHistory=3_1_5 1 bookHistory=1_3_5
list.remove(id);
list.addFirst(id);
}else{
if(list.size()>=3){
// bookHistory=3_2_5 1 bookHistory=1_3_2
list.removeLast();
list.addFirst(id);
}else{
// bookHistory=3_2 1 bookHistory=1_3_2
list.addFirst(id);
}
}
StringBuffer sb = new StringBuffer(); //2_3_4
for(String lid: list){
sb.append(lid + "_");
}
return sb.deleteCharAt(sb.length()-1).toString();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
3、session
在WEB開發中,伺服器可以為每個使用者瀏覽器建立一個會話物件(session物件),注意:一個瀏覽器獨佔一個session物件(預設情況下)。因此,在需要儲存使用者資料時,伺服器程式可以把使用者資料寫到使用者瀏覽器獨佔的session中,當用戶使用瀏覽器訪問其它程式時,其它程式可以從使用者的session中取出該使用者的資料,為使用者服務。Session和Cookie的主要區別在於:
- Cookie是把使用者的資料寫給使用者的瀏覽器。存於使用者,客戶端技術
- Session技術把使用者的資料寫到使用者獨佔的session中。存於伺服器,伺服器技術
購物車例項:
書類如上: index:response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
request.getSession();
out.write("本網站有如下書:<br/>");
Set<Map.Entry<String,Book>> set = DB.getAll().entrySet();
for(Map.Entry<String,Book> me : set){
Book book = me.getValue();
String url = "/day07/servlet/BuyServlet?id=" + book.getId();
url = response.encodeURL(url);
out.println(book.getName() + " <a href='"+url+"'>購買</a><br/>");
}
buy:
String id = request.getParameter("id");
Book book = (Book) DB.getAll().get(id); //得到使用者想買的書
HttpSession session = request.getSession();
/*Cookie cookie = new Cookie("JSESSIONID",session.getId());
cookie.setMaxAge(30*60);
cookie.setPath("/day07");
response.addCookie(cookie);*/
List list = (List) session.getAttribute("list"); //得到使用者用於儲存所有書的容器
if(list==null){
list = new ArrayList();
session.setAttribute("list", list);
}
list.add(book);
//request.getRequestDispatcher("/servlet/ListCartServlet").forward(request, response);
String url = response.encodeRedirectURL("/day07/servlet/ListCartServlet");
System.out.println(url);
response.sendRedirect(url);
ListCartServlet:
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
List<Book> list = (List) session.getAttribute("list");
if(list==null || list.size()==0){
out.write("對不起,您還沒有購買任何商品!!");
return;
}
//顯示使用者買過的商品
out.write("您買過如下商品:<br>");
for(Book book : list){
out.write(book.getName() + "<br/>");
}
伺服器是如何實現一個session為一個使用者瀏覽器服務的?
瀏覽器第一次getsession的時候,伺服器建立一個session物件,伺服器會給每個session一個固定的ID號碼,通過cookie的形式儲存到使用者硬碟上,由於沒有設定有效時間,因此,當關閉瀏覽器這個cookie就失效了,那麼這個session就結束了。
因此要實現關閉了瀏覽器,session物件還在,那麼就需要對儲存session的ID號碼的cookie物件設定有效時間(由於畢竟是session物件,伺服器預設該session如果30分鐘內沒人使用自動銷燬,因此自己設定的cookie有效期不應該30分鐘)
String id = session.getId();
Cookie cookie = new Cookie("JSESSIONID",id);
cookie.setMaxAge(1800);//不應該超過30分鐘
cookie.setPath("/");
response.addCookie(cookie);
cookie.setPath("/");
應用於所有工程下的servlet!
禁用Cookie後servlet共享資料導致的問題:URL重寫
利用URL攜帶session的ID,網站上的所有URL地址都要重寫
response. encodeRedirectURL(java.lang.String url)index:
用於對sendRedirect方法後的url地址進行重寫。
response. encodeURL(java.lang.String url)
用於對錶單action和超連結的url地址進行重寫
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
request.getSession();
out.write("本網站有如下圖書:<br>");
Set<Map.Entry<String,Book>> set = DB.getAll().entrySet();
for (Map.Entry<String,Book> me:set){
Book book = me.getValue();
String url = response.encodeRedirectURL("/Buy?id=" + book.getID());
out.write(book.getName()+" <a href='"+ url +"'>購買</a><br>");
}
buy:
String url = response.encodeRedirectURL("/ListCart");
response.sendRedirect(url);
禁用cookie的話,就不能實現關閉瀏覽器資料還在1、伺服器是如何做到一個session為一個瀏覽器的多次請求而服務
1.1 伺服器建立session出來後,會把 session的id號,以cookie的形式回寫給客戶機,這樣,只要客戶機的瀏覽器不關,再
去訪問伺服器時,都會帶著session 的id號去,伺服器發現客戶機帶session id過來了,就會使用記憶體中與之對應的
session為之服務
2、如何做到一個session為多個瀏覽器服務
2.1 伺服器第一次建立session,程式設計師把session id號,手工以cookie的形式回送給瀏覽器,並設定cookie的有效期 這樣,即使使用者的瀏覽器關了,開新瀏覽器時,還會帶著session id找伺服器,伺服器從而就可以用記憶體中與之對應的 session為第二個瀏覽器視窗服務
3、如何做使用者禁用cookie後,session還能為多次請求而服務
3.1 把使用者可能點的每一個超連結後面,都跟上使用者的session id號
4、session物件的建立和銷燬時機
4.1 使用者第一次request.getSession時 4.2 session物件預設30分鐘沒有使用,則伺服器會自動銷燬session,
購物網站可以用cookie也可以用session,cookie可以減輕伺服器壓力。4.2.1 使用者在web.xml檔案中手工配置session的失效時間:session-config timeout4.2.2 使用者可以手工呼叫session.invalidate方法,摧毀session
4、session案例
(1)、使用Session完成使用者登入
index.jsp<body>
歡迎您:<br>
<%
User user = (User) session.getAttribute("user");
if (user != null)
out.write(user.getUsername());
%>
<a href="login.jsp">登入</a>
</body>
Login(servlet)
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = DB.find(username, password);
if(user == null){
out.write("使用者名稱或密碼錯誤");
return;
}
request.getSession().setAttribute("user",user);
response.sendRedirect("/index.jsp");
login.jsp
<body>
<form action="/Login" method="post">
使用者名稱:<input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
<input type="submit" value="登入">
</form>
</body>
(2)、防止表單重複提交
情況:a、延時導致使用者多次提交 b、提交後多次重新整理 c、後退提交後退提交 需要前臺與後臺的共同努力: 前臺:頁面利用JavaScript程式碼進行:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<script type="text/javascript">
function dosubmit(){
var submit = document.getElementById("submit");
submit.disabled="disabled";
return true;
}
</script>
<body>
<form action="FormTest" method="post" onsubmit="return dosubmit()">
<input type="text" name="username"><br>
<input type="submit" value="提交" id="submit">
</form>
</body>
</html>
釋出表單的同時釋出一個隨機數,如果隨機數一致了,錄入資訊,並將session中的隨機數清除,再提交的時候由於隨機數銷燬了,就不會一致了防止了表單的重複提交。 隨機數的產生應該使用單態設計模式:
單態設計模式:一個類只生成一個實體物件
*1、把類的建構函式私有*2、自己建立一個類的物件
*3、對外提供一個公共的方法,返回類的物件
class TokenProcess{
//單態模式,原子模式,單例模式
/*
*單態設計模式(保證類的物件在記憶體中只有一個)
*1、把類的建構函式私有
*2、自己建立一個類的物件
*3、對外提供一個公共的方法,返回類的物件
*
*/
private TokenProcess(){}
private static final TokenProcess instance = new TokenProcess();
public static TokenProcess getInstance(){
return instance;
}
//形成隨機數
public String makeToken(){
String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
//資料指紋 128位長 16個位元組 md5
try {
MessageDigest md5 = MessageDigest.getInstance("md5");
byte[] digest = md5.digest(token.getBytes());
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(digest);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
ServletContext:其他人來了共用了 request:請求都變了 session:√ 不用UUID:通用唯一識別碼 (Universally Unique Identifier) //base64編碼--任意二進位制編碼明文字元 adfsdfsdfsf
3個位元組變成4個位元組,3個位元組24位,4個位元組各取6位,自後向前,取完加兩個0,形成一個位元組 BASE64Encoder 一般都是向上拋執行異常:throw new RuntimeException(e);但是如果是想用異常當做一種返回值處理,需要人家處理,則應該拋編譯異常。 //資料指紋 128位長 16個位元組 md5演算法 不可逆演算法 MessageDigest
try {
MessageDigest md5 = MessageDigest.getInstance("md5");
byte[] digest = md5.digest(token.getBytes());
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(digest);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}