Browse Source

集中会话和授权移到zheng-upms-client

shuzheng 9 years ago
parent
commit
f34fab55a9
16 changed files with 592 additions and 399 deletions
  1. 19 0
      zheng-cms/zheng-cms-admin/src/main/resources/zheng-upms-client-shiro.properties
  2. 2 2
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/filter/SSOFilter.java
  3. 0 210
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/UpmsSessionDao.java
  4. 170 0
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/filter/UpmsAuthenticationFilter.java
  5. 1 1
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/filter/UpmsSessionForceLogoutFilter.java
  6. 2 2
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/listener/UpmsSessionListener.java
  7. 1 1
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/session/UpmsSession.java
  8. 165 0
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/session/UpmsSessionDao.java
  9. 3 3
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/session/UpmsSessionFactory.java
  10. 3 3
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/util/RequestParameterUtil.java
  11. 45 0
      zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/util/SerializableUtil.java
  12. 26 30
      zheng-upms/zheng-upms-client/src/main/resources/applicationContext-shiro.xml
  13. 2 0
      zheng-upms/zheng-upms-common/src/main/java/com/zheng/upms/common/constant/UpmsConstant.java
  14. 138 146
      zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/controller/SSOController.java
  15. 1 1
      zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/controller/manage/UpmsSessionController.java
  16. 14 0
      zheng-upms/zheng-upms-server/src/main/resources/zheng-upms-client-shiro.properties

+ 19 - 0
zheng-cms/zheng-cms-admin/src/main/resources/zheng-upms-client-shiro.properties

@@ -0,0 +1,19 @@
+# \u7EC8\u7AEF\u7C7B\u578B
+upms.type=client
+# \u7EC8\u7AEFsession\u540D\u79F0
+upms.session.id=zheng-upms-client-session-id
+# \u767B\u5F55\u5730\u5740
+sso.server.url=http://upms.zhangshuzheng.cn:1111
+# \u767B\u5F55\u6210\u529F\u56DE\u8C03\u5730\u5740
+upms.successUrl=/manage/index
+# \u672A\u6388\u6743\u5730\u5740
+upms.unauthorizedUrl=/403
+# \u4F1A\u8BDD\u65F6\u957F,\u534A\u5C0F\u65F6\uFF08\u5355\u4F4D\u6BEB\u79D2\uFF09
+upms.session.timeout=1800000
+# \u8BB0\u4F4F\u5BC6\u7801\u65F6\u957F30\u5929
+upms.rememberMe.timeout=2592000
+# \u5B50\u7CFB\u7EDFid
+AppID=zheng-cms-admin
+AppSecret=
+# \u5F00\u53D1\u6A21\u5F0F
+debug=false

+ 2 - 2
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/filter/SSOFilter.java

@@ -73,7 +73,7 @@ public class SSOFilter implements Filter {
         if (null != clientSessionId && !StringUtils.isBlank(cacheToken)) {
             // 移除url中的token参数
             if (null != request.getParameter("token")) {
-                String backUrl = RequestParameterUtil.getParameterWithOutToken(request);
+                String backUrl = RequestParameterUtil.getParameterWithOutCode(request);
                 response.sendRedirect(backUrl.toString());
             } else {
                 filterChain.doFilter(request, response);
@@ -116,7 +116,7 @@ public class SSOFilter implements Filter {
                             jedis.close();
                             _log.debug("当前token={},对应的注册系统个数:{}个", token, RedisUtil.getJedis().scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + token));
                             // 移除url中的token参数
-                            String backUrl = RequestParameterUtil.getParameterWithOutToken(request);
+                            String backUrl = RequestParameterUtil.getParameterWithOutCode(request);
                             // 返回请求资源
                             response.sendRedirect(backUrl.toString());
                             return;

+ 0 - 210
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/UpmsSessionDao.java

@@ -1,210 +0,0 @@
-package com.zheng.upms.client.shiro;
-
-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.*;
-
-/**
- * 基于redis的sessionDao,缓存共享session
- * Created by shuzheng on 2017/2/23.
- */
-public class UpmsSessionDao extends EnterpriseCacheSessionDAO {
-
-    private static Logger _log = LoggerFactory.getLogger(UpmsSessionDao.class);
-    // 全局会话cookie的key
-    private final static String ZHENG_UPMS_SERVER_SESSION_ID = "zheng-upms-server-session-id";
-    // 全局会话redis的key
-    private final static String ZHENG_UPMS_SHIRO_SESSION_ID = "zheng-upms-shiro-session-id";
-    // 全局会话redis的id列表key
-    private final static String ZHENG_UPMS_SHIRO_SESSION_IDS = "zheng-upms-shiro-session-ids";
-    // 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((ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId).getBytes(), sessionToByte(session), (int) session.getTimeout() / 1000);
-        _log.debug("[UpmsSessionDao]创建session: sessionId={}", session.getId());
-        // 维护会话id列表,提供会话分页管理
-        Jedis jedis = RedisUtil.getJedis();
-        jedis.lpush(ZHENG_UPMS_SHIRO_SESSION_IDS, sessionId.toString());
-        jedis.close();
-        return sessionId;
-    }
-
-    @Override
-    protected Session doReadSession(Serializable sessionId) {
-        // 先从缓存中获取session,如果没有再去redis中获取
-        Session session = super.doReadSession(sessionId);
-        if(session == null){
-            byte[] bytes = RedisUtil.get((ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId).getBytes());
-            if(null != bytes && bytes.length > 0){
-                session = (Session) byteToSession(bytes);
-                _log.debug("[UpmsSessionDao]redis中获取session: sessionId={}", session.getId());
-            }
-        }
-        return session;
-    }
-
-    @Override
-    protected void doUpdate(Session session) {
-        // 更新session的最后一次访问时间
-        UpmsSession upmsSession = (UpmsSession) session;
-        UpmsSession cacheUpmsSession = (UpmsSession) doReadSession(session.getId());
-        upmsSession.setStatus(cacheUpmsSession.getStatus());
-        upmsSession.setAttribute("FORCE_LOGOUT", cacheUpmsSession.getAttribute("FORCE_LOGOUT"));
-        super.doUpdate(session);
-        RedisUtil.set((ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId()).getBytes(), sessionToByte(session), (int) session.getTimeout() / 1000);
-        _log.debug("[UpmsSessionDao]redis中更新session: sessionId={}, seconds={}", session.getId(), (int) session.getTimeout() / 1000);
-    }
-
-    @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((ZHENG_UPMS_SHIRO_SESSION_ID + "_" + serverSessionId).getBytes());
-        _log.debug("[UpmsSessionDao]redis中删除session: sessionId={}", session.getId());
-
-        // 维护会话id列表,提供会话分页管理
-        jedis = RedisUtil.getJedis();
-        jedis.lrem(ZHENG_UPMS_SHIRO_SESSION_IDS, 1, serverSessionId);
-        jedis.close();
-    }
-
-    /**
-     * 获取会话列表
-     * @param offset
-     * @param limit
-     * @return
-     */
-    public Map getActiveSessions(int offset, int limit) {
-        Map sessions = new HashMap();
-        Jedis jedis = RedisUtil.getJedis();
-        // 获取在线会话总数
-        long total = jedis.llen(ZHENG_UPMS_SHIRO_SESSION_IDS);
-        // 获取当前页会话详情
-        List<String> ids = jedis.lrange(ZHENG_UPMS_SHIRO_SESSION_IDS, offset, (offset + limit - 1));
-        List<Session> rows = new ArrayList<>();
-        for (String id : ids) {
-            byte[] bytes = RedisUtil.get((ZHENG_UPMS_SHIRO_SESSION_ID + "_" + id).getBytes());
-            if(null != bytes && bytes.length > 0){
-                Session session = (Session) byteToSession(bytes);
-                rows.add(session);
-            }
-        }
-        jedis.close();
-        sessions.put("total", total);
-        sessions.put("rows", rows);
-        return sessions;
-    }
-
-    /**
-     * 强制退出
-     * @param ids
-     * @return
-     */
-    public int forceout(String ids) {
-        String[] sessionIds = ids.split(",");
-        for (String serverSessionId : sessionIds) {
-            // 清空所有注册的局部会话和token
-            // 当前全局会话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();
-
-            // 会话增加强制退出属性标识,当此会话访问系统时,判断有该标识,则退出登录
-            byte[] bytes = RedisUtil.get((ZHENG_UPMS_SHIRO_SESSION_ID + "_" + serverSessionId).getBytes());
-            if(null != bytes && bytes.length > 0){
-                UpmsSession session = (UpmsSession) byteToSession(bytes);
-                session.setStatus(UpmsSession.OnlineStatus.force_logout);
-                session.setAttribute("FORCE_LOGOUT", "FORCE_LOGOUT");
-                super.doUpdate(session);
-                RedisUtil.set((ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId()).getBytes(), sessionToByte(session), (int) session.getTimeout() / 1000);
-            }
-        }
-        return sessionIds.length;
-    }
-
-    /**
-     * 更改在线状态
-     * @param sessionId
-     * @param onlineStatus
-     */
-    public void updateStatus(Serializable sessionId, UpmsSession.OnlineStatus onlineStatus) {
-        UpmsSession session = (UpmsSession) doReadSession(sessionId);
-        session.setStatus(onlineStatus);
-        super.doUpdate(session);
-        RedisUtil.set((ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId()).getBytes(), sessionToByte(session), (int) session.getTimeout() / 1000);
-    }
-
-    // 把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;
-    }
-
-}

+ 170 - 0
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/filter/UpmsAuthenticationFilter.java

@@ -0,0 +1,170 @@
+package com.zheng.upms.client.shiro.filter;
+
+import com.zheng.common.util.PropertiesFileUtil;
+import com.zheng.common.util.RedisUtil;
+import com.zheng.upms.client.shiro.session.UpmsSessionDao;
+import com.zheng.upms.client.util.RequestParameterUtil;
+import com.zheng.upms.common.constant.UpmsConstant;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.web.filter.authc.AuthenticationFilter;
+import org.apache.shiro.web.util.WebUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import redis.clients.jedis.Jedis;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 重写authc过滤器
+ * Created by shuzheng on 2017/3/11.
+ */
+public class UpmsAuthenticationFilter extends AuthenticationFilter {
+
+    private final static Logger _log = LoggerFactory.getLogger(UpmsAuthenticationFilter.class);
+
+    // 局部会话key
+    private final static String ZHENG_UPMS_CLIENT_SESSION_ID = "zheng-upms-client-session-id";
+    // 单点同一个code所有局部会话key
+    private final static String ZHENG_UPMS_CLIENT_SESSION_IDS = "zheng-upms-client-session-ids";
+
+    @Autowired
+    UpmsSessionDao upmsSessionDao;
+
+    @Override
+    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+        Subject subject = getSubject(request, response);
+        Session session = subject.getSession();
+        // 判断请求类型
+        String upmsType = PropertiesFileUtil.getInstance("zheng-upms-client-shiro").get("upms.type");
+        session.setAttribute(UpmsConstant.UPMS_TYPE, upmsType);
+        if ("client".equals(upmsType)) {
+            return validateClient(request, response);
+        }
+        if ("server".equals(upmsType)) {
+            return subject.isAuthenticated();
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+        StringBuffer sso_server_url = new StringBuffer(PropertiesFileUtil.getInstance("zheng-upms-client-shiro").get("sso.server.url"));
+        // server需要登录
+        String upmsType = PropertiesFileUtil.getInstance("zheng-upms-client-shiro").get("upms.type");
+        if ("server".equals(upmsType)) {
+            WebUtils.toHttp(response).sendRedirect(sso_server_url.append("/sso/login").toString());
+            return false;
+        }
+        sso_server_url.append("/sso/index").append("?").append("appid").append("=").append(PropertiesFileUtil.getInstance("zheng-upms-client-shiro").get("AppID"));
+        // 回跳地址
+        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
+        StringBuffer backurl = httpServletRequest.getRequestURL();
+        String queryString = httpServletRequest.getQueryString();
+        if (StringUtils.isNotBlank(queryString)) {
+            backurl.append("?").append(queryString);
+        }
+        sso_server_url.append("&").append("backurl").append("=").append(URLEncoder.encode(backurl.toString(), "utf-8"));
+        WebUtils.toHttp(response).sendRedirect(sso_server_url.toString());
+        return false;
+    }
+
+    /**
+     * 认证中心登录成功带回code
+     * @param request
+     */
+    private boolean validateClient(ServletRequest request, ServletResponse response) {
+        Subject subject = getSubject(request, response);
+        Session session = subject.getSession();
+        String sessionId = session.getId().toString();
+        int timeOut = (int) session.getTimeout() / 1000;
+        // 是否开发模式,为true则直接放行
+        boolean debug = PropertiesFileUtil.getInstance("zheng-upms-client-shiro").getBool("debug");
+        if (debug) {
+            return true;
+        }
+        // 判断局部会话是否登录
+        String cacheClientSession = RedisUtil.get(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + session.getId());
+        if (StringUtils.isNotBlank(cacheClientSession)) {
+            // 更新code有效期
+            RedisUtil.set(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId, cacheClientSession, timeOut);
+            Jedis jedis = RedisUtil.getJedis();
+            jedis.expire(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + cacheClientSession, timeOut);
+            jedis.close();
+            // 移除url中的code参数
+            if (null != request.getParameter("code")) {
+                String backUrl = RequestParameterUtil.getParameterWithOutCode(WebUtils.toHttp(request));
+                HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
+                try {
+                    httpServletResponse.sendRedirect(backUrl.toString());
+                } catch (IOException e) {
+                    _log.error("局部会话已登录,移除code参数跳转出错:", e);
+                }
+            } else {
+                return true;
+            }
+        }
+        // 判断是否有认证中心code
+        String code = request.getParameter("code");
+        // 已拿到code
+        if (StringUtils.isNotBlank(code)) {
+            // HttpPost去校验code
+            try {
+                StringBuffer sso_server_url = new StringBuffer(PropertiesFileUtil.getInstance("zheng-upms-client-shiro").get("sso.server.url"));
+                HttpClient httpclient = new DefaultHttpClient();
+                HttpPost httpPost = new HttpPost(sso_server_url.toString() + "/sso/code");
+
+                List<NameValuePair> nameValuePairs = new ArrayList<>();
+                nameValuePairs.add(new BasicNameValuePair("code", code));
+                httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
+
+                HttpResponse httpResponse = httpclient.execute(httpPost);
+                if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                    HttpEntity httpEntity = httpResponse.getEntity();
+                    String result = EntityUtils.toString(httpEntity);
+                    if (result.equals("success")) {
+                        // code校验正确,创建局部会话
+                        RedisUtil.set(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId, code, timeOut);
+                        // 保存code对应的局部会话sessionId,方便退出操作
+                        RedisUtil.sadd(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code, sessionId, timeOut);
+                        _log.info("当前code={},对应的注册系统个数:{}个", code, RedisUtil.getJedis().scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code));
+                        // 移除url中的token参数
+                        String backUrl = RequestParameterUtil.getParameterWithOutCode(WebUtils.toHttp(request));
+                        // 返回请求资源
+                        try {
+                            HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
+                            httpServletResponse.sendRedirect(backUrl.toString());
+                            return true;
+                        } catch (IOException e) {
+                            _log.error("已拿到code,移除code参数跳转出错:", e);
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                _log.error("验证token失败:", e);
+            }
+        }
+        return false;
+    }
+
+}

+ 1 - 1
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/UpmsSessionForceLogoutFilter.java → zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/filter/UpmsSessionForceLogoutFilter.java

@@ -1,4 +1,4 @@
-package com.zheng.upms.client.shiro;
+package com.zheng.upms.client.shiro.filter;
 
 import org.apache.shiro.session.Session;
 import org.apache.shiro.web.filter.AccessControlFilter;

+ 2 - 2
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/UpmsSessionListener.java → zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/listener/UpmsSessionListener.java

@@ -1,4 +1,4 @@
-package com.zheng.upms.client.shiro;
+package com.zheng.upms.client.shiro.listener;
 
 import org.apache.shiro.session.Session;
 import org.apache.shiro.session.SessionListener;
@@ -14,7 +14,7 @@ public class UpmsSessionListener implements SessionListener {
 
     @Override
     public void onStart(Session session) {
-        _log.debug("会话创建:" + session.getId());
+        _log.info("会话创建:" + session.getId());
     }
 
     @Override

+ 1 - 1
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/UpmsSession.java → zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/session/UpmsSession.java

@@ -1,4 +1,4 @@
-package com.zheng.upms.client.shiro;
+package com.zheng.upms.client.shiro.session;
 
 import org.apache.shiro.session.mgt.SimpleSession;
 

+ 165 - 0
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/session/UpmsSessionDao.java

@@ -0,0 +1,165 @@
+package com.zheng.upms.client.shiro.session;
+
+import com.zheng.common.util.RedisUtil;
+import com.zheng.upms.client.util.SerializableUtil;
+import com.zheng.upms.common.constant.UpmsConstant;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.SimpleSession;
+import org.apache.shiro.session.mgt.ValidatingSession;
+import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
+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.*;
+
+/**
+ * 基于redis的sessionDao,缓存共享session
+ * Created by shuzheng on 2017/2/23.
+ */
+public class UpmsSessionDao extends CachingSessionDAO {
+
+    private static Logger _log = LoggerFactory.getLogger(UpmsSessionDao.class);
+    // 会话key
+    private final static String ZHENG_UPMS_SHIRO_SESSION_ID = "zheng-upms-shiro-session-id";
+    // 全局会话key
+    private final static String ZHENG_UPMS_SERVER_SESSION_ID = "zheng-upms-server-session-id";
+    // 全局会话列表key
+    private final static String ZHENG_UPMS_SERVER_SESSION_IDS = "zheng-upms-server-session-ids";
+    // code key
+    private final static String ZHENG_UPMS_SERVER_CODE = "zheng-upms-server-code";
+    // 局部会话key
+    private final static String ZHENG_UPMS_CLIENT_SESSION_ID = "zheng-upms-client-session-id";
+    // 单点同一个code所有局部会话key
+    private final static String ZHENG_UPMS_CLIENT_SESSION_IDS = "zheng-upms-client-session-ids";
+
+    @Override
+    protected Serializable doCreate(Session session) {
+        Serializable sessionId = generateSessionId(session);
+        assignSessionId(session, sessionId);
+        RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId, SerializableUtil.serialize(session), (int) session.getTimeout() / 1000);
+        _log.info("doCreate >>>>> sessionId={}", sessionId);
+        return sessionId;
+    }
+
+    @Override
+    protected Session doReadSession(Serializable sessionId) {
+        String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
+        _log.debug("doReadSession >>>>> sessionId={}", sessionId);
+        return SerializableUtil.deserialize(session);
+    }
+
+    @Override
+    protected void doUpdate(Session session) {
+        // 如果会话过期/停止 没必要再更新了
+        if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()) {
+            return;
+        }
+        // 更新session的最后一次访问时间
+        UpmsSession upmsSession = (UpmsSession) session;
+        UpmsSession cacheUpmsSession = (UpmsSession) doReadSession(session.getId());
+        if (null != cacheUpmsSession) {
+            upmsSession.setStatus(cacheUpmsSession.getStatus());
+            upmsSession.setAttribute("FORCE_LOGOUT", cacheUpmsSession.getAttribute("FORCE_LOGOUT"));
+        }
+        RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId(), SerializableUtil.serialize(session), (int) session.getTimeout() / 1000);
+        _log.debug("doUpdate >>>>> sessionId={}", session.getId());
+    }
+
+    @Override
+    protected void doDelete(Session session) {
+        String sessionId = session.getId().toString();
+        String upmsType = ObjectUtils.toString(session.getAttribute(UpmsConstant.UPMS_TYPE));
+        if ("client".equals(upmsType)) {
+            // 删除局部会话和同一code注册的局部会话
+            String code = RedisUtil.get(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId);
+            Jedis jedis = RedisUtil.getJedis();
+            jedis.del(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId);
+            jedis.srem(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code, sessionId);
+            jedis.close();
+        }
+        if ("server".equals(upmsType)) {
+            // 当前全局会话code
+            String code = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId);
+            // 清除全局会话
+            RedisUtil.remove(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId);
+            // 清除code校验值
+            RedisUtil.remove(ZHENG_UPMS_SERVER_CODE + "_" + code);
+            // 清除所有局部会话
+            Jedis jedis = RedisUtil.getJedis();
+            Set<String> clientSessionIds = jedis.smembers(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code);
+            for (String clientSessionId : clientSessionIds) {
+                jedis.del(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + clientSessionId);
+                jedis.srem(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code, clientSessionId);
+            }
+            _log.debug("当前code={},对应的注册系统个数:{}个", code, jedis.scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code));
+            jedis.close();
+            // 维护会话id列表,提供会话分页管理
+            RedisUtil.lrem(ZHENG_UPMS_SERVER_SESSION_IDS, 1, sessionId);
+        }
+        // 删除session
+        RedisUtil.remove(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
+        _log.info("doUpdate >>>>> sessionId={}", sessionId);
+    }
+
+    /**
+     * 获取会话列表
+     * @param offset
+     * @param limit
+     * @return
+     */
+    public Map getActiveSessions(int offset, int limit) {
+        Map sessions = new HashMap();
+        Jedis jedis = RedisUtil.getJedis();
+        // 获取在线会话总数
+        long total = jedis.llen(ZHENG_UPMS_SERVER_SESSION_IDS);
+        // 获取当前页会话详情
+        List<String> ids = jedis.lrange(ZHENG_UPMS_SERVER_SESSION_IDS, offset, (offset + limit - 1));
+        List<Session> rows = new ArrayList<>();
+        for (String id : ids) {
+            String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + id);
+             rows.add(SerializableUtil.deserialize(session));
+        }
+        jedis.close();
+        sessions.put("total", total);
+        sessions.put("rows", rows);
+        return sessions;
+    }
+
+    /**
+     * 强制退出
+     * @param ids
+     * @return
+     */
+    public int forceout(String ids) {
+        String[] sessionIds = ids.split(",");
+        for (String sessionId : sessionIds) {
+            // 会话增加强制退出属性标识,当此会话访问系统时,判断有该标识,则退出登录
+            String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
+            UpmsSession upmsSession = (UpmsSession) SerializableUtil.deserialize(session);
+            upmsSession.setStatus(UpmsSession.OnlineStatus.force_logout);
+            upmsSession.setAttribute("FORCE_LOGOUT", "FORCE_LOGOUT");
+            RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId, SerializableUtil.serialize(upmsSession), (int) upmsSession.getTimeout() / 1000);
+        }
+        return sessionIds.length;
+    }
+
+    /**
+     * 更改在线状态
+     *
+     * @param sessionId
+     * @param onlineStatus
+     */
+    public void updateStatus(Serializable sessionId, UpmsSession.OnlineStatus onlineStatus) {
+        UpmsSession session = (UpmsSession) doReadSession(sessionId);
+        if (null == session) {
+            return;
+        }
+        session.setStatus(onlineStatus);
+        RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId(), SerializableUtil.serialize(session), (int) session.getTimeout() / 1000);
+    }
+
+}

+ 3 - 3
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/UpmsSessionFactory.java → zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/shiro/session/UpmsSessionFactory.java

@@ -1,4 +1,4 @@
-package com.zheng.upms.client.shiro;
+package com.zheng.upms.client.shiro.session;
 
 import org.apache.shiro.session.Session;
 import org.apache.shiro.session.mgt.SessionContext;
@@ -16,10 +16,10 @@ public class UpmsSessionFactory implements SessionFactory {
     @Override
     public Session createSession(SessionContext sessionContext) {
         UpmsSession session = new UpmsSession();
-        if (sessionContext != null && sessionContext instanceof WebSessionContext) {
+        if (null != sessionContext && sessionContext instanceof WebSessionContext) {
             WebSessionContext webSessionContext = (WebSessionContext) sessionContext;
             HttpServletRequest request = (HttpServletRequest) webSessionContext.getServletRequest();
-            if (request != null) {
+            if (null != request) {
                 session.setHost(request.getRemoteAddr());
                 session.setUserAgent(request.getHeader("User-Agent"));
             }

+ 3 - 3
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/util/RequestParameterUtil.java

@@ -12,16 +12,16 @@ import java.util.Map;
 public class RequestParameterUtil {
 
     /**
-     * 移除url中的token参数
+     * 移除url中的code参数
      * @param request
      * @return
      */
-    public static String getParameterWithOutToken(HttpServletRequest request) {
+    public static String getParameterWithOutCode(HttpServletRequest request) {
         StringBuffer backUrl = request.getRequestURL();
         String params = "";
         Map<String, String[]> parameterMap = request.getParameterMap();
         for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
-            if (!entry.getKey().equals("token")) {
+            if (!entry.getKey().equals("code")) {
                 if (params.equals("")) {
                     params = entry.getKey() + "=" + entry.getValue()[0];
                 } else {

+ 45 - 0
zheng-upms/zheng-upms-client/src/main/java/com/zheng/upms/client/util/SerializableUtil.java

@@ -0,0 +1,45 @@
+package com.zheng.upms.client.util;
+
+import com.alibaba.dubbo.common.utils.StringUtils;
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.session.Session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * session序列化工具
+ * Created by shuzheng on 2017/3/12.
+ */
+public class SerializableUtil {
+
+    public static String serialize(Session session) {
+        if (null == session) {
+            return null;
+        }
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos.writeObject(session);
+            return Base64.encodeToString(bos.toByteArray());
+        } catch (Exception e) {
+            throw new RuntimeException("serialize session error", e);
+        }
+    }
+
+    public static Session deserialize(String sessionStr) {
+        if (StringUtils.isBlank(sessionStr)) {
+            return null;
+        }
+        try {
+            ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(sessionStr));
+            ObjectInputStream ois = new ObjectInputStream(bis);
+            return (Session) ois.readObject();
+        } catch (Exception e) {
+            throw new RuntimeException("deserialize session error", e);
+        }
+    }
+
+}

+ 26 - 30
zheng-upms/zheng-upms-client/src/main/resources/applicationContext-shiro.xml

@@ -1,17 +1,25 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
-	    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
+	    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
     <description>zheng-upms</description>
 
+    <context:property-placeholder location="classpath*:zheng-upms-shiro-*.properties"/>
+
     <!-- Shiro的Web过滤器 -->
     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
         <property name="securityManager" ref="securityManager"/>
-        <property name="loginUrl" value="http://upms.zhangshuzheng.cn:1111/sso/login"/>
-        <property name="successUrl" value="/manage"/>
-        <property name="unauthorizedUrl" value="/403"/>
+        <property name="loginUrl" value="${upms.loginUrl}"/>
+        <property name="successUrl" value="${upms.successUrl}"/>
+        <property name="unauthorizedUrl" value="${upms.unauthorizedUrl}"/>
+        <property name="filters">
+            <util:map>
+                <entry key="authc" value-ref="upmsAuthenticationFilter"/>
+            </util:map>
+        </property>
         <property name="filterChainDefinitions">
             <value>
                 /manage/** = upmsSessionForceLogout,authc
@@ -24,8 +32,11 @@
         </property>
     </bean>
 
+    <!-- 重写authc过滤器 -->
+    <bean id="upmsAuthenticationFilter" class="com.zheng.upms.client.shiro.filter.UpmsAuthenticationFilter"/>
+
     <!-- 强制退出会话过滤器 -->
-    <bean id="upmsSessionForceLogout" class="com.zheng.upms.client.shiro.UpmsSessionForceLogoutFilter"/>
+    <bean id="upmsSessionForceLogout" class="com.zheng.upms.client.shiro.filter.UpmsSessionForceLogoutFilter"/>
 
     <!-- 安全管理器 -->
     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
@@ -34,7 +45,6 @@
         </property>
         <property name="sessionManager" ref="sessionManager"/>
         <property name="rememberMeManager" ref="rememberMeManager"/>
-        <!--<property name="cacheManager" ref="cacheManager"/>-->
     </bean>
 
     <!-- realm实现,继承自AuthorizingRealm -->
@@ -42,33 +52,21 @@
 
     <!-- 会话管理器 -->
     <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
-        <!-- 全局session超时时间,半小时(单位毫秒) -->
-        <property name="globalSessionTimeout" value="1800000"/>
-        <!-- 删除无效的session,默认为true -->
-        <property name="deleteInvalidSessions" value="true"/>
-        <!-- 是否开启session过期检测,默认为true -->
-        <property name="sessionValidationSchedulerEnabled" value="true"/>
-        <!-- session检测调度器 -->
-        <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
+        <!-- 全局session超时时间 -->
+        <property name="globalSessionTimeout" value="${upms.session.timeout}"/>
         <!-- sessionDAO -->
         <property name="sessionDAO" ref="sessionDAO"/>
         <property name="sessionIdCookieEnabled" value="true"/>
         <property name="sessionIdCookie" ref="sessionIdCookie"/>
+        <property name="sessionValidationSchedulerEnabled" value="false"/>
         <property name="sessionListeners">
             <list><ref bean="sessionListener"/></list>
         </property>
         <property name="sessionFactory" ref="sessionFactory"/>
     </bean>
 
-    <!-- 会话验证调度器 -->
-    <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
-        <!-- 相隔多久检查一次session的有效性,半小时(单位毫秒) -->
-        <property name="sessionValidationInterval" value="1800000"/>
-        <property name="sessionManager" ref="sessionManager"/>
-    </bean>
-
     <!-- 会话DAO,可重写,持久化session -->
-    <bean id="sessionDAO" class="com.zheng.upms.client.shiro.UpmsSessionDao"/>
+    <bean id="sessionDAO" class="com.zheng.upms.client.shiro.session.UpmsSessionDao"/>
 
     <!-- 会话Cookie模板 -->
     <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
@@ -77,14 +75,14 @@
         <!-- 设置Cookie的过期时间,秒为单位,默认-1表示关闭浏览器时过期Cookie -->
         <property name="maxAge" value="-1"/>
         <!-- Cookie名称 -->
-        <property name="name" value="zheng-upms-server-session-id"/>
+        <property name="name" value="${upms.session.id}"/>
     </bean>
 
     <!-- 会话监听器 -->
-    <bean id="sessionListener" class="com.zheng.upms.client.shiro.UpmsSessionListener"/>
+    <bean id="sessionListener" class="com.zheng.upms.client.shiro.listener.UpmsSessionListener"/>
 
     <!-- session工厂 -->
-    <bean id="sessionFactory" class="com.zheng.upms.client.shiro.UpmsSessionFactory"/>
+    <bean id="sessionFactory" class="com.zheng.upms.client.shiro.session.UpmsSessionFactory"/>
 
     <!-- rememberMe管理器 -->
     <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
@@ -98,12 +96,10 @@
         <constructor-arg value="rememberMe"/>
         <!-- 不会暴露给客户端 -->
         <property name="httpOnly" value="true"/>
-        <!-- 记住我cookie生效时间30天 -->
-        <property name="maxAge" value="2592000"/>
+        <!-- 记住我cookie生效时间 -->
+        <property name="maxAge" value="${upms.rememberMe.timeout}"/>
     </bean>
 
-    <!-- cacheManager -->
-
     <!-- 设置SecurityUtils,相当于调用SecurityUtils.setSecurityManager(securityManager) -->
     <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
         <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>

+ 2 - 0
zheng-upms/zheng-upms-common/src/main/java/com/zheng/upms/common/constant/UpmsConstant.java

@@ -8,4 +8,6 @@ import com.zheng.common.base.BaseConstants;
  */
 public class UpmsConstant extends BaseConstants {
 
+    public static final String UPMS_TYPE = "upms.type";
+
 }

+ 138 - 146
zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/controller/SSOController.java

@@ -2,8 +2,8 @@ package com.zheng.upms.server.controller;
 
 import com.zheng.common.base.BaseController;
 import com.zheng.common.util.RedisUtil;
-import com.zheng.upms.client.shiro.UpmsSession;
-import com.zheng.upms.client.shiro.UpmsSessionDao;
+import com.zheng.upms.client.shiro.session.UpmsSession;
+import com.zheng.upms.client.shiro.session.UpmsSessionDao;
 import com.zheng.upms.common.constant.UpmsResult;
 import com.zheng.upms.common.constant.UpmsResultConstant;
 import com.zheng.upms.dao.model.UpmsSystemExample;
@@ -18,9 +18,8 @@ import org.apache.shiro.authc.IncorrectCredentialsException;
 import org.apache.shiro.authc.LockedAccountException;
 import org.apache.shiro.authc.UnknownAccountException;
 import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.session.Session;
 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,12 +28,10 @@ import org.springframework.ui.ModelMap;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.ResponseBody;
-import redis.clients.jedis.Jedis;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.net.URLEncoder;
-import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -46,153 +43,148 @@ import java.util.UUID;
 @Api(value = "单点登录管理", description = "单点登录管理")
 public class SSOController extends BaseController {
 
-	private final static Logger _log = LoggerFactory.getLogger(SSOController.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";
+    private final static Logger _log = LoggerFactory.getLogger(SSOController.class);
+    // 全局会话key
+    private final static String ZHENG_UPMS_SERVER_SESSION_ID = "zheng-upms-server-session-id";
+    // 全局会话key列表
+    private final static String ZHENG_UPMS_SERVER_SESSION_IDS = "zheng-upms-server-session-ids";
+    // code key
+    private final static String ZHENG_UPMS_SERVER_CODE = "zheng-upms-server-code";
 
-	@Autowired
-	UpmsSystemService upmsSystemService;
+    @Autowired
+    UpmsSystemService upmsSystemService;
 
-	@Autowired
-	UpmsUserService upmsUserService;
+    @Autowired
+    UpmsUserService upmsUserService;
 
-	@Autowired
-	UpmsSessionDao upmsSessionDao;
+    @Autowired
+    UpmsSessionDao upmsSessionDao;
 
-	@ApiOperation(value = "认证中心首页")
-	@RequestMapping(value = "/index", method = RequestMethod.GET)
-	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 (0 == count) {
-			throw new RuntimeException(String.format("未注册的系统:%s", system_name));
-		}
-		return "redirect:/sso/login?backurl=" + URLEncoder.encode(backurl, "utf-8");
-	}
+    @ApiOperation(value = "认证中心首页")
+    @RequestMapping(value = "/index", method = RequestMethod.GET)
+    public String index(HttpServletRequest request) throws Exception {
+        String appid = request.getParameter("appid");
+        String backurl = request.getParameter("backurl");
+        if (StringUtils.isBlank(appid)) {
+            throw new RuntimeException("无效访问!");
+        }
+        // 判断请求认证系统是否注册
+        UpmsSystemExample upmsSystemExample = new UpmsSystemExample();
+        upmsSystemExample.createCriteria()
+                .andNameEqualTo(appid);
+        int count = upmsSystemService.countByExample(upmsSystemExample);
+        if (0 == count) {
+            throw new RuntimeException(String.format("未注册的系统:%s", appid));
+        }
+        return "redirect:/sso/login?backurl=" + URLEncoder.encode(backurl, "utf-8");
+    }
 
-	@ApiOperation(value = "登录")
-	@RequestMapping(value = "/login", method = RequestMethod.GET)
-	public String login(HttpServletRequest request) {
-		String backurl = request.getParameter("backurl");
-		Subject subject = SecurityUtils.getSubject();
-		String serverSessionId = subject.getSession().getId().toString();
-		// 有回跳路径的访问判断是否已登录,如果已登录,则回跳
-		String token = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + serverSessionId);
-		// token校验值
-		if (!StringUtils.isBlank(token)) {
-			// 回跳
-			String redirectUrl = backurl;
-			if (StringUtils.isBlank(backurl)) {
-				redirectUrl = "/";
-			} else {
-				if (backurl.contains("?")) {
-					redirectUrl += "&token=" + token;
-				} else {
-					redirectUrl += "?token=" + token;
-				}
-			}
-			_log.debug("认证中心帐号通过,带token回跳:{}", redirectUrl);
-			return "redirect:" + redirectUrl;
-		}
-		return "/sso/login";
-	}
+    @ApiOperation(value = "登录")
+    @RequestMapping(value = "/login", method = RequestMethod.GET)
+    public String login(HttpServletRequest request) {
+        Subject subject = SecurityUtils.getSubject();
+        Session session = subject.getSession();
+        String serverSessionId = session.getId().toString();
+        // 判断是否已登录,如果已登录,则回跳
+        String code = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + serverSessionId);
+        // code校验值
+        if (StringUtils.isNotBlank(code)) {
+            // 回跳
+            String backurl = request.getParameter("backurl");
+            if (StringUtils.isBlank(backurl)) {
+                backurl = "/";
+            } else {
+                if (backurl.contains("?")) {
+                    backurl += "&code=" + code;
+                } else {
+                    backurl += "?code=" + code;
+                }
+            }
+            _log.debug("认证中心帐号通过,带code回跳:{}", backurl);
+            return "redirect:" + backurl;
+        }
+        return "/sso/login";
+    }
 
-	@ApiOperation(value = "登录")
-	@RequestMapping(value = "/login", method = RequestMethod.POST)
-	@ResponseBody
-	public Object login(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) {
-		String backurl = request.getParameter("backurl");
-		String username = request.getParameter("username");
-		String password = request.getParameter("password");
-		String rememberMe = request.getParameter("rememberMe");
+    @ApiOperation(value = "登录")
+    @RequestMapping(value = "/login", method = RequestMethod.POST)
+    @ResponseBody
+    public Object login(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) {
+        String username = request.getParameter("username");
+        String password = request.getParameter("password");
+        String rememberMe = request.getParameter("rememberMe");
+        if (StringUtils.isBlank(username)) {
+            return new UpmsResult(UpmsResultConstant.EMPTY_USERNAME, "帐号不能为空!");
+        }
+        if (StringUtils.isBlank(password)) {
+            return new UpmsResult(UpmsResultConstant.EMPTY_PASSWORD, "密码不能为空!");
+        }
+        Subject subject = SecurityUtils.getSubject();
+        Session session = subject.getSession();
+        String sessionId = session.getId().toString();
+        // 判断是否已登录,如果已登录,则回跳,防止重复登录
+        String hasCode = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId);
+        // code校验值
+        if (StringUtils.isBlank(hasCode)) {
+            // 使用shiro认证
+            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
+            try {
+                if (BooleanUtils.toBoolean(rememberMe)) {
+                    usernamePasswordToken.setRememberMe(true);
+                } else {
+                    usernamePasswordToken.setRememberMe(false);
+                }
+                subject.login(usernamePasswordToken);
+            } catch (UnknownAccountException e) {
+                return new UpmsResult(UpmsResultConstant.INVALID_USERNAME, "帐号不存在!");
+            } catch (IncorrectCredentialsException e) {
+                return new UpmsResult(UpmsResultConstant.INVALID_PASSWORD, "密码错误!");
+            } catch (LockedAccountException e) {
+                return new UpmsResult(UpmsResultConstant.INVALID_ACCOUNT, "帐号已锁定!");
+            }
+            // 更新session状态
+            upmsSessionDao.updateStatus(sessionId, UpmsSession.OnlineStatus.on_line);
+            // 全局会话sessionId列表,供会话管理
+            RedisUtil.lpush(ZHENG_UPMS_SERVER_SESSION_IDS, sessionId.toString());
+            // 默认验证帐号密码正确,创建code
+            String code = UUID.randomUUID().toString();
+            // 全局会话的code
+            RedisUtil.set(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId, code, (int) subject.getSession().getTimeout() / 1000);
+            // code校验值
+            RedisUtil.set(ZHENG_UPMS_SERVER_CODE + "_" + code, code, (int) subject.getSession().getTimeout() / 1000);
+        }
+        // 回跳登录前地址
+        String backurl = request.getParameter("backurl");
+        if (StringUtils.isBlank(backurl)) {
+            return new UpmsResult(UpmsResultConstant.SUCCESS, "/");
+        } else {
+            return new UpmsResult(UpmsResultConstant.SUCCESS, backurl);
+        }
+    }
 
-		if (StringUtils.isBlank(username)) {
-			return new UpmsResult(UpmsResultConstant.EMPTY_USERNAME, "帐号不能为空!");
-		}
-		if (StringUtils.isBlank(password)) {
-			return new UpmsResult(UpmsResultConstant.EMPTY_PASSWORD, "密码不能为空!");
-		}
-		// 使用shiro认证
-		Subject subject = SecurityUtils.getSubject();
-		UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
-		try {
-			if (BooleanUtils.toBoolean(rememberMe)) {
-				usernamePasswordToken.setRememberMe(true);
-			} else {
-				usernamePasswordToken.setRememberMe(false);
-			}
-			subject.login(usernamePasswordToken);
-		} catch (UnknownAccountException e) {
-			return new UpmsResult(UpmsResultConstant.INVALID_USERNAME, "帐号不存在!");
-		} catch (IncorrectCredentialsException e) {
-			return new UpmsResult(UpmsResultConstant.INVALID_PASSWORD, "密码错误!");
-		} catch (LockedAccountException e) {
-			return new UpmsResult(UpmsResultConstant.INVALID_ACCOUNT, "帐号已锁定!");
-		}
-		// serverSessionId
-		String serverSessionId = subject.getSession().getId().toString();
-		// 更新session状态
-		upmsSessionDao.updateStatus(serverSessionId, UpmsSession.OnlineStatus.on_line);
-		// 默认验证帐号密码正确,创建token
-		String token = UUID.randomUUID().toString();
-		// 全局会话sessionId
-		RedisUtil.set(ZHENG_UPMS_SERVER_SESSION_ID + "_" + serverSessionId, token, (int) subject.getSession().getTimeout() / 1000);
-		// token校验值
-		RedisUtil.set(ZHENG_UPMS_SERVER_TOKEN + "_" + token, token, (int) subject.getSession().getTimeout() / 1000);
-		// 回跳登录前地址
-		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("?")) {
-				redirectUrl += "&token=" + token;
-			} else {
-				redirectUrl += "?token=" + token;
-			}
-			_log.debug("认证中心帐号通过,带token回跳:{}", redirectUrl);
-			return new UpmsResult(UpmsResultConstant.SUCCESS, redirectUrl);
-		}
-	}
+    @ApiOperation(value = "校验code")
+    @RequestMapping(value = "/code", method = RequestMethod.POST)
+    @ResponseBody
+    public String code(HttpServletRequest request) {
+        String codeParam = request.getParameter("code");
+        String code = RedisUtil.get(ZHENG_UPMS_SERVER_CODE + "_" + codeParam);
+        if (StringUtils.isBlank(codeParam) || !codeParam.equals(code)) {
+            return "failed";
+        }
+        return "success";
+    }
 
-	@ApiOperation(value = "校验token")
-	@RequestMapping(value = "/token", method = RequestMethod.POST)
-	@ResponseBody
-	public String token(HttpServletRequest request) {
-		String tokenParam = request.getParameter("token");
-		String token = RedisUtil.get(ZHENG_UPMS_SERVER_TOKEN + "_" + tokenParam);
-		if (StringUtils.isBlank(tokenParam) || !tokenParam.equals(token)) {
-			return "failed";
-		}
-		return "success";
-	}
-
-	@ApiOperation(value = "退出登录")
-	@RequestMapping(value = "/logout", method = RequestMethod.GET)
-	public String logout(HttpServletRequest request, HttpServletResponse response) {
-		// shiro退出登录
-		SecurityUtils.getSubject().logout();
-		// 跳回原地址
-		String redirectUrl = request.getHeader("Referer");
-		if (null == redirectUrl) {
-			redirectUrl = "/";
-		}
-		return "redirect:" + redirectUrl;
-	}
+    @ApiOperation(value = "退出登录")
+    @RequestMapping(value = "/logout", method = RequestMethod.GET)
+    public String logout(HttpServletRequest request) {
+        // shiro退出登录
+        SecurityUtils.getSubject().logout();
+        // 跳回原地址
+        String redirectUrl = request.getHeader("Referer");
+        if (null == redirectUrl) {
+            redirectUrl = "/";
+        }
+        return "redirect:" + redirectUrl;
+    }
 
 }

+ 1 - 1
zheng-upms/zheng-upms-server/src/main/java/com/zheng/upms/server/controller/manage/UpmsSessionController.java

@@ -1,7 +1,7 @@
 package com.zheng.upms.server.controller.manage;
 
 import com.zheng.common.base.BaseController;
-import com.zheng.upms.client.shiro.UpmsSessionDao;
+import com.zheng.upms.client.shiro.session.UpmsSessionDao;
 import com.zheng.upms.common.constant.UpmsResult;
 import com.zheng.upms.common.constant.UpmsResultConstant;
 import io.swagger.annotations.Api;

+ 14 - 0
zheng-upms/zheng-upms-server/src/main/resources/zheng-upms-client-shiro.properties

@@ -0,0 +1,14 @@
+# \u7EC8\u7AEF\u7C7B\u578B
+upms.type=server
+# \u7EC8\u7AEFsession\u540D\u79F0
+upms.session.id=zheng-upms-server-session-id
+# \u767B\u5F55\u5730\u5740
+upms.loginUrl=http://upms.zhangshuzheng.cn:1111/sso/login
+# \u767B\u5F55\u6210\u529F\u56DE\u8C03\u5730\u5740
+upms.successUrl=/manage/index
+# \u672A\u6388\u6743\u5730\u5740
+upms.unauthorizedUrl=/403
+# \u4F1A\u8BDD\u65F6\u957F,\u534A\u5C0F\u65F6\uFF08\u5355\u4F4D\u6BEB\u79D2\uFF09
+upms.session.timeout=1800000
+# \u8BB0\u4F4F\u5BC6\u7801\u65F6\u957F30\u5929
+upms.rememberMe.timeout=2592000