HttpSession與JSESSIONID的”盜用”
先說一下什麼是HttpSession,Http協定是一種無狀态的協定,當我們從用戶端發起一個浏覽器請求的時候,伺服器端如果說需要保留我們的登入資訊的話,我們就需要通過某種方式解決這個登入問題。
在B/S模式中不可能每次通路伺服器都把自己的登入資訊傳遞到伺服器端,如果說我們不考慮單點登入系統和cookie沒有被禁用的情況下,首選的就是HttpSession技術實作儲存用戶端傳遞過來的資訊或者是根據參數對資料庫查詢後的結果。如果說是cookie技術被禁用的話,導緻的結果就是HttpSession對象被廢掉,也無法使用。HttpSession如果說沒有設定聲明周期的話,它會在浏覽器關閉的時候清除在浏覽器上對應的存儲的資訊(JSESSIONID)。當我們開着浏覽器,預設30min不操作的情況下,目前會話會自動死亡。
下面我們用一個項目模拟一下JSESSIONID盜用問題。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX1kFVPFTR650MNRkT4VEWjZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TN3cDN1czMxIDOxcDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
首先清除一下浏覽器的緩存,這樣更友善于檢視我們的測試效果。這個框框彈出來的快捷鍵是shift+ctrl+del
。做好這個基本工作之後我們就來進入編碼階段:
下面這是我們測試使用的javaBean 一個User實體類
package com.test.entity;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String username;
private String password;
private String phone;
private String role;
private Integer companyId;
private String lastLoginTime;
public User() {
super();
}
public User(Integer id, String username, String password, String phone,
String role, Integer companyId, String lastLoginTime) {
this();
this.id = id;
this.username = username;
this.password = password;
this.phone = phone;
this.role = role;
this.companyId = companyId;
this.lastLoginTime = lastLoginTime;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Integer getCompanyId() {
return companyId;
}
public void setCompanyId(Integer companyId) {
this.companyId = companyId;
}
public String getLastLoginTime() {
return lastLoginTime;
}
public void setLastLoginTime(String lastLoginTime) {
this.lastLoginTime = lastLoginTime;
}
@Override
public int hashCode() {
final int prime = ;
int result = ;
result = prime * result
+ ((companyId == null) ? : companyId.hashCode());
result = prime * result + ((id == null) ? : id.hashCode());
result = prime * result
+ ((lastLoginTime == null) ? : lastLoginTime.hashCode());
result = prime * result
+ ((password == null) ? : password.hashCode());
result = prime * result + ((phone == null) ? : phone.hashCode());
result = prime * result + ((role == null) ? : role.hashCode());
result = prime * result
+ ((username == null) ? : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (companyId == null) {
if (other.companyId != null)
return false;
} else if (!companyId.equals(other.companyId))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (lastLoginTime == null) {
if (other.lastLoginTime != null)
return false;
} else if (!lastLoginTime.equals(other.lastLoginTime))
return false;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
if (phone == null) {
if (other.phone != null)
return false;
} else if (!phone.equals(other.phone))
return false;
if (role == null) {
if (other.role != null)
return false;
} else if (!role.equals(other.role))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password="
+ password + ", phone=" + phone + ", role=" + role
+ ", companyId=" + companyId + ", lastLoginTime="
+ lastLoginTime + "]";
}
}
JavaBean必須實作序列化接口,重寫hashCode( )方法和equals( )方法。
我們再資料庫中對應的表是 :
準備完畢,我們開始準備Servlet提供一個通路的方法。
在這裡,大家一定要知道的是這裡的Servlet是HttpServelt,這個類聲明周期方法必須爛熟于心。
package com.test.servlet;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.test.entity.User;
import com.test.utils.JDBCUtils;
public class LoginServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "select * from user where username = ? and password = ?";
Connection conn = JDBCUtils.getConnection();
PreparedStatement pstmt = null;
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(,username);
pstmt.setString(,password);
ResultSet rs = pstmt.executeQuery();
if(rs.next()){
int id = rs.getInt("id");
String DBUsername = rs.getString("username");
String DBPassword = rs.getString("password");
String phone = rs.getString("phone");
String role = rs.getString("role");
int companyId = rs.getInt("companyId");
String lastLoginTime = rs.getString("lastLoginTime");
User user = new User(id, DBUsername, DBPassword, phone, role, companyId, lastLoginTime);
request.getSession().setAttribute("user",user);
Cookie[] cookies = request.getCookies();
for(Cookie cookie:cookies){
System.out.println(cookie.getName());
System.out.println(cookie.getValue());
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
在上面的Servlet中我們使用到了JDBCUtils方法,這個就是我們自己封裝的一個方法,不要想着它有多麼的高大上,就是很普通的一個工具類而已。
我們還是展示一下我們的JDBCUtils方法的具體編碼 :
package com.test.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
private static String driverName;
private static String url;
private static String username;
private static String password;
private static Properties props;
static {
props = new Properties();
InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
props.load(in);
driverName = props.getProperty("mysql.driver");
url = props.getProperty("mysql.url");
username = props.getProperty("mysql.username");
password = props.getProperty("mysql.password");
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName(driverName);
conn = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static void main(String[] args) {
System.out.println(getConnection());
}
}
然後是我們的jdbc配置檔案 :
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:/cctrace
mysql.username=root
mysql.password=
這樣的話,我們還差一個設定JSESSIONID的servlet。其代碼實作如下所示 :
package com.test.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SetJeSessionIdServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String JESESSIONID = request.getParameter("JSESSIONID");
Cookie cookie = new Cookie("JSESSIONID",JESESSIONID);
response.addCookie(cookie);
}
}
以上基本上已經把準備工作做完,還差一個展示頁面,我們就用歡迎頁中的index.jsp頁面配合EL表達式來做展示功能。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
${sessionScope.user.username}
</body>
</html>
上述是我們的展示頁面,使我們第一次通路該站點時要通路的頁面,HttpSession在jsp頁面第一次被通路時或者在Servlet中第一次被調用request(HttpServletRequest).getSession()時建立。但是目前會話中不存在這個對象,是以不會顯示任何資訊。
看一下web.xml中的配置資訊,我們對應的映射之後,我們就可以對該項目進行測試了 :
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>JSessionIdDBTest</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>com.test.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>setJeSession</servlet-name>
<servlet-class>com.test.servlet.SetJeSessionIdServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>setJeSession</servlet-name>
<url-pattern>/setSession.do</url-pattern>
</servlet-mapping>
</web-app>
通路結果是空白,下面我們登陸一下,登陸之後,HttpSession中會儲存一個user對象,就是目前登入使用者。
簡單的模拟了登入之後,我們看一下控制台列印效果 :
登入之後的結果是 :
此處擷取到了前台傳遞過來的Cookie中的JSESSIONID,也就是說我們擷取到了一個HttpSession對象的靈魂。
調用了這個方法之後,我們換一個浏覽器,但是不登入,去添加一個Cookie,通路我們寫好的Servlet就OK了。
我們的servlet中的處理如圖 :
我們沒有調用登入方法,換了個浏覽器,我們去首頁測試一下。
,沒有登入,盜取JSESSIONID成功。