sunyj 8 лет назад
Родитель
Сommit
975b18529d

+ 45 - 10
kanban-auth/src/main/java/com/uas/kanban/controller/UserController.java

@@ -4,7 +4,9 @@ import com.alibaba.fastjson.JSONObject;
 import com.uas.kanban.annotation.NotEmpty;
 import com.uas.kanban.base.BaseController;
 import com.uas.kanban.exception.OperationException;
+import com.uas.kanban.model.RememberKey;
 import com.uas.kanban.model.User;
+import com.uas.kanban.service.RememberKeyService;
 import com.uas.kanban.service.UserService;
 import com.uas.kanban.support.SessionHelper;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -13,8 +15,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * 用户
@@ -29,6 +32,9 @@ public class UserController extends BaseController<User> {
     @Autowired
     private UserService userService;
 
+    @Autowired
+    private RememberKeyService rememberKeyService;
+
     @Autowired
     private SessionHelper sessionHelper;
 
@@ -38,23 +44,51 @@ public class UserController extends BaseController<User> {
      * @param name     名称
      * @param password 密码
      * @param request  request
-     * @return 是否成功登陆
+     * @return 登陆信息
+     * <table border=1 cellpadding=5 cellspacing=0 summary="Fields and types">
+     * <tr>
+     * <th>Key</th>
+     * <th>Value</th>
+     * </tr>
+     * <tr>
+     * <td>code</td>
+     * <td>用户 code</td>
+     * </tr>
+     * <tr>
+     * <td>name</td>
+     * <td>用户名称</td>
+     * </tr>
+     * <tr>
+     * <td>role</td>
+     * <td>用户角色</td>
+     * </tr>
+     * <tr>
+     * <td>key</td>
+     * <td>生成的 key</td>
+     * </tr>
+     * </table>
      */
     @RequestMapping("/login")
     @ResponseBody
-    public User login(@NotEmpty("name") String name, @NotEmpty("password") String password,
-                      HttpServletRequest request) {
+    public Map<String, Object> login(@NotEmpty("name") String name, @NotEmpty("password") String password,
+                                     HttpServletRequest request) {
         User user = userService.login(name, password);
         if (user != null) {
             sessionHelper.saveSession(request, user);
-            user.setPassword(null);
-            return user;
+            String userCode = user.codeNotEmpty();
+            RememberKey rememberKey = rememberKeyService.generate(userCode);
+            Map<String, Object> map = new HashMap<>();
+            map.put("code", userCode);
+            map.put("name", name);
+            map.put("role", user.getRole());
+            map.put("key", rememberKey.getKey());
+            return map;
         }
         return null;
     }
 
     /**
-     * 退出 登陆
+     * 退出登陆
      *
      * @param request request
      * @return 是否成功登陆
@@ -63,9 +97,10 @@ public class UserController extends BaseController<User> {
     @RequestMapping("/logout")
     @ResponseBody
     public boolean logout(HttpServletRequest request) throws OperationException {
-        HttpSession session = request.getSession();
-        if (session != null) {
-            sessionHelper.clearSession(session);
+        User user = sessionHelper.readSession(request);
+        if (user != null) {
+            sessionHelper.clearSession(request);
+            rememberKeyService.clearOldKey(user.codeNotEmpty());
             return true;
         }
         throw new OperationException("并非登陆状态");

+ 55 - 0
kanban-auth/src/main/java/com/uas/kanban/dao/RememberKeyDao.java

@@ -0,0 +1,55 @@
+package com.uas.kanban.dao;
+
+import com.uas.kanban.annotation.NotEmpty;
+import com.uas.kanban.base.BaseDao;
+import com.uas.kanban.model.RememberKey;
+import org.mongodb.morphia.query.Query;
+import org.springframework.stereotype.Component;
+
+/**
+ * 存储的 key 用于自动登陆
+ *
+ * @author sunyj
+ * @since 2017/11/2 18:25
+ */
+@Component
+public class RememberKeyDao extends BaseDao<RememberKey> {
+
+
+    /**
+     * 获取指定的 key
+     *
+     * @param userCode 用户 code
+     * @param key      key
+     * @return key
+     */
+    public RememberKey findByUserCodeAndKey(@NotEmpty("userCode") String userCode, @NotEmpty("key") String key) {
+        Query<RememberKey> query = createQuery();
+        query.field("userCode").equal(userCode);
+        query.field("key").equal(key);
+        return query.get();
+    }
+
+    /**
+     * 为指定用户生成 key
+     *
+     * @param userCode 用户 code
+     * @return 生成的 key
+     */
+    public RememberKey generate(@NotEmpty("userCode") String userCode) {
+        clearOldKey(userCode);
+        return save(new RememberKey(userCode));
+    }
+
+    /**
+     * 清除指定用户旧的 key
+     *
+     * @param userCode 用户 code
+     * @return 清除的 key 的数目
+     */
+    public int clearOldKey(@NotEmpty("userCode") String userCode) {
+        Query<RememberKey> query = createQuery();
+        query.field("userCode").equal(userCode);
+        return delete(query);
+    }
+}

+ 77 - 15
kanban-auth/src/main/java/com/uas/kanban/filter/SecurityInterceptor.java

@@ -1,11 +1,15 @@
 package com.uas.kanban.filter;
 
 import com.uas.kanban.annotation.NotEmpty;
+import com.uas.kanban.dao.RememberKeyDao;
+import com.uas.kanban.dao.UserDao;
 import com.uas.kanban.exception.OperationException;
+import com.uas.kanban.model.RememberKey;
 import com.uas.kanban.model.User;
 import com.uas.kanban.model.User.Role;
 import com.uas.kanban.support.SessionHelper;
 import com.uas.kanban.support.SystemSession;
+import com.uas.kanban.util.ArrayUtils;
 import com.uas.kanban.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -15,13 +19,11 @@ import org.springframework.util.AntPathMatcher;
 import org.springframework.util.PathMatcher;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
+import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 
 /**
  * 安全验证
@@ -34,6 +36,12 @@ public class SecurityInterceptor extends HandlerInterceptorAdapter {
 
     private Logger logger = LoggerFactory.getLogger(getClass());
 
+    @Autowired
+    private RememberKeyDao rememberKeyDao;
+
+    @Autowired
+    private UserDao userDao;
+
     @Autowired
     private SessionHelper sessionHelper;
 
@@ -61,24 +69,35 @@ public class SecurityInterceptor extends HandlerInterceptorAdapter {
             // session 中有登陆信息,重定向到首页
             if (user != null) {
                 response.sendRedirect("");
+                return false;
+            }
+            // 没有登陆信息,则尝试自动登陆
+            user = autoLogin(request);
+            if (user != null) {
+                response.sendRedirect("");
+                return false;
             }
             return true;
         }
 
         // session 中不存在登陆信息
         if (user == null) {
-            logger.info("No session for path: " + url + " , redirecting to page: login ...");
-            // 如果是 XMLHttpRequest ,抛出异常,否则重定向
-            if (Objects.equals("XMLHttpRequest", request.getHeader("X-Requested-With"))) {
-                throw new SecurityException("未登录");
-            }
-            String returnUrl = request.getRequestURL().toString();
-            String queryString = request.getQueryString();
-            if (!StringUtils.isEmpty(queryString)) {
-                returnUrl += "?" + queryString;
+            // 尝试自动登陆
+            user = autoLogin(request);
+            if (user == null) {
+                logger.info("No session for path: " + url + " , redirecting to page: login ...");
+                // 如果是 XMLHttpRequest ,抛出异常,否则重定向
+                if (Objects.equals("XMLHttpRequest", request.getHeader("X-Requested-With"))) {
+                    throw new SecurityException("未登录");
+                }
+                String returnUrl = request.getRequestURL().toString();
+                String queryString = request.getQueryString();
+                if (!StringUtils.isEmpty(queryString)) {
+                    returnUrl += "?" + queryString;
+                }
+                response.sendRedirect(contextPath + "/login?returnUrl=" + URLEncoder.encode(returnUrl, "UTF-8"));
+                return false;
             }
-            response.sendRedirect(contextPath + "/login?returnUrl=" + URLEncoder.encode(returnUrl, "UTF-8"));
-            return false;
         }
 
         // 只允许管理员访问
@@ -93,6 +112,49 @@ public class SecurityInterceptor extends HandlerInterceptorAdapter {
         return true;
     }
 
+    /**
+     * 自动登陆
+     *
+     * @param request 请求
+     * @return 用户信息
+     */
+    private User autoLogin(HttpServletRequest request) {
+        Cookie[] cookies = request.getCookies();
+        String userCode = null;
+        String key = null;
+        // 获取 cookies 中的用户 code 和 key
+        if (!ArrayUtils.isEmpty(cookies)) {
+            for (Cookie cookie : cookies) {
+                switch (cookie.getName()) {
+                    case "code":
+                        userCode = cookie.getValue();
+                        break;
+                    case "key":
+                        key = cookie.getValue();
+                        break;
+                }
+            }
+        }
+        // 如果 cookies 中存有用户 code 和 key
+        if (!StringUtils.isEmpty(userCode) && !StringUtils.isEmpty(key)) {
+            RememberKey rememberKey = rememberKeyDao.findByUserCodeAndKey(userCode, key);
+            if (rememberKey != null) {
+                Date lastModified = rememberKey.getLastModified();
+                Long maxInactiveInterval = rememberKey.getMaxInactiveInterval();
+                Date now = new Date();
+                // 如果 key 未过有效期,自动创建 session
+                if (now.getTime() < lastModified.getTime() + maxInactiveInterval * 1000L) {
+                    User user = userDao.findOne(userCode);
+                    if (user != null) {
+                        sessionHelper.saveSession(request, user);
+                        return user;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
     @Override
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
             throws Exception {

+ 94 - 0
kanban-auth/src/main/java/com/uas/kanban/model/RememberKey.java

@@ -0,0 +1,94 @@
+package com.uas.kanban.model;
+
+import com.uas.kanban.annotation.CollectionProperty;
+import com.uas.kanban.annotation.FieldProperty;
+import com.uas.kanban.base.BaseEntity;
+import com.uas.kanban.util.MD5Utils;
+import com.uas.kanban.util.NumberGenerator;
+import org.mongodb.morphia.annotations.Entity;
+import org.mongodb.morphia.annotations.IndexOptions;
+import org.mongodb.morphia.annotations.Indexed;
+
+/**
+ * 存储的 key 用于自动登陆
+ *
+ * @author sunyj
+ * @since 2017/11/2 18:14
+ */
+@Entity
+@CollectionProperty(simpleName = "用户 key")
+public class RememberKey extends BaseEntity {
+
+    /**
+     * 有效期(秒)
+     */
+    public static final long MAX_INACTIVE_INTERVAL = 3600 * 24 * 30;
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户 code
+     */
+    @Indexed(options = @IndexOptions(unique = true))
+    @FieldProperty(nullable = false)
+    private String userCode;
+
+    /**
+     * key
+     */
+    @FieldProperty(nullable = false)
+    private String key;
+
+    /**
+     * 有效期
+     */
+    @FieldProperty(nullable = false)
+    private Long maxInactiveInterval;
+
+    public RememberKey() {
+    }
+
+    public RememberKey(String userCode) {
+        this.userCode = userCode;
+    }
+
+    @Override
+    public void init() {
+        this.maxInactiveInterval = MAX_INACTIVE_INTERVAL;
+        this.key = MD5Utils.encode(NumberGenerator.generateId());
+        super.init();
+    }
+
+    public String getUserCode() {
+        return userCode;
+    }
+
+    public void setUserCode(String userCode) {
+        this.userCode = userCode;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public Long getMaxInactiveInterval() {
+        return maxInactiveInterval;
+    }
+
+    public void setMaxInactiveInterval(Long maxInactiveInterval) {
+        this.maxInactiveInterval = maxInactiveInterval;
+    }
+
+    @Override
+    public String toString() {
+        return "RememberKey{" +
+                "userCode='" + userCode + '\'' +
+                ", key='" + key + '\'' +
+                ", maxInactiveInterval=" + maxInactiveInterval +
+                "} " + super.toString();
+    }
+}

+ 29 - 0
kanban-auth/src/main/java/com/uas/kanban/service/RememberKeyService.java

@@ -0,0 +1,29 @@
+package com.uas.kanban.service;
+
+import com.uas.kanban.annotation.NotEmpty;
+import com.uas.kanban.model.RememberKey;
+
+/**
+ * 存储的 key 用于自动登陆
+ *
+ * @author sunyj
+ * @since 2017/11/2 19:50
+ */
+public interface RememberKeyService {
+
+    /**
+     * 为指定用户生成 key
+     *
+     * @param userCode 用户 code
+     * @return 生成的 key
+     */
+    RememberKey generate(@NotEmpty("userCode") String userCode);
+
+    /**
+     * 清除指定用户旧的 key
+     *
+     * @param userCode 用户 code
+     * @return 清除的 key 的数目
+     */
+    int clearOldKey(@NotEmpty("userCode") String userCode);
+}

+ 37 - 0
kanban-auth/src/main/java/com/uas/kanban/service/impl/RememberKeyServiceImpl.java

@@ -0,0 +1,37 @@
+package com.uas.kanban.service.impl;
+
+import com.uas.kanban.annotation.NotEmpty;
+import com.uas.kanban.dao.RememberKeyDao;
+import com.uas.kanban.dao.UserDao;
+import com.uas.kanban.model.RememberKey;
+import com.uas.kanban.service.RememberKeyService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 存储的 key 用于自动登陆
+ *
+ * @author sunyj
+ * @since 2017/11/2 19:51
+ */
+@Service
+public class RememberKeyServiceImpl implements RememberKeyService {
+
+    @Autowired
+    private RememberKeyDao rememberKeyDao;
+
+    @Autowired
+    private UserDao userDao;
+
+    @Override
+    public RememberKey generate(@NotEmpty("userCode") String userCode) {
+        userDao.checkExist(userCode);
+        return rememberKeyDao.generate(userCode);
+    }
+
+    @Override
+    public int clearOldKey(@NotEmpty("userCode") String userCode) {
+        userDao.checkExist(userCode);
+        return rememberKeyDao.clearOldKey(userCode);
+    }
+}

+ 4 - 0
kanban-auth/src/main/java/com/uas/kanban/service/impl/UserServiceImpl.java

@@ -3,6 +3,7 @@ package com.uas.kanban.service.impl;
 import com.uas.kanban.annotation.NotEmpty;
 import com.uas.kanban.base.BaseDao;
 import com.uas.kanban.base.BaseService;
+import com.uas.kanban.dao.RememberKeyDao;
 import com.uas.kanban.dao.UserDao;
 import com.uas.kanban.exception.OperationException;
 import com.uas.kanban.model.Panel;
@@ -34,6 +35,9 @@ public class UserServiceImpl extends BaseService<User> implements UserService {
     @Autowired
     private BaseDao<Panel> panelDao;
 
+    @Autowired
+    private RememberKeyDao rememberKeyDao;
+
     @Override
     public User save(@NotEmpty("json") String json) {
         User user = userDao.parse(json);

+ 4 - 12
kanban-auth/src/main/java/com/uas/kanban/support/SessionHelper.java

@@ -5,7 +5,6 @@ import com.uas.kanban.model.User;
 import org.springframework.stereotype.Component;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
 
 /**
  * session 辅助类
@@ -21,11 +20,6 @@ public class SessionHelper {
      */
     private static final String ATTRIBUTE_NAME = "user";
 
-    /**
-     * session 有效期
-     */
-    private static final int MaxInactiveInterval = 3600 * 12;
-
     /**
      * 将登录的账户信息保存到 session
      *
@@ -33,9 +27,7 @@ public class SessionHelper {
      * @param user    账户信息
      */
     public void saveSession(@NotEmpty("request") HttpServletRequest request, @NotEmpty("user") User user) {
-        HttpSession session = request.getSession();
-        session.setMaxInactiveInterval(MaxInactiveInterval);
-        session.setAttribute(ATTRIBUTE_NAME, user);
+        request.getSession().setAttribute(ATTRIBUTE_NAME, user);
         SystemSession.setUser(user);
     }
 
@@ -56,10 +48,10 @@ public class SessionHelper {
     /**
      * 清除 session 中的登陆信息
      *
-     * @param session session
+     * @param request 请求
      */
-    public void clearSession(HttpSession session) {
-        session.setAttribute(ATTRIBUTE_NAME, null);
+    public void clearSession(@NotEmpty("request") HttpServletRequest request) {
+        request.getSession().setAttribute(ATTRIBUTE_NAME, null);
         SystemSession.clear();
     }
 }

+ 56 - 0
kanban-common/src/main/java/com/uas/kanban/util/MD5Utils.java

@@ -0,0 +1,56 @@
+package com.uas.kanban.util;
+
+import java.security.MessageDigest;
+
+/**
+ * MD5 工具
+ *
+ * @author sunyj
+ * @since 2017/11/2 20:48
+ */
+public class MD5Utils {
+
+    /**
+     * 加密
+     *
+     * @param source 要加密的字符串
+     * @return 加密后的结果
+     */
+    public static String encode(String source) {
+        if (StringUtils.isEmpty(source)) {
+            return source;
+        }
+        try {
+            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
+            byte[] digest = messageDigest.digest(source.getBytes("UTF-8"));
+            return toHex(digest);
+        } catch (Throwable e) {
+            throw new IllegalStateException("加密失败", e);
+        }
+    }
+
+    /**
+     * 二进制转十六进制
+     *
+     * @param bytes 输入的二进制
+     * @return 十六进制结果
+     */
+    private static String toHex(byte[] bytes) {
+        StringBuffer stringBuffer = new StringBuffer();
+        // 把数组每一字节换成十六进制连成 MD5 字符串
+        int digital;
+        for (int i = 0; i < bytes.length; i++) {
+            digital = bytes[i];
+
+            if (digital < 0) {
+                digital += 256;
+            }
+            if (digital < 16) {
+                stringBuffer.append("0");
+            }
+            stringBuffer.append(Integer.toHexString(digital));
+        }
+        return stringBuffer.toString().toUpperCase();
+    }
+
+}

+ 2 - 0
kanban-console/src/main/webapp/resources/app/controller/login.js

@@ -44,6 +44,8 @@ Ext.define('erp.controller.login', {
 									me.setCookie('username',res.name,'/',14);
 									me.setCookie('password',values.password);
 									me.setCookie('role',res.role,'/',14);
+									me.setCookie('code',res.code,'/',14);
+                                    me.setCookie('key',res.key,'/',30);
 									
 									/**
 									 * 获取链接参数