android保持Session會話


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM