android保持Session會話
在服務端完成實現app在線用戶列表的功能,無論是HttpSessionListener還是HttpSessionBindingListener都依賴session機制。但app未退出,服務端的session會話就銷毀了,導致無法正確監測到app用戶的在線情況。
1.session的生命周期
-
當服務端接收到請求時,服務端會為其分配一個
session會話
-
這個
session會話
有一個最大保存時間MaxInactiveInterval
,當用戶的不活動狀態
超過這個時間,session會銷毀 -
如果是網頁端,當用戶注銷或者退出瀏覽器時,session會銷毀
2.android無法保持會話的原因
-
網頁端的每次請求都是活動,將重置
session會話
的時間,實現持久訪問 -
android端就不同了,它的請求頭中是不帶sessionId的,所以服務端無法識別它訪問的是哪個session,也無法識別請求來自於哪個session
3.解決問題
建議大家安裝 FireFox瀏覽器,它的檢查功能是真的好用,比谷歌瀏覽器好用
-
寫個servlet,運行服務端,網頁里加個css外鏈
-
用FireFox打開網頁並檢查
-
注意到高亮的
Cookie:JSESSIONID=70943E03B9683A966235579F4D57892F
,打印session.getId()
驗證,發現這個JSESSIONID
就是本次session的sessionId
-
如何查看android訪問服務端的請求頭呢?在servlet添加以下代碼:
Cookie[] Cookies = request.getCookies(); for(int i =0;i<Cookies.length;i++){ Cookie c = Cookies[i]; System.out.println(c.getName() + "=" + c.getValue()); }
-
使用android發起一個請求,報錯java.lang.NullPointerException,說明android默認的請求是不帶Cookie的
-
給android的請求頭加上Cookie,我這里使用的是OkHttp3
Request request = new Request.Builder() .url(url).addHeader("Cookie", "JSESSIONID=" + session_id) .post(body) .build();
-
至於這個session_id,可以這樣做:服務端接收到登錄請求,獲取會話的sessionID,返回給android端,android端存到SharedPreferences里,發送請求的時候取出
-
使用帶Cookie的方法再次訪問服務端,可以看到打印出
JSESSIONID
,這時候android和網頁端一樣都能實現session持久了
4.驗證Session持久
-
在服務端工程的web.xml里添加
<session-config> <session-timeout>1</session-timeout> </session-config>
-
表示設置session的最大保存時間為1分鍾,設置為1分鍾是為了方便驗證
-
添加在線用戶監聽器,這里給出一個方案:
//登錄處理的Servlet添加,user為用戶實體對象 session.setAttribute("onlineUserBindingListener", new OnlineUserBindingListener(user));
//在線用戶監聽器,使用注解方式 import java.util.ArrayList; import java.util.List; import javax.servlet.ServletContext; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import domain.User; @WebListener public class OnlineUserBindingListener implements HttpSessionBindingListener { private User user; public OnlineUserBindingListener() { } public OnlineUserBindingListener(User user) { this.user = user; } @SuppressWarnings("unchecked") public void valueBound(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); ServletContext application = session.getServletContext(); List<User> onlineUserList = (List<User>) application.getAttribute("onlineUserList"); if (onlineUserList == null) { onlineUserList = new ArrayList<User>(); application.setAttribute("onlineUserList", onlineUserList); } onlineUserList.add(this.user); } @SuppressWarnings("unchecked") public void valueUnbound(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); ServletContext application = session.getServletContext(); List<User> onlineUserList = (List<User>) application.getAttribute("onlineUserList"); onlineUserList.remove(this.user); } }
-
網頁端顯示用戶列表,直接application.getAttribute("onlineUserList"),我不再贅述。網頁每30秒自動刷新:
<meta http-equiv="refresh" content="30"/>
-
app登錄后不操作,1分鍾后用戶列表數據消失
-
app登錄后連續操作,每次操作間隔在1分鍾內,用戶列表數據持續存在。驗證成功
5.其它
-
session的getAttribute和setAttribute方法是不會影響它的生命周期的,仍視為無活動狀態
-
根據sessionId獲取session的方法已經再Servlet 2.5后棄用,仍然想實現該功能的可以參考https://blog.csdn.net/sihai12345/article/details/81098765,基本原理是自己維護一個
HashMap<String,HttpSession>
-
Jsp中顯示實體型列表盡量結合JSTL和EL表達式,簡便清楚,用JSTL別忘了導包。示例:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<c:when test="${requestScope.operat_type eq 'userList'}">
<table>
<thead>
<tr>
<th>姓名</th>
<th>密碼</th>
<th>部門</th>
<th>等級</th>
<th>注冊時間</th>
<th>最近登錄時間</th>
<th>最近登錄IP</th>
<th>登錄次數</th>
</tr>
</thead>
<tbody>
<c:forEach items="${requestScope.userList}" var="user">
<tr>
<td>${user.name }</td>
<td>${user.password }</td>
<td>${user.department }</td>
<td>${user.grade }</td>
<td>${fn:substring(user.reg_time,0,19) }</td>
<td>${fn:substring(user.log_time,0,19) }</td>
<td>${user.log_ip }</td>
<td>${user.log_count }</td>
</tr>
</c:forEach>
</tbody>
</table>
</c:when>
轉載請注明博文來源,有什么問題歡迎在評論欄留言。 ——Kevin_Lu 2020/4/14