Explorar el Código

重写基于redis的sessionDAO,持久化session

shuzheng hace 9 años
padre
commit
72f718f712

+ 49 - 50
zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/controller/SSOController.java

@@ -3,12 +3,15 @@ package com.zheng.upms.server.controller;
 import com.zheng.common.base.BaseController;
 import com.zheng.common.util.CookieUtil;
 import com.zheng.common.util.RedisUtil;
-import com.zheng.upms.server.util.SystemConstant;
+import com.zheng.upms.common.constant.UpmsResult;
+import com.zheng.upms.common.constant.UpmsResultConstant;
 import com.zheng.upms.dao.model.UpmsSystemExample;
 import com.zheng.upms.rpc.api.UpmsSystemService;
 import com.zheng.upms.rpc.api.UpmsUserService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang.BooleanUtils;
+import org.apache.commons.lang.ObjectUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authc.IncorrectCredentialsException;
@@ -16,6 +19,8 @@ import org.apache.shiro.authc.LockedAccountException;
 import org.apache.shiro.authc.UnknownAccountException;
 import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.subject.Subject;
+import org.apache.shiro.web.util.SavedRequest;
+import org.apache.shiro.web.util.WebUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -29,11 +34,11 @@ import redis.clients.jedis.Jedis;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.net.URLEncoder;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
+import static org.apache.shiro.web.util.WebUtils.getSavedRequest;
+
 /**
  * 单点登录管理
  * Created by shuzheng on 2016/12/10.
@@ -65,36 +70,38 @@ public class SSOController extends BaseController {
 	public String index(HttpServletRequest request) throws Exception {
 		String system_name = request.getParameter("system_name");
 		String backurl = request.getParameter("backurl");
+		if (StringUtils.isBlank(system_name)) {
+			throw new RuntimeException("无效访问!");
+		}
 
 		// 判断请求认证系统是否注册
 		UpmsSystemExample upmsSystemExample = new UpmsSystemExample();
 		upmsSystemExample.createCriteria()
 				.andNameEqualTo(system_name);
 		int count = upmsSystemService.countByExample(upmsSystemExample);
-		if (StringUtils.isEmpty(system_name) || 0 == count) {
-			_log.info("未注册的系统:{}", system_name);
-			return "/500";
+		if (0 == count) {
+			throw new RuntimeException(String.format("未注册的系统:%s", system_name));
 		}
 		return "redirect:/sso/login?backurl=" + URLEncoder.encode(backurl, "utf-8");
 	}
 
 	@ApiOperation(value = "登录")
 	@RequestMapping(value = "/login", method = RequestMethod.GET)
-	public String login(HttpServletRequest request, HttpServletResponse response) {
+	public String login(HttpServletRequest request) {
 		// 分配单点登录sessionId,首次获取后缓存到cookie,防止session丢失
 		String serverSessionId = CookieUtil.getCookie(request, ZHENG_UPMS_SERVER_SESSION_ID);
-		if (StringUtils.isEmpty(serverSessionId)) {
-			serverSessionId = request.getSession().getId();
-			CookieUtil.setCookie(response, ZHENG_UPMS_SERVER_SESSION_ID, serverSessionId);
+		if (StringUtils.isBlank(serverSessionId)) {
+			Subject subject = SecurityUtils.getSubject();
+			serverSessionId = subject.getSession().getId().toString();
 		}
 		// 有回跳路径的访问判断是否已登录,如果已登录,则回跳
 		String backurl = request.getParameter("backurl");
 		String token = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + serverSessionId);
 		// token校验值
-		if (!StringUtils.isEmpty(token)) {
+		if (!StringUtils.isBlank(token)) {
 			// 回跳
 			String redirectUrl = backurl;
-			if (StringUtils.isEmpty(backurl)) {
+			if (StringUtils.isBlank(backurl)) {
 				redirectUrl = "/";
 			} else {
 				if (backurl.contains("?")) {
@@ -103,7 +110,7 @@ public class SSOController extends BaseController {
 					redirectUrl += "?token=" + token;
 				}
 			}
-			_log.info("认证中心帐号通过,带token回跳:{}", redirectUrl);
+			_log.debug("认证中心帐号通过,带token回跳:{}", redirectUrl);
 			return "redirect:" + redirectUrl;
 		}
 		return "/sso/login";
@@ -116,54 +123,44 @@ public class SSOController extends BaseController {
 		String backurl = request.getParameter("backurl");
 		String username = request.getParameter("username");
 		String password = request.getParameter("password");
+		String rememberMe = request.getParameter("rememberMe");
 
-		Map result = new HashMap<>();
-		String data = "";
-		if (StringUtils.isEmpty(username)) {
-			result.put("result", false);
-			result.put("data", SystemConstant.NO_USERNAME);
-			return result;
+		if (StringUtils.isBlank(username)) {
+			return new UpmsResult(UpmsResultConstant.EMPTY_USERNAME, "帐号不能为空!");
 		}
-		if (StringUtils.isEmpty(password)) {
-			result.put("result", false);
-			result.put("data", SystemConstant.NO_PASSWORD);
-			return result;
+		if (StringUtils.isBlank(password)) {
+			return new UpmsResult(UpmsResultConstant.EMPTY_PASSWORD, "密码不能为空!");
 		}
 		// 使用shiro认证
 		Subject subject = SecurityUtils.getSubject();
 		UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
 		try {
-			//usernamePasswordToken.setRememberMe(false);
+			if (BooleanUtils.toBoolean(rememberMe)) {
+				usernamePasswordToken.setRememberMe(true);
+			} else {
+				usernamePasswordToken.setRememberMe(false);
+			}
 			subject.login(usernamePasswordToken);
 		} catch (UnknownAccountException e) {
-			result.put("result", false);
-			result.put("data", SystemConstant.ERROR_USERNAME);
-			return result;
+			return new UpmsResult(UpmsResultConstant.INVALID_USERNAME, "帐号不存在!");
 		} catch (IncorrectCredentialsException e) {
-			result.put("result", false);
-			result.put("data", SystemConstant.ERROR_PASSWORD);
-			return result;
+			return new UpmsResult(UpmsResultConstant.INVALID_PASSWORD, "密码错误!");
 		} catch (LockedAccountException e) {
-			result.put("result", false);
-			result.put("data", SystemConstant.INVALID_ACCOUNT);
-			return result;
-		}
-		// 分配单点登录sessionId,首次获取后缓存到cookie,防止session丢失
-		String serverSessionId = CookieUtil.getCookie(request, ZHENG_UPMS_SERVER_SESSION_ID);
-		if (StringUtils.isEmpty(serverSessionId)) {
-			serverSessionId = request.getSession().getId();
-			CookieUtil.setCookie(response, ZHENG_UPMS_SERVER_SESSION_ID, serverSessionId);
+			return new UpmsResult(UpmsResultConstant.INVALID_ACCOUNT, "帐号已锁定!");
 		}
+		// serverSessionId
+		String serverSessionId = subject.getSession().getId().toString();
 		// 默认验证帐号密码正确,创建token
 		String token = UUID.randomUUID().toString();
 		// 全局会话sessionId
 		RedisUtil.set(ZHENG_UPMS_SERVER_SESSION_ID + "_" + serverSessionId, token, TIMEOUT);
 		// token校验值
 		RedisUtil.set(ZHENG_UPMS_SERVER_TOKEN + "_" + token, token, TIMEOUT);
-		// 回调子系统
-		if (StringUtils.isEmpty(backurl)) {
-			result.put("result", true);
-			result.put("data", "/");
+		// 回跳登录前地址
+		if (StringUtils.isBlank(backurl)) {
+			SavedRequest savedRequest = WebUtils.getSavedRequest(request);
+			backurl = null == savedRequest ? "/" : savedRequest.getRequestURI();
+			return new UpmsResult(UpmsResultConstant.SUCCESS, backurl);
 		} else {
 			String redirectUrl = backurl;
 			if (backurl.contains("?")) {
@@ -171,11 +168,9 @@ public class SSOController extends BaseController {
 			} else {
 				redirectUrl += "?token=" + token;
 			}
-			_log.info("认证中心帐号通过,带token回跳:{}", redirectUrl);
-			result.put("result", true);
-			result.put("data", redirectUrl);
+			_log.debug("认证中心帐号通过,带token回跳:{}", redirectUrl);
+			return new UpmsResult(UpmsResultConstant.SUCCESS, redirectUrl);
 		}
-		return result;
 	}
 
 	@ApiOperation(value = "校验token")
@@ -184,7 +179,7 @@ public class SSOController extends BaseController {
 	public String token(HttpServletRequest request) {
 		String tokenParam = request.getParameter("token");
 		String token = RedisUtil.get(ZHENG_UPMS_SERVER_TOKEN + "_" + tokenParam);
-		if (StringUtils.isEmpty(tokenParam) || !tokenParam.equals(token)) {
+		if (StringUtils.isBlank(tokenParam) || !tokenParam.equals(token)) {
 			return "failed";
 		}
 		return "success";
@@ -212,10 +207,14 @@ public class SSOController extends BaseController {
 		}
 		// 清除全局会话sessionId
 		CookieUtil.removeCookie(response, ZHENG_UPMS_SERVER_SESSION_ID);
-		_log.info("当前token={},对应的注册系统个数:{}个", token, jedis.scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + token));
+		_log.debug("当前token={},对应的注册系统个数:{}个", token, jedis.scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + token));
+        jedis.close();
 		// 跳回原地址
 		String redirectUrl = request.getHeader("Referer");
-		_log.info("跳回退出登录请求地址:{}", redirectUrl);
+		if (null == redirectUrl) {
+			redirectUrl = "/";
+		}
+		_log.debug("跳回退出登录请求地址:{}", redirectUrl);
 		return "redirect:" + redirectUrl;
 	}
 

+ 117 - 0
zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/shiro/SessionRedisDao.java

@@ -0,0 +1,117 @@
+package com.zheng.upms.server.shiro;
+
+import com.zheng.common.util.CookieUtil;
+import com.zheng.common.util.RedisUtil;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.SimpleSession;
+import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import redis.clients.jedis.Jedis;
+
+import java.io.*;
+import java.util.Set;
+
+/**
+ * 基于redis的sessionDao,缓存共享session
+ * Created by shuzheng on 2017/2/23.
+ */
+public class SessionRedisDao extends EnterpriseCacheSessionDAO {
+
+    private static Logger _log = LoggerFactory.getLogger(SessionRedisDao.class);
+    // 全局会话key
+    private final static String ZHENG_UPMS_SERVER_SESSION_ID = "zheng-upms-server-session-id";
+    // token key
+    private final static String ZHENG_UPMS_SERVER_TOKEN = "zheng-upms-server-token";
+    // 局部会话key
+    private final static String ZHENG_UPMS_CLIENT_SESSION_ID = "zheng-upms-client-session-id";
+    // 单点同一个token所有局部会话key
+    private final static String ZHENG_UPMS_CLIENT_SESSION_IDS = "zheng-upms-client-session-ids";
+
+    @Override
+    protected Serializable doCreate(Session session) {
+        Serializable sessionId = super.doCreate(session);
+        RedisUtil.set(sessionToByte("shiro-sessionId-" + sessionId), sessionToByte(session));
+        _log.debug("[SessionRedisDao]创建session: sessionId={}", session.getId());
+        return sessionId;
+    }
+
+    @Override
+    protected Session doReadSession(Serializable sessionId) {
+        // 先从缓存中获取session,如果没有再去redis中获取
+        Session session = super.doReadSession(sessionId);
+        if(session == null){
+            byte[] bytes = RedisUtil.get(sessionId.toString().getBytes());
+            if(null != bytes && bytes.length > 0){
+                session = (Session) byteToSession(bytes);
+                _log.debug("[SessionRedisDao]redis中获取session: sessionId={}", session.getId());
+            }
+        }
+        return session;
+    }
+
+    @Override
+    protected void doUpdate(Session session) {
+        // 更新session的最后一次访问时间
+        super.doUpdate(session);
+        RedisUtil.set(session.getId().toString().getBytes(), sessionToByte(session));
+        _log.debug("[SessionRedisDao]redis中更新session: sessionId={}", session.getId());
+    }
+
+    @Override
+    protected void doDelete(Session session) {
+        // 删除session前,清空所有注册的局部会话
+        String serverSessionId = session.getId().toString();
+        // 当前全局会话token
+        String token = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + serverSessionId);
+        // 清除全局会话
+        RedisUtil.remove(ZHENG_UPMS_SERVER_SESSION_ID + "_" + serverSessionId);
+        // 清除token校验值
+        RedisUtil.remove(ZHENG_UPMS_SERVER_TOKEN + "_" + token);
+        // 清除所有局部会话
+        Jedis jedis = RedisUtil.getJedis();
+        Set<String> clientSessionIds = jedis.smembers(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + token);
+        for (String clientSessionId : clientSessionIds) {
+            jedis.del(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + clientSessionId);
+            jedis.srem(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + token, clientSessionId);
+        }
+        _log.debug("当前token={},对应的注册系统个数:{}个", token, jedis.scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + token));
+        jedis.close();
+
+        // 删除session
+        super.doDelete(session);
+        RedisUtil.remove(session.getId().toString().getBytes());
+        _log.debug("[SessionRedisDao]redis中删除session: sessionId={}", session.getId());
+    }
+
+    // 把Object对象转化为byte保存到redis中
+    public byte[] sessionToByte(Object session) {
+        ByteArrayOutputStream bo = new ByteArrayOutputStream();
+        byte[] bytes = null;
+        try {
+            ObjectOutputStream oo = new ObjectOutputStream(bo);
+            oo.writeObject(session);
+            bytes = bo.toByteArray();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return bytes;
+    }
+
+    // 把byte还原为Object
+    public Object byteToSession(byte[] bytes) {
+        ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
+        ObjectInputStream in;
+        SimpleSession session = null;
+        try {
+            in = new ObjectInputStream(bi);
+            session = (SimpleSession) in.readObject();
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return session;
+    }
+
+}

+ 9 - 25
zheng-upms/zheng-upms-server/src/main/resources/applicationContext-shiro.xml

@@ -19,19 +19,11 @@
         </property>
         <property name="filterChainDefinitions">
             <value>
-                <!-- isAuthenticated -->
-                /manage/**=authc
-                <!-- isAuthenticated或RememberMe -->
-                /druid/**=user
-                <!-- logout -->
-                /sso/logout = logout
-                <!-- anon -->
-                / = anon
-                /swagger-ui.html = anon
-                /webjars/** = anon
-                /resources/** = anon
-                /sso/** = anon
-                /403 = anon
+                /swagger-ui.html = user
+                /druid/** = user
+                /manage/index = user
+                /manage/** = authc
+                /** = anon
             </value>
         </property>
     </bean>
@@ -47,7 +39,7 @@
     </bean>
 
     <!-- realm实现,继承自AuthorizingRealm -->
-    <bean id="upmsRealm" class="com.zheng.upms.server.realm.UpmsRealm"></bean>
+    <bean id="upmsRealm" class="com.zheng.upms.server.shiro.realm.UpmsRealm"></bean>
 
     <!-- 会话管理器 -->
     <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
@@ -76,13 +68,7 @@
     </bean>
 
     <!-- 会话DAO,可重写,持久化session -->
-    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.MemorySessionDAO">
-        <!--<property name="shiroSessionRepository" ref="jedisShiroSessionRepository"/>-->
-        <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
-    </bean>
-
-    <!-- 会话ID生成器 -->
-    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
+    <bean id="sessionDAO" class="com.zheng.upms.server.shiro.SessionRedisDao"/>
 
     <!-- 会话Cookie模板 -->
     <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
@@ -91,11 +77,11 @@
         <!-- 设置Cookie的过期时间,秒为单位,默认-1表示关闭浏览器时过期Cookie -->
         <property name="maxAge" value="-1"/>
         <!-- Cookie名称 -->
-        <property name="name" value="zheng-upms-shiro-sessionId"/>
+        <property name="name" value="zheng-upms-server-session-id"/>
     </bean>
 
     <!-- 会话监听器 -->
-    <bean id="sessionListener" class="com.zheng.upms.server.listener.ShiroSessionListener"/>
+    <bean id="sessionListener" class="com.zheng.upms.server.shiro.listener.ShiroSessionListener"/>
 
     <!-- rememberMe管理器 -->
     <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
@@ -124,8 +110,6 @@
         <property name="loginUrl" value="/sso/login"/>
     </bean>
 
-
-
     <!-- 设置SecurityUtils,相当于调用SecurityUtils.setSecurityManager(securityManager) -->
     <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
         <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>