在北京項目中遇到了session不能共享的問題,按照一般的處理方式,Nginx配置ip_hash即可,但是配置之后也沒用。仔細分析北京的環境,請求的地址是外網四層地址,再用Nginx轉發到內網四層地址,所以即使在nginx配置了ipHash,也會在四層交換被打亂。最后采用的解決方案是將session寫入redis,因為北京項目使用了shiro,自定義授權會話管理即可
1.在spring-context-shiro.xml中配置路徑
<bean id="sessionDAO" class="com.cs.core.common.security.shiro.session.JedisSessionDAO">
<property name="sessionIdGenerator" ref="idGen" />
<property name="sessionKeyPrefix" value="jcs_session_" />
</bean>
2.JedisSessionDao.java
package com.cs.core.common.security.shiro.session;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ailk.jike.common.cache.redis.RedisClient;
import com.cs.core.common.config.Global;
import com.cs.core.common.utils.DateUtils;
import com.cs.core.common.web.Servlets;
import com.google.common.collect.Sets;
/**
* 自定義授權會話管理類
*/
public class JedisSessionDAO extends AbstractSessionDAO implements SessionDAO {
private Logger logger = LoggerFactory.getLogger(getClass());
private String sessionKeyPrefix = "jcs_session_";
@Resource
private RedisClient redisClient;
@Override
public void update(Session session) throws UnknownSessionException {
if (session == null || session.getId() == null) {
return;
}
HttpServletRequest request = Servlets.getRequest();
if (request != null){
String uri = request.getServletPath();
// 如果是靜態文件,則不更新SESSION
if (Servlets.isStaticFile(uri)){
return;
}
// 如果是視圖文件,則不更新SESSION
if (org.apache.commons.lang3.StringUtils.startsWith(uri, Global.getConfig("web.view.prefix"))
&& org.apache.commons.lang3.StringUtils.endsWith(uri, Global.getConfig("web.view.suffix"))){
return;
}
// 手動控制不更新SESSION
if (Global.NO.equals(request.getParameter("updateSession"))){
return;
}
}
try {
// 獲取登錄者編號
PrincipalCollection pc = (PrincipalCollection)session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
String principalId = pc != null ? pc.getPrimaryPrincipal().toString() : org.apache.commons.lang3.StringUtils.EMPTY;
redisClient.hset(sessionKeyPrefix, session.getId().toString(), principalId + "|" + session.getTimeout() + "|" + session.getLastAccessTime().getTime());
redisClient.set(sessionKeyPrefix + session.getId(), session);
// 設置超期時間
long timeoutSeconds = (long)(session.getTimeout() / 1000);
redisClient.expire((sessionKeyPrefix + session.getId()), timeoutSeconds);
logger.debug("update {} {}", session.getId(), request != null ? request.getRequestURI() : "");
} catch (Exception e) {
logger.error("update {} {}", session.getId(), request != null ? request.getRequestURI() : "", e);
}
}
@Override
public void delete(Session session) {
if (session == null || session.getId() == null) {
return;
}
//Jedis jedis = null;
try {
//jedis = JedisUtils.getResource();
redisClient.hdel(sessionKeyPrefix, session.getId().toString());
redisClient.del(sessionKeyPrefix+ session.getId());
logger.debug("delete {} ", session.getId());
} catch (Exception e) {
logger.error("delete {} ", session.getId(), e);
}
}
@Override
public Collection<Session> getActiveSessions() {
return getActiveSessions(true);
}
/**
* 獲取活動會話
* @param includeLeave 是否包括離線(最后訪問時間大於3分鍾為離線會話)
* @return
*/
@Override
public Collection<Session> getActiveSessions(boolean includeLeave) {
return getActiveSessions(includeLeave, null, null);
}
/**
* 獲取活動會話
* @param includeLeave 是否包括離線(最后訪問時間大於3分鍾為離線會話)
* @param principal 根據登錄者對象獲取活動會話
* @param filterSession 不為空,則過濾掉(不包含)這個會話。
* @return
*/
@Override
public Collection<Session> getActiveSessions(boolean includeLeave, Object principal, Session filterSession){
Set<Session> sessions = Sets.newHashSet();
//Jedis jedis = null;
try {
//jedis = JedisUtils.getResource();
Map<String, String> map = redisClient.hgetAll(sessionKeyPrefix);
for (Map.Entry<String, String> e : map.entrySet()){
if (org.apache.commons.lang3.StringUtils.isNotBlank(e.getKey()) && org.apache.commons.lang3.StringUtils.isNotBlank(e.getValue())){
String[] ss = org.apache.commons.lang3.StringUtils.split(e.getValue(), "|");
if (ss != null && ss.length == 3){// jedis.exists(sessionKeyPrefix + e.getKey())){
// Session session = (Session)JedisUtils.toObject(jedis.get(JedisUtils.getBytesKey(sessionKeyPrefix + e.getKey())));
SimpleSession session = new SimpleSession();
session.setId(e.getKey());
session.setAttribute("principalId", ss[0]);
session.setTimeout(Long.valueOf(ss[1]));
session.setLastAccessTime(new Date(Long.valueOf(ss[2])));
try{
// 驗證SESSION
session.validate();
boolean isActiveSession = false;
// 不包括離線並符合最后訪問時間小於等於3分鍾條件。
if (includeLeave || DateUtils.pastMinutes(session.getLastAccessTime()) <= 3){
isActiveSession = true;
}
// 符合登陸者條件。
if (principal != null){
PrincipalCollection pc = (PrincipalCollection)session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (principal.toString().equals(pc != null ? pc.getPrimaryPrincipal().toString() : org.apache.commons.lang3.StringUtils.EMPTY)){
isActiveSession = true;
}
}
// 過濾掉的SESSION
if (filterSession != null && filterSession.getId().equals(session.getId())){
isActiveSession = false;
}
if (isActiveSession){
sessions.add(session);
}
}
// SESSION驗證失敗
catch (Exception e2) {
redisClient.hdel(sessionKeyPrefix, e.getKey());
}
}
// 存儲的SESSION不符合規則
else{
redisClient.hdel(sessionKeyPrefix, e.getKey());
}
}
// 存儲的SESSION無Value
else if (org.apache.commons.lang3.StringUtils.isNotBlank(e.getKey())){
redisClient.hdel(sessionKeyPrefix, e.getKey());
}
}
logger.info("getActiveSessions size: {} ", sessions.size());
} catch (Exception e) {
logger.error("getActiveSessions", e);
}
return sessions;
}
@Override
protected Serializable doCreate(Session session) {
HttpServletRequest request = Servlets.getRequest();
if (request != null){
String uri = request.getServletPath();
// 如果是靜態文件,則不創建SESSION
if (Servlets.isStaticFile(uri)){
return null;
}
}
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.update(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
Session s = null;
HttpServletRequest request = Servlets.getRequest();
if (request != null){
String uri = request.getServletPath();
// 如果是靜態文件,則不獲取SESSION
if (Servlets.isStaticFile(uri)){
return null;
}
s = (Session)request.getAttribute("session_"+sessionId);
}
if (s != null){
return s;
}
Session session = null;
try {
session = (Session)redisClient.get(sessionKeyPrefix + sessionId);
logger.debug("doReadSession {} {}", sessionId, request != null ? request.getRequestURI() : "");
} catch (Exception e) {
logger.error("doReadSession {} {}", sessionId, request != null ? request.getRequestURI() : "", e);
}
if (request != null && session != null){
request.setAttribute("session_"+sessionId, session);
}
return session;
}
@Override
public Session readSession(Serializable sessionId) throws UnknownSessionException {
try{
return super.readSession(sessionId);
}catch (UnknownSessionException e) {
return null;
}
}
public String getSessionKeyPrefix() {
return sessionKeyPrefix;
}
public void setSessionKeyPrefix(String sessionKeyPrefix) {
this.sessionKeyPrefix = sessionKeyPrefix;
}
}