會話管理-cookie,session,禁用cookie的URL重寫,token的單態設計模式,異常拋出,UUID,MD5,base64編碼

1、會話

會話可簡單理解爲:用戶開一個瀏覽器,點擊多個超鏈接,訪問服務器多個web資源,然後關閉瀏覽器,整個過程稱之爲一個會話。
  • 會話過程中要解決的一些問題?
每個用戶在使用瀏覽器與服務器進行會話的過程中,不可避免各自會產生一些數據,程序要想辦法爲每個用戶保存這些數據。
例如:用戶點擊超鏈接通過一個servlet購買了一個商品,程序應該想辦法保存用戶購買的商品,以便於用戶點結帳servlet時,結帳servlet可以得到用戶購買的商品爲用戶結帳。
不能使用request或servletcontext來進行存儲

保存會話數據的兩種技術:

  • Cookie
Cookie是客戶端技術,程序把每個用戶的數據以cookie的形式寫給用戶各自的瀏覽器。當用戶使用瀏覽器再去訪問服務器中的web資源時,就會帶着各自的數據去。這樣,web資源處理的就是用戶各自的數據了。


  • Session
Session是服務器端技術,利用這個技術,服務器在運行時可以爲每一個用戶的瀏覽器創建一個其獨享的session對象,由於session爲用戶瀏覽器獨享,所以用戶在訪問服務器的web資源時,可以把各自的數據放在各自的session中,當用戶再去訪問服務器中的其它web資源時,其它web資源再從用戶各自的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 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中。存於服務器,服務器技術
Session對象由服務器創建,開發人員可以調用request對象的getSession方法得到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) 
用於對sendRedirect方法後的url地址進行重寫。
response. encodeURL(java.lang.String url)
用於對錶單action和超鏈接的url地址進行重寫

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 = 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,
4.2.1  用戶在web.xml文件中手工配置session的失效時間:session-config     timeout
4.2.2  用戶可以手工調用session.invalidate方法,摧毀session

購物網站可以用cookie也可以用session,cookie可以減輕服務器壓力。


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" οnsubmit="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);
}

(3)、利用Session實現一次性驗證碼


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章