一、概述
前面是扯犢子的,JSP/SERVLET基於HTTP規範,提供了幾種安全支持,BASIC、DIGEST、FORM、CLIENT-CERT,本文主要介紹一下FORM的安全支持開發,文末有一個小demo用於展示。
因爲FORM方式比較少用,因此本文介紹的原理點到即止,並沒有特別特別深入,見諒。
更多詳情,請參見《JSP/SERVLET核心編程》(新浪微盤,您找PDF的天堂···)
二、詳細
1. 原理簡述
FROM聲明式安全基於表單驗證。
簡述:用戶想要訪問某一個受保護的資源,如果用戶未登路,則將用戶導向一個特定格式的表單進行身份驗證,驗證通過則繼續訪問資源;驗證不通過,則將用戶導向一個提示頁面。
關鍵點:
a. 對受保護資源進行配置。本例是基於URL進行限制保護,因此需要在web.xml中配置受保護的URL;
b. 特定格式的表單寫法。主要是三部分的固定寫法,一是表單action必須爲j_security_check,用戶名輸入input的name必須爲j_username,用戶密碼的輸入input的name必須爲j_password;
c. 只要用戶登錄成功,並且支持cookie(會話cookie,跟session搭一塊幹活的那個cookie:JSESSIONID),那麼這個用戶名和密碼就會被保存到當前用戶的session中,後續只要用戶的會話cookie存在並且服務器session未過期,則一直驗證通過,否則驗證失敗,更多原理,請往下看;
FROM驗證的安全性基於cookie/session機制的安全性,並且可以配置通過SSL傳輸,因此安全性還是比較高的,至少比BASIC的安全性高了不止一個等級。之前寫的一個小demo,手動實現BASIC驗證(不是基於JSP/SERVLET提供的封裝了的BASIC,而是其底層原汁原味id實現):點擊打開鏈接
d. 侷限性。用戶的身份驗證,是基於服務器提供的用戶角色,以tomcat爲例,所有基於FORM的用戶安全驗證,都是基於tomcat user的安全驗證。也就是說,能夠通過用戶驗證的用戶配置,都是屬於tomcat_dir/conf/tomcat-user.xml中的<role rolename="xxx"/>配置和<user username="ooo" password="ooo" roles="xxx"/>。因此,基於表單驗證FORM的侷限性在於程序的可移植性,儘管代碼不需要重寫,但是不同的服務器的用戶規則不一樣,因此需要重新配置用戶;
2. demo講解
業務邏輯:首頁index.jsp有一個超鏈,訪問一個受保護的路徑,如果用戶未被驗證,則自動跳轉到認證頁面,否則直接訪問資源;如果認證失敗,則自動跳轉到一個失敗提示頁面。
整個FORM驗證的所有核心在於配置文件web.xml的配置,因此在理解demo業務邏輯的基礎上,本節的所有內容將圍繞配置文件展開。
2.1 首頁
即index.jsp,這個頁面上有一個超鏈,用於訪問受保護資源,內容如下所示。
<a href="${pageContext.request.contextPath}/gif">查看羣相冊</a>
2.2 配置文件
此時,因爲超鏈的訪問路徑“/gif”是受保護的,因此會判斷用戶是否已經驗證通過,判斷的條件是根據會話cookie找到對應的session,看session裏面是否已經存在j_username和j_password。下面分別來講解。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>春田花花同學會</display-name>
<!--
一下配置都是基於FROM表單身份驗證的角度來說明的
爲了更好的閱讀,請重下網上看配置文件,謝謝合作!
-->
<!--
和form-error-page不同的是,當在授權頁面進行驗證的時候,如果username是一個合法的認證用戶
即username所在的role存在tomcat-users.xml中,但是這個role不具備訪問該資源的權限,則會返回403,跳轉到location指定的頁面
而form-error-page是認證失敗:1. 用戶名或密碼錯誤;2. username不存在tomcat-users.xml中
-->
<error-page>
<error-code>403</error-code>
<location>/security/fail.jsp</location>
</error-page>
<!--
login-config:
1. 當訪問受限資源但是還未驗證,則跳轉到form-login-page頁面,這個頁面的編寫需要固定的格式(理出爲不變格式化的內容,其他可選自填),形如
<form action="j_security_check">
<input type="text" name="j_username"/>
<input type="password" name="j_password"/>
</form>
如果有密碼,則表單最好是post提交
2. 當驗證失敗,即用戶名和密碼錯誤或用戶不在授權列表中,則直接跳轉到form-error-page指定頁面
這個頁面隨便編寫,目的是提示用戶
-->
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/security/login.jsp</form-login-page>
<form-error-page>/security/logfail.jsp</form-error-page>
</form-login-config>
</login-config>
<!--
security-role:對應着tomcat-users.xml中的role配置
可選,有無均可以,配置主要是方便項目開發的人查看
-->
<security-role>
<role-name>user-auth</role-name>
</security-role>
<!-- 安全約束配置:
display-name:可選,表示受保護的資源名
web-resource-collection:必選,至少一個,配置受保護的資源
本處配置的受保護資源爲:/gif
auth-constraint:授權約束,可選,role-name的value應該是在tomcat-users.xml中配置的<role rolename="xxx"/>
如果配置了auth-constraint,但是沒有設置value,則表示任何人都不能訪問
-->
<security-constraint>
<display-name>R_file</display-name>
<web-resource-collection>
<web-resource-name>aiyou_gif</web-resource-name>
<url-pattern>/gif</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user-auth</role-name>
</auth-constraint>
</security-constraint>
<!-- 首頁,這個頁面的超鏈請求到一個受保護的資源路徑(/gif,即上文中security-constraint中配置),於是被攔截到一個登陸認證的頁面 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
2.3 實現原理
查看form-lgoin-page頁面的請求頭和響應頭,推測j_username和j_password是保存於session中,但是查看sessionScope是空的,未能進一步驗證。但是根據HTTP的跡象來推測,應該是介樣的,有大神能進一步解答,請不吝賜教!
General
Remote Address:[::1]:8080
Request URL:http://localhost:8080/web02/j_security_check
Request Method:POST
Status Code:302 Found
Response Headers
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: http://localhost:8080/web02/gif
Content-Length: 0
Date: Tue, 16 Jun 2015 09:54:39 GMT
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:36
Content-Type:application/x-www-form-urlencoded
Cookie:JSESSIONID=168CB17F9CF19F3615845B438332B7CD
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/web02/gif
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36
Form Data
view parsed
j_username=wangxinyan&j_password=123
隨即驗證通過,進行/gif的資源訪問,此時會設置新的會話cookie,實際上在訪問授權頁面的時候,也會生成新的會話cookie。
General
Remote Address:[::1]:8080
Request URL:http://localhost:8080/web02/gif
Request Method:GET
Status Code:200 OK
Response Headers
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=6F12E8122EB68186270DC626974E24FE; Path=/web02/; HttpOnly
Cache-Control: private
Expires: Thu, 01 Jan 1970 08:00:00 CST
Content-Type: text/html;charset=UTF-8
Content-Length: 306
Date: Tue, 16 Jun 2015 09:54:39 GMT
Request Headers
GET /web02/gif HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36
Referer: http://localhost:8080/web02/gif
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: JSESSIONID=168CB17F9CF19F3615845B438332B7CD
基於Form的身份驗證,是需要瀏覽器支持cookie,否則機制會癱瘓。但是整個驗證的流程中HTTP透露的信息來看,並沒有看到set-cookie除了會話cookie JSESSIONID之外的其他cookie,因此並不是將認證信息存放於cookie中;但是整個流程中都存在會話cookie,因此猜測FORM機制是將認證信息存放於session,每次訪問cookie都會帶着session id到瀏覽器,如果訪問受保護資源,則瀏覽器通會過id找到session,session保存了認證信息,因此用戶可以直接訪問資源。
並且,爲了輔助驗證,做了session.invalidate();的測試,發現當session失效以後,必須要重新驗證才能訪問受保護的資源,因此可以確定驗證信息是藉助於會話cookie/session機制來實現的,因此其安全性依賴於session和會話cookie的安全性,如果需要進一步提高安全性,在security-constraint中還可以配置SSL進行數據傳輸,因此,這種驗證方式還是比較安全的!
三、demo下載
示例demo百度雲地址:點擊打開鏈接
(該demo僅用於展示原理,其中還有很多不完善的地方,僅僅能作爲一個參考,謝謝!)
附註:
參考資料:《JSP/SERVLET核心編程(第二版)》
本文如有錯漏,煩請不吝指正,謝謝!