在Servlet 3.0中增加對Cookie(請注意,這里所說的Cookie,僅指和Session互動的Cookie,即人們常說的會話Cookie)較為全面的操作API。最為突出特性:支持直接修改Session ID的名稱(默認為“JSESSIONID”),支持對cookie設置HttpOnly屬性以增強安全,避免一定程度的跨站攻擊。
相關較為深入信息,請訪問
進階閱讀部分。
以前的實現
雖然新的API提供了簡答便捷的API操作會話Cookie,但新的API之前,我們可以較為生硬的操作響應頭部,完成設定工作。看看以前的代碼吧:
/**
* 自定義會話Cookie屬性
* @author yongboy
* @date 2011-1-19
* @version 1.0
*/
@WebFilter(dispatcherTypes = { DispatcherType.REQUEST }, urlPatterns = { "/*" })
public class CustomCookieFilter implements Filter {
private static final Log log = LogFactory.getLog(CustomCookieFilter.class);
private static final String CUSTOM_SESSION_ID = "YONGBOYID";
private static final String HTTP_ONLY = "HttpOnly";
private static final String SET_COOKIE = "SET-COOKIE";
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse rep,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) rep;
if (response.containsHeader(SET_COOKIE)) {
log.info("haha,we have one new user visit the site ...");
String sessionId = request.getSession().getId();
String cookieValue = CUSTOM_SESSION_ID + "=" + sessionId + ";Path="
+ request.getContextPath() + ";" + HTTP_ONLY;
log.info(SET_COOKIE + ":" + cookieValue);
response.setHeader(SET_COOKIE, cookieValue);
}
chain.doFilter(req, response);
}
public void init(FilterConfig fConfig) throws ServletException {
}
}
利用攔截器實現,判斷每次請求的響應是否包含SET-COOKIE頭部,重寫會話Cookie,添加需要的屬性。雖較為生硬,但靈活性強。
新的規范API
新的規范添加SessionCookieConfig接口,用于操作會話Cookie,需要掌握以下主要方法:
- setName(String name)
修改Session ID的名稱,默認為"JSESSIONID" - setDomain(String domain)
設置當前Cookie所處于的域 - setPath(String path)
設置當前Cookie所處于的相對路徑 - setHttpOnly(boolean httpOnly)
設置是否支持HttpOnly屬性 - setSecure(boolean secure)
若使用HTTPS安全連接,則需要設置其屬性為true - setMaxAge(int maxAge)
設置存活時間,單位為秒
如何使用呢,很方便,在ServletContextListener監聽器初始化方法中進行設定即可;下面實例演示如何修改"JSESSIONID",以及添加支持HttpOnly支持:
/** 全局設置Session-Cookie相交互部分屬性
*
* @author yongboy
* @date 2011-1-19
* @version 1.0
*/
@WebListener
public class SessionCookieInitialization implements ServletContextListener {
private static final Log log = LogFactory
.getLog(SessionCookieInitialization.class);
public void contextInitialized(ServletContextEvent sce) {
log.info("now init the Session Cookie");
ServletContext servletContext = sce.getServletContext();
SessionCookieConfig sessionCookie = servletContext
.getSessionCookieConfig();
sessionCookie.setName("YONGBOYID");
sessionCookie.setPath(servletContext.getContextPath());
sessionCookie.setHttpOnly(true);
sessionCookie.setSecure(false);
log.info("name : " + sessionCookie.getName() + "\n" + "domain:"
+ sessionCookie.getDomain() + "\npath:"
+ sessionCookie.getPath() + "\nage:"
+ sessionCookie.getMaxAge());
log.info("isHttpOnly : " + sessionCookie.isHttpOnly());
log.info("isSecure : " + sessionCookie.isSecure());
}
public void contextDestroyed(ServletContextEvent sce) {
log.info("the context is destroyed !");
}
}
需要通過ServletContext對象獲得SessionCookieConfig對象,才能夠進一步自定義session cookie的名字等屬性。
無論以前的硬編碼還是新的API實現,目標都是一致的,所產生頭部信息也是完全一致。毫無疑問,后者更為方便快捷,省缺了顯示的操作響應元數據。
對當前站點的第一次請求,很容易從響應頭信息中看到Set-Cookie的屬性值:
不同瀏覽器平臺上測試
- 在Safari、IE8、Opera 11 一切都很正常
- Firefox 3.6、Chrome 9.0,JSESSIONID會繼續存在:
YONGBOYID=601A6C82D535343163B175A4FD5376EA; JSESSIONID=AA78738AB1EAD1F9C649F705EC64D92D; AJSTAT_ok_times=6; JSESSIONID=abcpxyJmIpBVz6WHVo_1s; BAYEUX_BROWSER=439-1vyje1gmqt8y8giva7pqsu1
- 在所有瀏覽器中,SESSION ID等于新設置的YONGBOYID值(若不相等,問題就嚴重了!)
- 在客戶端JS無法獲得正確的SESSIONI ID了。
Tomcat服務器內置支持在Tomcat 6-7,可以不用如上顯示設置Cookie domain、name、HttpOnly支持,在conf/context.xml文件中配置即可:
<Context useHttpOnly="true", sessionCookieName="YONGBOYID", sessionCookieDomain="/servlet3" … >
...
</Context>
既然JAVA應用服務器本身支持會話Cookie設定,那就沒有必要在程序代碼中再次進行編碼了。這是一個好的實踐:不要重復造輪子。
這里給出一段測試Session重寫的一段腳本:
<div style="margin: 40px; paddding: 10px">
<div><a href="sessionCookieTest">正常連接</a></div>
<div><a href="<%=response.encodeURL("sessionCookieTest") %>">重定向連接</a></div>
</div>
會被重寫的URL地址類似于:
http://localhost/servlet3/sessionCookieTest;YONGBOYID=19B94935D50245270060E49C9E69F5B6
嗯,在取消會話Cookie之后,可以直接看到修改后的SESSION ID名稱了,當然這時候HttpOnly屬性也沒有多大意義了。
有一點別忘記,設置HttpOnly之后,客戶端的JS將無法獲取的到會話ID了。
進階閱讀:
- 維基對HttpOnly的解釋
- 利用HTTP-only Cookie緩解XSS之痛
- Tomcat Context 屬性
- HttpOnly cookie與跨站點追蹤