From 72f718f71252e6681eb5818103abd7b88bc01580 Mon Sep 17 00:00:00 2001 From: shuzheng <469741414@qq.com> Date: Fri, 24 Feb 2017 00:09:37 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99=E5=9F=BA=E4=BA=8Eredis?= =?UTF-8?q?=E7=9A=84sessionDAO=EF=BC=8C=E6=8C=81=E4=B9=85=E5=8C=96session?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../upms/server/controller/SSOController.java | 99 ++++++++------- .../upms/server/shiro/SessionRedisDao.java | 117 ++++++++++++++++++ .../resources/applicationContext-shiro.xml | 34 ++--- 3 files changed, 175 insertions(+), 75 deletions(-) create mode 100644 zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/shiro/SessionRedisDao.java diff --git a/zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/controller/SSOController.java b/zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/controller/SSOController.java index 46b03128..5f44fb2f 100644 --- a/zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/controller/SSOController.java +++ b/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; } diff --git a/zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/shiro/SessionRedisDao.java b/zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/shiro/SessionRedisDao.java new file mode 100644 index 00000000..bfdddbe6 --- /dev/null +++ b/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 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; + } + +} diff --git a/zheng-upms/zheng-upms-server/src/main/resources/applicationContext-shiro.xml b/zheng-upms/zheng-upms-server/src/main/resources/applicationContext-shiro.xml index e1e3f309..f98bf86d 100644 --- a/zheng-upms/zheng-upms-server/src/main/resources/applicationContext-shiro.xml +++ b/zheng-upms/zheng-upms-server/src/main/resources/applicationContext-shiro.xml @@ -19,19 +19,11 @@ - - /manage/**=authc - - /druid/**=user - - /sso/logout = logout - - / = 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 @@ -47,7 +39,7 @@ - + @@ -76,13 +68,7 @@ - - - - - - - + @@ -91,11 +77,11 @@ - + - + @@ -124,8 +110,6 @@ - -