/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.citrus.service.requestcontext.session.impl;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.alibaba.citrus.service.requestcontext.RequestContext;
import com.alibaba.citrus.service.requestcontext.RequestContextException;
import com.alibaba.citrus.service.requestcontext.session.SessionConfig;
import com.alibaba.citrus.service.requestcontext.session.SessionConfig.CookieConfig;
import com.alibaba.citrus.service.requestcontext.session.SessionRequestContext;
import com.alibaba.citrus.service.requestcontext.support.AbstractRequestContextWrapper;
import com.alibaba.citrus.service.requestcontext.support.AbstractRequestWrapper;
import com.alibaba.citrus.service.requestcontext.support.AbstractResponseWrapper;
import com.alibaba.citrus.service.requestcontext.util.CookieSupport;
import com.alibaba.citrus.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** 支持session的<code>HttpRequestContext</code>实现。 */
public class SessionRequestContextImpl extends AbstractRequestContextWrapper implements SessionRequestContext {
private final static Logger log = LoggerFactory.getLogger(SessionRequestContext.class);
private SessionConfig sessionConfig;
private boolean requestedSessionIDParsed;
private String requestedSessionID;
private boolean requestedSessionIDFromCookie;
private boolean requestedSessionIDFromURL;
private SessionImpl session;
private boolean sessionReturned;
/** 构造函数。 */
public SessionRequestContextImpl(RequestContext wrappedContext, SessionConfig sessionConfig) {
super(wrappedContext);
this.sessionConfig = sessionConfig;
setRequest(new SessionRequestWrapper(wrappedContext.getRequest()));
setResponse(new SessionResponseWrapper(wrappedContext.getResponse()));
}
/**
* 取得<code>SessionConfig</code>实例。
*
* @return <code>SessionConfig</code>实例
*/
public SessionConfig getSessionConfig() {
return sessionConfig;
}
/**
* 判断session是否已经作废。
*
* @return 如已作废,则返回<code>true</code>
*/
public boolean isSessionInvalidated() {
return session == null ? false : session.isInvalidated();
}
/**
* 清除session。类似<code>invalidate()</code>方法,但支持后续操作,而不会抛出
* <code>IllegalStateException</code>。
*/
public void clear() {
if (session != null) {
session.clear();
}
}
/**
* 取得当前的session ID。
*
* @return session ID
*/
public String getRequestedSessionID() {
ensureRequestedSessionID();
return requestedSessionID;
}
/**
* 当前的session ID是从cookie中取得的吗?
*
* @return 如果是,则返回<code>true</code>
*/
public boolean isRequestedSessionIDFromCookie() {
ensureRequestedSessionID();
return requestedSessionIDFromCookie;
}
/**
* 当前的session ID是从URL中取得的吗?
*
* @return 如果是,则返回<code>true</code>
*/
public boolean isRequestedSessionIDFromURL() {
ensureRequestedSessionID();
return requestedSessionIDFromURL;
}
/**
* 判断当前的session ID是否仍然合法。
*
* @return 如果是,则返回<code>true</code>
*/
public boolean isRequestedSessionIDValid() {
HttpSession session = getSession(false);
return session != null && session.getId().equals(requestedSessionID);
}
/** 确保session ID已经从request中被解析出来了。 */
private void ensureRequestedSessionID() {
if (!requestedSessionIDParsed) {
if (sessionConfig.getId().isCookieEnabled()) {
requestedSessionID = decodeSessionIDFromCookie();
requestedSessionIDFromCookie = requestedSessionID != null;
}
if (requestedSessionID == null && sessionConfig.getId().isUrlEncodeEnabled()) {
requestedSessionID = decodeSessionIDFromURL();
requestedSessionIDFromURL = requestedSessionID != null;
}
}
}
/** 将session ID编码到Cookie中去。 */
public void encodeSessionIDIntoCookie() {
writeSessionIDCookie(session.getId());
}
/** 将session ID从Cookie中删除。 */
public void clearSessionIDFromCookie() {
writeSessionIDCookie("");
}
/** 写cookie。 */
private void writeSessionIDCookie(String cookieValue) {
CookieConfig cookieConfig = sessionConfig.getId().getCookie();
CookieSupport cookie = new CookieSupport(cookieConfig.getName(), cookieValue);
String cookieDomain = cookieConfig.getDomain();
if (!StringUtil.isEmpty(cookieDomain)) {
cookie.setDomain(cookieDomain);
}
String cookiePath = cookieConfig.getPath();
if (!StringUtil.isEmpty(cookiePath)) {
cookie.setPath(cookiePath);
}
int cookieMaxAge = cookieConfig.getMaxAge();
if (cookieMaxAge > 0) {
cookie.setMaxAge(cookieMaxAge);
}
cookie.setHttpOnly(cookieConfig.isHttpOnly());
cookie.setSecure(cookieConfig.isSecure());
log.debug("{}", cookie);
cookie.addCookie(getResponse());
}
/**
* 从cookie中取得session ID。
*
* @return 如果存在,则返回session ID,否则返回<code>null</code>
*/
public String decodeSessionIDFromCookie() {
Cookie[] cookies = getRequest().getCookies();
if (cookies != null) {
String sessionCookieName = sessionConfig.getId().getCookie().getName();
for (Cookie cookie : cookies) {
if (cookie.getName().equals(sessionCookieName)) {
String sessionID = StringUtil.trimToNull(cookie.getValue());
if (sessionID != null) {
return sessionID;
}
}
}
}
return null;
}
/**
* 将session ID编码到URL中去。
*
* @return 包含session ID的URL
*/
public String encodeSessionIDIntoURL(String url) {
HttpSession session = getRequest().getSession(false);
if (session != null && (session.isNew() || isRequestedSessionIDFromURL() && !isRequestedSessionIDFromCookie())) {
String sessionID = session.getId();
String keyName = getSessionConfig().getId().getUrlEncode().getName();
int keyNameLength = keyName.length();
int urlLength = url.length();
int urlQueryIndex = url.indexOf('?');
if (urlQueryIndex >= 0) {
urlLength = urlQueryIndex;
}
boolean found = false;
for (int keyBeginIndex = url.indexOf(';'); keyBeginIndex >= 0 && keyBeginIndex < urlLength; keyBeginIndex = url
.indexOf(';', keyBeginIndex + 1)) {
keyBeginIndex++;
if (urlLength - keyBeginIndex <= keyNameLength
|| !url.regionMatches(keyBeginIndex, keyName, 0, keyNameLength)
|| url.charAt(keyBeginIndex + keyNameLength) != '=') {
continue;
}
int valueBeginIndex = keyBeginIndex + keyNameLength + 1;
int valueEndIndex = url.indexOf(';', valueBeginIndex);
if (valueEndIndex < 0) {
valueEndIndex = urlLength;
}
if (!url.regionMatches(valueBeginIndex, sessionID, 0, sessionID.length())) {
url = url.substring(0, valueBeginIndex) + sessionID + url.substring(valueEndIndex);
}
found = true;
break;
}
if (!found) {
url = url.substring(0, urlLength) + ';' + keyName + '=' + sessionID + url.substring(urlLength);
}
}
return url;
}
/**
* 从URL中取得session ID。
*
* @return 如果存在,则返回session ID,否则返回<code>null</code>
*/
public String decodeSessionIDFromURL() {
String uri = getRequest().getRequestURI();
String keyName = sessionConfig.getId().getUrlEncode().getName();
int uriLength = uri.length();
int keyNameLength = keyName.length();
for (int keyBeginIndex = uri.indexOf(';'); keyBeginIndex >= 0; keyBeginIndex = uri.indexOf(';',
keyBeginIndex + 1)) {
keyBeginIndex++;
if (uriLength - keyBeginIndex <= keyNameLength
|| !uri.regionMatches(keyBeginIndex, keyName, 0, keyNameLength)
|| uri.charAt(keyBeginIndex + keyNameLength) != '=') {
continue;
}
int valueBeginIndex = keyBeginIndex + keyNameLength + 1;
int valueEndIndex = uri.indexOf(';', valueBeginIndex);
if (valueEndIndex < 0) {
valueEndIndex = uriLength;
}
return uri.substring(valueBeginIndex, valueEndIndex);
}
return null;
}
/**
* 取得当前的session,如果不存在,且<code>create</code>为<code>true</code>,则创建一个新的。
*
* @param create 必要时是否创建新的session
* @return 当前的session或新的session,如果不存在,且<code>create</code>为
* <code>false</code> ,则返回<code>null</code>
*/
public HttpSession getSession(boolean create) {
// 如果getSession方法已经被执行过了,那么直接返回
if (session != null && sessionReturned) {
return session;
}
// 创建session,尽管有可能创建却不返回
if (session == null) {
// 从request中取得session ID
ensureRequestedSessionID();
String sessionID = requestedSessionID;
boolean isNew = false;
// 如果sessionID为空,则创建一个新的ID
if (sessionID == null) {
if (!create) {
return null; // 除了create=false,直接返回null
}
sessionID = sessionConfig.getId().getGenerator().generateSessionID();
isNew = true;
}
// 不管怎样,先创建一个session对象再说,但这个session有可能不存在或是过期的
session = new SessionImpl(sessionID, this, isNew, create);
}
// Session为new,有可能是sessionID为空,或是sessionID对应的session不存在,或是session已过期
// 如果同时create为false,返回null就可以了。
if (session.isNew() && !create) {
return null;
}
// 如果原来sessionID已存在于request中,不论session是否是新建与否,重用该sessionID。
// 因此,在这种情况下,就不需要再设置cookie了。
if (sessionConfig.getId().isCookieEnabled() && !session.getId().equals(requestedSessionID)) {
if (getResponse().isCommitted()) {
throw new IllegalStateException(
"Failed to create a new session because the responseWrapper was already committed");
}
encodeSessionIDIntoCookie();
}
sessionReturned = true;
return session;
}
@Override
public void commitHeaders() throws RequestContextException {
if (!sessionReturned) {
return;
}
if (session.isInvalidated()) {
// 清除cookie中的session ID
clearSessionIDFromCookie();
}
session.commit(true);
}
/** 结束一个请求。 */
@Override
public void commit() {
if (!sessionReturned) {
return;
}
session.commit(false);
}
/** 支持session的<code>HttpServletRequestWrapper</code>。 */
private class SessionRequestWrapper extends AbstractRequestWrapper {
/**
* 构造函数。
*
* @param request 被包装的<code>HttpServletRequest</code>实例
*/
public SessionRequestWrapper(HttpServletRequest request) {
super(SessionRequestContextImpl.this, request);
}
/**
* 取得当前request中的session ID。
*
* @return session ID
*/
@Override
public String getRequestedSessionId() {
return SessionRequestContextImpl.this.getRequestedSessionID();
}
/**
* 当前的session ID是从cookie中取得的吗?
*
* @return 如果是,则返回<code>true</code>
*/
@Override
public boolean isRequestedSessionIdFromCookie() {
return SessionRequestContextImpl.this.isRequestedSessionIDFromCookie();
}
/**
* 当前的session ID是从URL中取得的吗?
*
* @return 如果是,则返回<code>true</code>
*/
@Override
public boolean isRequestedSessionIdFromURL() {
return SessionRequestContextImpl.this.isRequestedSessionIDFromURL();
}
/**
* 判断当前的session ID是否仍然合法。
*
* @return 如果是,则返回<code>true</code>
*/
@Override
public boolean isRequestedSessionIdValid() {
return SessionRequestContextImpl.this.isRequestedSessionIDValid();
}
/**
* 取得当前的session,如果不存在,则创建一个新的。
*
* @return 当前的session或新的session
*/
@Override
public HttpSession getSession() {
return getSession(true);
}
/**
* 取得当前的session,如果不存在,且<code>create</code>为<code>true</code>,则创建一个新的。
*
* @param create 必要时是否创建新的session
* @return 当前的session或新的session,如果不存在,且<code>create</code>为
* <code>false</code>,则返回<code>null</code>
*/
@Override
public HttpSession getSession(boolean create) {
return SessionRequestContextImpl.this.getSession(create);
}
/** @deprecated use isRequestedSessionIdFromURL instead */
@Override
@Deprecated
public boolean isRequestedSessionIdFromUrl() {
return isRequestedSessionIdFromURL();
}
}
/** 支持session的<code>HttpServletResponseWrapper</code>。 */
private class SessionResponseWrapper extends AbstractResponseWrapper {
/**
* 构造函数。
*
* @param response 被包装的<code>HttpServletResponse</code>实例
*/
public SessionResponseWrapper(HttpServletResponse response) {
super(SessionRequestContextImpl.this, response);
}
/**
* 将session ID编码到URL中。
*
* @param url 要编码的URL
* @return 包含session ID的URL
*/
@Override
public String encodeURL(String url) {
if (sessionConfig.getId().isUrlEncodeEnabled()) {
url = SessionRequestContextImpl.this.encodeSessionIDIntoURL(url);
}
return url;
}
/**
* 将session ID编码到URL中。
*
* @param url 要编码的URL
* @return 包含session ID的URL
*/
@Override
public String encodeRedirectURL(String url) {
if (sessionConfig.getId().isUrlEncodeEnabled()) {
url = SessionRequestContextImpl.this.encodeSessionIDIntoURL(url);
}
return url;
}
/** @deprecated use encodeURL instead */
@Override
@Deprecated
public String encodeUrl(String url) {
return encodeURL(url);
}
/** @deprecated use encodeRedirectURL instead */
@Override
@Deprecated
public String encodeRedirectUrl(String url) {
return encodeRedirectURL(url);
}
}
}