wangliang 5 роки тому
батько
коміт
ed921ef0ab
17 змінених файлів з 410 додано та 303 видалено
  1. 1 5
      themis-backend/src/main/java/com/qmth/themis/backend/api/SysController.java
  2. 13 17
      themis-backend/src/main/java/com/qmth/themis/backend/api/TBUserController.java
  3. 1 1
      themis-backend/src/main/java/com/qmth/themis/backend/config/SwaggerConfig.java
  4. 72 58
      themis-backend/src/main/java/com/qmth/themis/backend/interceptor/AuthInterceptor.java
  5. 76 11
      themis-backend/src/main/java/com/qmth/themis/backend/util/ServletUtil.java
  6. 0 5
      themis-business/pom.xml
  7. 22 25
      themis-business/src/main/java/com/qmth/themis/business/constant/SystemConstant.java
  8. 2 3
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TBSessionServiceImpl.java
  9. 0 169
      themis-business/src/main/java/com/qmth/themis/business/util/JwtUtil.java
  10. 2 4
      themis-business/src/main/java/com/qmth/themis/business/util/SessionUtil.java
  11. 5 1
      themis-common/src/main/java/com/qmth/themis/common/enums/ExceptionResultEnum.java
  12. 1 1
      themis-common/src/main/java/com/qmth/themis/common/enums/Platform.java
  13. 1 1
      themis-common/src/main/java/com/qmth/themis/common/enums/Source.java
  14. 1 1
      themis-common/src/main/java/com/qmth/themis/common/exception/GlobalDefultExceptionHandler.java
  15. 195 0
      themis-common/src/main/java/com/qmth/themis/common/signature/SignatureInfo.java
  16. 17 0
      themis-common/src/main/java/com/qmth/themis/common/signature/SignatureType.java
  17. 1 1
      themis-exam/src/main/java/com/qmth/themis/exam/config/SwaggerConfig.java

+ 1 - 5
themis-backend/src/main/java/com/qmth/themis/backend/api/SysController.java

@@ -7,8 +7,6 @@ import com.qmth.themis.business.entity.TBPrivilege;
 import com.qmth.themis.business.entity.TBUser;
 import com.qmth.themis.business.enums.RoleEnum;
 import com.qmth.themis.business.service.TBPrivilegeService;
-import com.qmth.themis.business.util.JwtUtil;
-import com.qmth.themis.business.util.RedisUtil;
 import com.qmth.themis.common.enums.ExceptionResultEnum;
 import com.qmth.themis.common.exception.BusinessException;
 import com.qmth.themis.common.util.Result;
@@ -45,9 +43,7 @@ public class SysController {
     @RequestMapping(value = "/getMenu", method = RequestMethod.GET)
     @ApiResponses({@ApiResponse(code = 200, message = "菜单信息", response = TBPrivilege.class)})
     public Result getMenu(HttpServletRequest request) {
-        String token = ServletUtil.getRequestToken(request);
-        String userId = JwtUtil.getClaim(token, SystemConstant.JWT_USERID);
-        TBUser tbUser = (TBUser) RedisUtil.getUser(Long.parseLong(userId));
+        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount(request);
         if (Objects.isNull(tbUser)) {
             throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
         }

+ 13 - 17
themis-backend/src/main/java/com/qmth/themis/backend/api/TBUserController.java

@@ -7,11 +7,9 @@ import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.AuthDto;
 import com.qmth.themis.business.entity.TBSession;
 import com.qmth.themis.business.entity.TBUser;
-import com.qmth.themis.business.enums.RoleEnum;
 import com.qmth.themis.business.service.EhcacheService;
 import com.qmth.themis.business.service.TBSessionService;
 import com.qmth.themis.business.service.TBUserService;
-import com.qmth.themis.business.util.JwtUtil;
 import com.qmth.themis.business.util.RedisUtil;
 import com.qmth.themis.business.util.SessionUtil;
 import com.qmth.themis.common.contanst.Constants;
@@ -22,6 +20,7 @@ import com.qmth.themis.common.util.AesUtil;
 import com.qmth.themis.common.util.Result;
 import com.qmth.themis.common.util.ResultUtil;
 import io.swagger.annotations.*;
+import org.apache.commons.lang3.RandomStringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -34,6 +33,7 @@ import javax.servlet.http.HttpServletRequest;
 import java.io.UnsupportedEncodingException;
 import java.security.NoSuchAlgorithmException;
 import java.security.spec.InvalidKeySpecException;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -95,17 +95,20 @@ public class TBUserController {
         //添加用户鉴权缓存
         AuthDto authDto = ehcacheService.addAccountCache(user);
         //生成token
-        String token = JwtUtil.sign(user.getId(), platform, deviceId, authDto.getRoleEnum());
+        String token = RandomStringUtils.randomAlphanumeric(32);
         //添加用户缓存
         RedisUtil.setUser(user.getId(), user);
         //添加用户会话缓存
         String sessionId = SessionUtil.digest(user.getId(), authDto.getRoleEnum().name(), platform.getSource());
-        TBSession tbSession = tbSessionService.saveSessionInfo(sessionId, user.getId(), authDto.getRoleEnum().name(), platform.name(), platform.getSource(), deviceId, token, request.getLocalAddr());
+
+        Date expire = SystemConstant.getExpireTime(platform);
+        TBSession tbSession = tbSessionService.saveSessionInfo(sessionId, user.getId(), authDto.getRoleEnum().name(), platform.name(), platform.getSource(), deviceId, token, request.getLocalAddr(), expire);
         RedisUtil.setUserSession(sessionId, tbSession);
 
         Map<String, Object> map = new HashMap<>();
-        map.put(SystemConstant.TOKEN, token);
-        map.put(SystemConstant.USER, user);
+        map.put(SystemConstant.ACCESS_TOKEN, token);
+        map.put(SystemConstant.ACCOUNT, user);
+        map.put(SystemConstant.SESSION_ID, sessionId);
         return ResultUtil.ok(map);
     }
 
@@ -119,18 +122,11 @@ public class TBUserController {
     @RequestMapping(value = "/logout", method = RequestMethod.GET)
     @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
     public Result logout(HttpServletRequest request) throws NoSuchAlgorithmException {
-        String token = ServletUtil.getRequestToken(request);
-        if (Objects.isNull(token) || Objects.equals(token, "")) {
-            throw new BusinessException(ExceptionResultEnum.TOKEN_INVALID);
-        }
-        Platform platform = Platform.valueOf(ServletUtil.getRequestPlatform(request));
-        if (Objects.isNull(platform) || Objects.equals(platform, "")) {
-            throw new BusinessException(ExceptionResultEnum.PLATFORM_INVALID);
+        TBSession tbSession = (TBSession) ServletUtil.getRequestSession(request);
+        if (Objects.isNull(tbSession)) {
+            throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
         }
-        String userId = JwtUtil.getClaim(token, SystemConstant.JWT_USERID);
-        String role = JwtUtil.getClaim(token, SystemConstant.ROLE);
-        String sessionId = SessionUtil.digest(Long.parseLong(userId), RoleEnum.valueOf(role), platform.getSource());
-        RedisUtil.deleteUserSession(sessionId);
+        RedisUtil.deleteUserSession(tbSession.getId());
         return ResultUtil.ok(JSONObject.parseObject(SystemConstant.SUCCESS));
     }
 }

+ 1 - 1
themis-backend/src/main/java/com/qmth/themis/backend/config/SwaggerConfig.java

@@ -32,7 +32,7 @@ public class SwaggerConfig {
     public Docket createRestApi() {
         ParameterBuilder tokenPar = new ParameterBuilder();
         List<Parameter> pars = new ArrayList<>();
-        tokenPar.name(SystemConstant.TOKEN).description(SystemConstant.TOKEN).modelRef(new ModelRef("string")).parameterType("header").required(false).build();
+        tokenPar.name(SystemConstant.ACCESS_TOKEN).description(SystemConstant.ACCESS_TOKEN).modelRef(new ModelRef("string")).parameterType("header").required(false).build();
         pars.add(tokenPar.build());
 
         return new Docket(DocumentationType.SWAGGER_2)

+ 72 - 58
themis-backend/src/main/java/com/qmth/themis/backend/interceptor/AuthInterceptor.java

@@ -6,16 +6,16 @@ import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.AuthDto;
 import com.qmth.themis.business.entity.TBSession;
 import com.qmth.themis.business.entity.TBUser;
-import com.qmth.themis.business.enums.RoleEnum;
 import com.qmth.themis.business.service.EhcacheService;
 import com.qmth.themis.business.service.TBUserService;
 import com.qmth.themis.business.util.EhcacheUtil;
-import com.qmth.themis.business.util.JwtUtil;
 import com.qmth.themis.business.util.RedisUtil;
-import com.qmth.themis.business.util.SessionUtil;
+import com.qmth.themis.common.contanst.Constants;
 import com.qmth.themis.common.enums.ExceptionResultEnum;
 import com.qmth.themis.common.enums.Platform;
 import com.qmth.themis.common.exception.BusinessException;
+import com.qmth.themis.common.signature.SignatureInfo;
+import com.qmth.themis.common.signature.SignatureType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.web.servlet.HandlerInterceptor;
@@ -52,71 +52,80 @@ public class AuthInterceptor implements HandlerInterceptor {
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
         log.info("HandlerInterceptor preHandle is come in");
         String url = request.getServletPath();
-        if (url.equalsIgnoreCase("/error")) {
-            if (response.getStatus() == cn.hutool.http.HttpStatus.HTTP_NOT_FOUND) {
-                throw new BusinessException(ExceptionResultEnum.NOT_FOUND);
-            } else {
-                throw new BusinessException(ExceptionResultEnum.EXCEPTION_ERROR);
-            }
-        }
-        String token = ServletUtil.getRequestToken(request);
+        String method = request.getMethod();
         Platform platform = Platform.valueOf(ServletUtil.getRequestPlatform(request));
         String deviceId = ServletUtil.getRequestDeviceId(request);
-        if (Objects.isNull(token) || Objects.equals(token, "")) {
-            throw new BusinessException(ExceptionResultEnum.TOKEN_INVALID);
-        }
         if (Objects.isNull(platform) || Objects.equals(platform, "")) {
             throw new BusinessException(ExceptionResultEnum.PLATFORM_INVALID);
         }
         if (Objects.isNull(deviceId) || Objects.equals(deviceId, "")) {
             throw new BusinessException(ExceptionResultEnum.DEVICE_ID_INVALID);
         }
-        Long userId = Long.parseLong(JwtUtil.getClaim(token, SystemConstant.JWT_USERID));
-        String role = JwtUtil.getClaim(token, SystemConstant.ROLE);
-        //首先验证token是否匹配
-        if (!JwtUtil.verify(token, userId, platform, deviceId, RoleEnum.valueOf(role))) {
-            throw new BusinessException(ExceptionResultEnum.TOKEN_NO);
-        }
-        //系统公用接口不拦截
-        List<String> sysUrls = dictionaryConfig.systemUrlDomain().getUrls();
-        int sysCount = (int) sysUrls.stream().filter(s -> {
-            return s.equalsIgnoreCase(url);
-        }).count();
-        if (sysCount > 0) {
-            return true;
-        }
-        //验证token是否有效
-        String sessionId = SessionUtil.digest(userId, RoleEnum.valueOf(role), platform.getSource());
-        TBSession tbSession = (TBSession) RedisUtil.getUserSession(sessionId);
-        if (Objects.isNull(tbSession)) {
-            throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
-        } else {
-            if (!Objects.equals(token, tbSession.getAccessToken())) {
-                throw new BusinessException(ExceptionResultEnum.TOKEN_NO);
-            }
-            Date expireTime = tbSession.getExpireTime();
-            //手机端的token时长为一个月,所以会出现缓存没有的情况
-            TBUser tbUser = (TBUser) RedisUtil.getUser(userId);
-            if (Objects.isNull(tbUser)) {
-                tbUser = tbUserService.getById(userId);
-                RedisUtil.setUser(tbUser.getId(), tbUser);
-            }
-            //还剩5分钟刷新会话缓存
-            if (Objects.nonNull(expireTime) && (expireTime.getTime() - System.currentTimeMillis()) <= SystemConstant.REFRESH_EXPIRE_TIME) {
-                RedisUtil.refreshUserSession(sessionId);
+        if (url.equalsIgnoreCase("/error")) {
+            if (response.getStatus() == cn.hutool.http.HttpStatus.HTTP_NOT_FOUND) {
+                throw new BusinessException(ExceptionResultEnum.NOT_FOUND);
+            } else {
+                throw new BusinessException(ExceptionResultEnum.EXCEPTION_ERROR);
             }
         }
-        AuthDto authDto = (AuthDto) EhcacheUtil.get(SystemConstant.AUTH_CACHE, userId);
-        if (Objects.isNull(authDto)) {
-            authDto = ehcacheService.addAccountCache(userId);
-        }
-        //验证权限
-        Set<String> urls = authDto.getUrls();
-        int count = (int) urls.stream().filter(s -> {
-            return s.equalsIgnoreCase(url);
-        }).count();
-        if (count == 0) {
-            throw new BusinessException(ExceptionResultEnum.UN_AUTHORIZATION);
+        Long userId = null;
+        Long timestamp = Long.parseLong(ServletUtil.getRequestTime(request));
+        if (!expire(timestamp.longValue())) {
+            final SignatureInfo info = SignatureInfo
+                    .parse(method, url, timestamp, ServletUtil.getRequestAuthorization(request));
+            if (Objects.nonNull(info) && info.getType() == SignatureType.TOKEN) {
+                String sessionId = info.getInvoker();
+                TBSession tbSession = (TBSession) RedisUtil.getUserSession(sessionId);
+                if (Objects.isNull(tbSession)) {
+                    throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
+                } else {
+                    if (info.validate(tbSession.getAccessToken()) && info.getTimestamp() < tbSession.getExpireTime().getTime()
+                            && platform.name().equalsIgnoreCase(tbSession.getPlatform()) && Objects.equals(deviceId, tbSession.getDeviceId())) {
+                        userId = Long.parseLong(tbSession.getIdentity());
+                        Date expireTime = tbSession.getExpireTime();
+                        //手机端的token时长为一个月,所以会出现缓存没有的情况
+                        TBUser tbUser = (TBUser) RedisUtil.getUser(userId);
+                        if (Objects.isNull(tbUser)) {
+                            tbUser = tbUserService.getById(userId);
+                            RedisUtil.setUser(tbUser.getId(), tbUser);
+                        }
+                        //还剩5分钟刷新会话缓存
+                        if (Objects.nonNull(expireTime) && (expireTime.getTime() - System.currentTimeMillis()) <= SystemConstant.REFRESH_EXPIRE_TIME) {
+                            RedisUtil.refreshUserSession(sessionId);
+                        }
+
+                        request.setAttribute(SystemConstant.SESSION, tbSession);
+                        request.setAttribute(SystemConstant.ACCOUNT, tbUser);
+                        //系统公用接口不拦截
+                        List<String> sysUrls = dictionaryConfig.systemUrlDomain().getUrls();
+                        int sysCount = (int) sysUrls.stream().filter(s -> {
+                            return s.equalsIgnoreCase(url);
+                        }).count();
+                        if (sysCount > 0) {
+                            return true;
+                        }
+
+                        //验证权限
+                        AuthDto authDto = (AuthDto) EhcacheUtil.get(SystemConstant.AUTH_CACHE, userId);
+                        if (Objects.isNull(authDto)) {
+                            authDto = ehcacheService.addAccountCache(userId);
+                        }
+                        Set<String> urls = authDto.getUrls();
+                        int count = (int) urls.stream().filter(s -> {
+                            return s.equalsIgnoreCase(url);
+                        }).count();
+                        if (count == 0) {
+                            throw new BusinessException(ExceptionResultEnum.UN_AUTHORIZATION);
+                        }
+                    } else {
+                        throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
+                    }
+                }
+            } else {
+                throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
+            }
+        } else {
+            throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
         }
         return true;
     }
@@ -133,4 +142,9 @@ public class AuthInterceptor implements HandlerInterceptor {
                                 HttpServletResponse response,
                                 Object o, Exception e) throws Exception {
     }
+
+    public boolean expire(long timestamp) {
+        long diff = (System.currentTimeMillis() - timestamp) / 1000;
+        return diff < -1 * Constants.SIGNATURE_AHEAD_SECONDS || diff > Constants.SIGNATURE_EXPIRE_SECONDS;
+    }
 }

+ 76 - 11
themis-backend/src/main/java/com/qmth/themis/backend/util/ServletUtil.java

@@ -2,6 +2,7 @@ package com.qmth.themis.backend.util;
 
 import com.alibaba.fastjson.JSONObject;
 import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.common.contanst.Constants;
 import com.qmth.themis.common.util.Result;
 import com.qmth.themis.common.util.ResultUtil;
 
@@ -36,17 +37,17 @@ public class ServletUtil {
     }
 
     /**
-     * 获取请求的token
+     * 获取请求的accessToken
      *
      * @param httpRequest
      * @return
      */
     public static String getRequestToken(HttpServletRequest httpRequest) {
         // 从header中获取token
-        String token = httpRequest.getHeader(SystemConstant.TOKEN);
+        String token = httpRequest.getHeader(SystemConstant.ACCESS_TOKEN);
         // 如果header中不存在token,则从参数中获取token
         if (Objects.isNull(token)) {
-            token = httpRequest.getParameter(SystemConstant.TOKEN);
+            token = httpRequest.getParameter(SystemConstant.ACCESS_TOKEN);
         }
         return token;
     }
@@ -59,10 +60,10 @@ public class ServletUtil {
      */
     public static String getRequestPlatform(HttpServletRequest httpRequest) {
         // 从header中获取platform
-        String platform = httpRequest.getHeader(SystemConstant.PLATFORM);
+        String platform = httpRequest.getHeader(Constants.HEADER_PLATFORM);
         // 如果header中不存在platform,则从参数中获取platform
         if (Objects.isNull(platform)) {
-            platform = httpRequest.getParameter(SystemConstant.PLATFORM);
+            platform = httpRequest.getParameter(Constants.HEADER_PLATFORM);
         }
         return platform;
     }
@@ -74,12 +75,76 @@ public class ServletUtil {
      * @return
      */
     public static String getRequestDeviceId(HttpServletRequest httpRequest) {
-        // 从header中获取platform
-        String platform = httpRequest.getHeader(SystemConstant.DEVICE_ID);
-        // 如果header中不存在platform,则从参数中获取platform
-        if (Objects.isNull(platform)) {
-            platform = httpRequest.getParameter(SystemConstant.DEVICE_ID);
+        // 从header中获取deviceId
+        String deviceId = httpRequest.getHeader(Constants.HEADER_DEVICE_ID);
+        // 如果header中不存在deviceId,则从参数中获取deviceId
+        if (Objects.isNull(deviceId)) {
+            deviceId = httpRequest.getParameter(Constants.HEADER_DEVICE_ID);
         }
-        return platform;
+        return deviceId;
+    }
+
+    /**
+     * 获取请求的time
+     *
+     * @param httpRequest
+     * @return
+     */
+    public static String getRequestTime(HttpServletRequest httpRequest) {
+        // 从header中获取time
+        String time = httpRequest.getHeader(Constants.HEADER_TIME);
+        // 如果header中不存在time,则从参数中获取time
+        if (Objects.isNull(time)) {
+            time = httpRequest.getParameter(Constants.HEADER_TIME);
+        }
+        return time;
+    }
+
+    /**
+     * 获取请求的Authorization
+     *
+     * @param httpRequest
+     * @return
+     */
+    public static String getRequestAuthorization(HttpServletRequest httpRequest) {
+        // 从header中获取authorization
+        String authorization = httpRequest.getHeader(Constants.HEADER_AUTHORIZATION);
+        // 如果header中不存在authorization,则从参数中获取authorization
+        if (Objects.isNull(authorization)) {
+            authorization = httpRequest.getParameter(Constants.HEADER_AUTHORIZATION);
+        }
+        return authorization;
+    }
+
+    /**
+     * 获取请求的Session
+     *
+     * @param httpRequest
+     * @return
+     */
+    public static Object getRequestSession(HttpServletRequest httpRequest) {
+        // 从header中获取session
+        Object session = httpRequest.getHeader(SystemConstant.SESSION);
+        // 如果header中不存在session,则从参数中获取session
+        if (Objects.isNull(session)) {
+            session = httpRequest.getParameter(SystemConstant.SESSION);
+        }
+        return session;
+    }
+
+    /**
+     * 获取请求的Account
+     *
+     * @param httpRequest
+     * @return
+     */
+    public static Object getRequestAccount(HttpServletRequest httpRequest) {
+        // 从header中获取account
+        Object account = httpRequest.getHeader(SystemConstant.ACCOUNT);
+        // 如果header中不存在account,则从参数中获取account
+        if (Objects.isNull(account)) {
+            account = httpRequest.getParameter(SystemConstant.ACCOUNT);
+        }
+        return account;
     }
 }

+ 0 - 5
themis-business/pom.xml

@@ -87,10 +87,5 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-aop</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.auth0</groupId>
-            <artifactId>java-jwt</artifactId>
-            <version>3.4.0</version>
-        </dependency>
     </dependencies>
 </project>

+ 22 - 25
themis-business/src/main/java/com/qmth/themis/business/constant/SystemConstant.java

@@ -3,6 +3,8 @@ package com.qmth.themis.business.constant;
 import com.qmth.themis.common.enums.Platform;
 import com.qmth.themis.common.enums.Source;
 
+import java.util.Calendar;
+import java.util.Date;
 import java.util.Objects;
 
 /**
@@ -16,11 +18,8 @@ public class SystemConstant {
     /**
      * 鉴权
      */
-    public static final String TOKEN = "token";
-    public static final String PLATFORM = "platform";
-    public static final String DEVICE_ID = "deviceId";
-    public static final String ROLE = "role";
-
+    public static final String ACCESS_TOKEN = "accessToken";
+    public static final String SESSION_ID = "sessionId";
     /**
      * 阿里云oss
      */
@@ -34,20 +33,18 @@ public class SystemConstant {
      */
     public static final String SUCCESS = "{'success':true}";
     public static final String RECORDS = "records";
-    public static final String USER = "user_";
-    public static final String SESSION = "session_";
+    public static final String USER = "user:";
+    public static final String SESSION = "session:";
     public static final String LINK = "LINK";
     public static final String MENU = "MENU";
     public static final String AUTH_CACHE = "auth_cache";
     public static final String ALL_PATH = "/**";
+    public static final String ACCOUNT = "account";
+
+    public static final int WEB_SESSION_EXPIRE = 1;//过期时间1天
+    public static final int PC_SESSION_EXPIRE = 1;//过期时间1天
+    public static final int WXAPP_SESSION_EXPIRE = 30;//过期时间30天
 
-    public static final String JWT_USERID = "userId";
-    public static final String JWT_SECRET = "YuiopB0009Xn";//jwt密钥
-    public static final String JWT_CURRENT_TIME = "currentTimeMillis";
-    public static final long JWT_WEB_EXPIRE_TIME = 60L * 1440L * 1000L;//过期时间24小时
-    public static final long JWT_PC_EXPIRE_TIME = 60L * 1440L * 1000L;//过期时间24小时
-    public static final long JWT_PAD_EXPIRE_TIME = 60L * 1440L * 1000L;//过期时间24小时
-    public static final long JWT_PHONE_EXPIRE_TIME = 60L * 43200L * 1000L;//过期时间30天
     public static final long REDIS_EXPIRE_TIME = 60L * 1440L;//过期时间24小时
     public static final long REDIS_REFRESH_EXPIRE_TIME = 60L * 60L;//刷新时间1小时,理论上说24小时还不睡觉也已经快不行了
     public static final long REFRESH_EXPIRE_TIME = 60L * 5L;//过期剩余时间5分钟
@@ -58,17 +55,17 @@ public class SystemConstant {
      * @param platform
      * @return
      */
-    public static long expireTime(Platform platform) {
-        Long expireTime = null;
-        if (Objects.equals(platform.getSource(), Source.Web.name())) {
-            expireTime = SystemConstant.JWT_WEB_EXPIRE_TIME;
-        } else if (Objects.equals(platform.getSource(), Source.PC.name())) {
-            expireTime = SystemConstant.JWT_PC_EXPIRE_TIME;
-        } else if (Objects.equals(platform.getSource(), Source.Pad.name())) {
-            expireTime = SystemConstant.JWT_PAD_EXPIRE_TIME;
-        } else if (Objects.equals(platform.getSource(), Source.Phone.name())) {
-            expireTime = SystemConstant.JWT_PHONE_EXPIRE_TIME;
+    public static Date getExpireTime(Platform platform) {
+        Date now = new Date();
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(now);
+        if (Objects.equals(platform.getSource(), Source.web.name())) {
+            calendar.add(Calendar.DAY_OF_YEAR, SystemConstant.WEB_SESSION_EXPIRE);
+        } else if (Objects.equals(platform.getSource(), Source.wxapp.name())) {
+            calendar.add(Calendar.DAY_OF_YEAR, SystemConstant.WXAPP_SESSION_EXPIRE);
+        } else if (Objects.equals(platform.getSource(), Source.pc.name())) {
+            calendar.add(Calendar.DAY_OF_YEAR, SystemConstant.PC_SESSION_EXPIRE);
         }
-        return expireTime.longValue();
+        return calendar.getTime();
     }
 }

+ 2 - 3
themis-business/src/main/java/com/qmth/themis/business/service/impl/TBSessionServiceImpl.java

@@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.themis.business.dao.TBSessionMapper;
 import com.qmth.themis.business.entity.TBSession;
 import com.qmth.themis.business.service.TBSessionService;
-import com.qmth.themis.business.util.JwtUtil;
 import org.springframework.stereotype.Service;
 
 import java.util.Date;
@@ -51,14 +50,14 @@ public class TBSessionServiceImpl extends ServiceImpl<TBSessionMapper, TBSession
             tbSession.setAddress(ip);
             tbSession.setLastAccessTime(new Date());
             tbSession.setUpdateTime(new Date());
-            tbSession.setExpireTime(JwtUtil.getExpirationDateFromToken(token));
+            tbSession.setExpireTime((Date) o[8]);
         } else {
             tbSession.setDeviceId(deviceId);
             tbSession.setAccessToken(token);
             tbSession.setLastAccessIp(ip);
             tbSession.setLastAccessTime(new Date());
             tbSession.setUpdateTime(new Date());
-            tbSession.setExpireTime(JwtUtil.getExpirationDateFromToken(token));
+            tbSession.setExpireTime((Date) o[8]);
         }
         this.saveOrUpdate(tbSession);
         return tbSession;

+ 0 - 169
themis-business/src/main/java/com/qmth/themis/business/util/JwtUtil.java

@@ -1,169 +0,0 @@
-package com.qmth.themis.business.util;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.JWTVerifier;
-import com.auth0.jwt.algorithms.Algorithm;
-import com.auth0.jwt.exceptions.JWTDecodeException;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import com.qmth.themis.business.constant.SystemConstant;
-import com.qmth.themis.business.enums.RoleEnum;
-import com.qmth.themis.common.enums.ExceptionResultEnum;
-import com.qmth.themis.common.enums.Platform;
-import com.qmth.themis.common.exception.BusinessException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Date;
-
-/**
- * @Description: jwt工具类
- * @Param:
- * @return:
- * @Author: wangliang
- * @Date: 2020/4/8
- */
-public class JwtUtil {
-    private final static Logger log = LoggerFactory.getLogger(JwtUtil.class);
-
-    /**
-     * 校验token是否正确
-     *
-     * @param token
-     * @param userId
-     * @return
-     */
-    public static boolean verify(String token, Long userId) {
-        try {
-            //根据密码生成JWT效验器
-            String secret = getClaim(token, SystemConstant.JWT_USERID) + SystemConstant.JWT_SECRET;
-            Algorithm algorithm = Algorithm.HMAC256(secret);
-            JWTVerifier verifier = JWT.require(algorithm)
-                    .withClaim(SystemConstant.JWT_USERID, String.valueOf(userId))
-                    .build();
-            //效验TOKEN
-            verifier.verify(token);
-            return true;
-        } catch (Exception e) {
-            e.printStackTrace();
-            throw new BusinessException(ExceptionResultEnum.TOKEN_NO);
-        }
-    }
-
-    /**
-     * 校验token是否正确
-     *
-     * @param token
-     * @param userId
-     * @param platform
-     * @param deviceId
-     * @param roleEnum
-     * @return
-     */
-    public static boolean verify(String token, Long userId, Platform platform, String deviceId, RoleEnum roleEnum) {
-        try {
-            //根据密码生成JWT效验器
-            String secret = userId + SystemConstant.JWT_SECRET;
-            Algorithm algorithm = Algorithm.HMAC256(secret);
-            JWTVerifier verifier = JWT.require(algorithm)
-                    .withClaim(SystemConstant.JWT_USERID, String.valueOf(userId))
-                    .withClaim(SystemConstant.PLATFORM, platform.name())
-                    .withClaim(SystemConstant.DEVICE_ID, deviceId)
-                    .withClaim(SystemConstant.ROLE, roleEnum.name())
-                    .build();
-            //效验TOKEN
-            verifier.verify(token);
-            return true;
-        } catch (Exception e) {
-            e.printStackTrace();
-            throw new BusinessException(ExceptionResultEnum.TOKEN_NO);
-        }
-    }
-
-    /**
-     * 获得token中的信息无需secret解密也能获得
-     *
-     * @param token
-     * @param key
-     * @return
-     */
-    public static String getClaim(String token, String key) {
-        try {
-            DecodedJWT jwt = JWT.decode(token);
-            return jwt.getClaim(key).asString();
-        } catch (JWTDecodeException e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-    /**
-     * 生成签名
-     *
-     * @param userId
-     * @return
-     */
-    public static String sign(Long userId) {
-        try {
-            String secret = userId + SystemConstant.JWT_SECRET;
-            Long start = System.currentTimeMillis();
-            Date date = new Date(start + SystemConstant.JWT_WEB_EXPIRE_TIME);
-            Algorithm algorithm = Algorithm.HMAC256(secret);
-            // 附带username信息
-            return JWT.create()
-                    .withClaim(SystemConstant.JWT_USERID, String.valueOf(userId))
-                    .withClaim(SystemConstant.JWT_CURRENT_TIME, String.valueOf(start))
-                    .withExpiresAt(date)
-                    .sign(algorithm);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-    /**
-     * 生成签名
-     *
-     * @param userId
-     * @param platform
-     * @param deviceId
-     * @param roleEnum
-     * @return
-     */
-    public static String sign(Long userId, Platform platform, String deviceId, RoleEnum roleEnum) {
-        try {
-            String secret = userId + SystemConstant.JWT_SECRET;
-            Long start = System.currentTimeMillis();
-            Date date = new Date(start + SystemConstant.expireTime(platform));
-            Algorithm algorithm = Algorithm.HMAC256(secret);
-            // 附带username信息
-            return JWT.create()
-                    .withClaim(SystemConstant.JWT_USERID, String.valueOf(userId))
-                    .withClaim(SystemConstant.PLATFORM, platform.name())
-                    .withClaim(SystemConstant.DEVICE_ID, deviceId)
-                    .withClaim(SystemConstant.ROLE, roleEnum.name())
-                    .withClaim(SystemConstant.JWT_CURRENT_TIME, String.valueOf(start))
-                    .withExpiresAt(date)
-                    .sign(algorithm);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-
-    /**
-     * 获取jwt失效时间
-     *
-     * @param token
-     * @return
-     */
-    public static Date getExpirationDateFromToken(String token) {
-        try {
-            DecodedJWT jwt = JWT.decode(token);
-            return jwt.getExpiresAt();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-}

+ 2 - 4
themis-business/src/main/java/com/qmth/themis/business/util/SessionUtil.java

@@ -1,7 +1,5 @@
 package com.qmth.themis.business.util;
 
-import com.qmth.themis.common.util.MD5Util;
-
 import java.security.NoSuchAlgorithmException;
 import java.util.Objects;
 import java.util.StringJoiner;
@@ -25,9 +23,9 @@ public class SessionUtil {
         StringJoiner stringJoiner = new StringJoiner("");
         if (Objects.nonNull(o) && o.length > 0) {
             for (int i = 0; i < o.length; i++) {
-                stringJoiner.add(String.valueOf(o[i]));
+                stringJoiner.add(String.valueOf(o[i])).add("_");
             }
         }
-        return MD5Util.encoder(stringJoiner.toString());
+        return stringJoiner.toString().substring(0, stringJoiner.length() - 1);
     }
 }

+ 5 - 1
themis-common/src/main/java/com/qmth/themis/common/enums/ExceptionResultEnum.java

@@ -90,7 +90,11 @@ public enum ExceptionResultEnum {
 
     NOT_FOUND("404", "请求地址错误"),
 
-    UN_AUTHORIZATION("401", "没有权限");
+    UN_AUTHORIZATION("401", "没有权限"),
+
+    AUTHORIZATION_ERROR("401", "签名验证失败"),
+
+    AUTHORIZATION_SESSION_ERROR("401", "sessionId验证失败");
 
     private String code;
     private String message;

+ 1 - 1
themis-common/src/main/java/com/qmth/themis/common/enums/Platform.java

@@ -2,7 +2,7 @@ package com.qmth.themis.common.enums;
 
 public enum Platform {
 
-    SERVER("Server"), IOS("Phone"), Android("Phone"), WAP("Web"), Ipad("Pad"), Windows("PC"), Mac("PC");
+    web("web"), wap("web"), wxapp("wxapp"), win("pc");
 
     public static Platform findByName(String name) {
         if (name == null) {

+ 1 - 1
themis-common/src/main/java/com/qmth/themis/common/enums/Source.java

@@ -2,7 +2,7 @@ package com.qmth.themis.common.enums;
 
 public enum Source {
 
-    Phone, Pad, PC, Web, Server;
+    web, wxapp, pc;
 
     public static Source findByName(String name) {
         if (name == null) {

+ 1 - 1
themis-common/src/main/java/com/qmth/themis/common/exception/GlobalDefultExceptionHandler.java

@@ -36,7 +36,7 @@ public class GlobalDefultExceptionHandler {
             return ResultUtil.error(businessException.getCode(), businessException.getMessage());
         } else if (e instanceof IllegalArgumentException) {
             if (e.getMessage().contains("No enum constant com.qmth.themis.common.enums.Platform")) {
-                return ResultUtil.error(ExceptionResultEnum.EXCEPTION_ERROR.getCode(), "没有此平台");
+                return ResultUtil.error(ExceptionResultEnum.EXCEPTION_ERROR.getCode(), "暂不支持此平台");
             }
         }
         //Exception错误

+ 195 - 0
themis-common/src/main/java/com/qmth/themis/common/signature/SignatureInfo.java

@@ -0,0 +1,195 @@
+package com.qmth.themis.common.signature;
+
+import com.qmth.themis.common.util.Base64Utils;
+import com.qmth.themis.common.util.ShaUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 签名数据对象,包括构造、解析、验证的通用方法
+ */
+public class SignatureInfo {
+
+    private static final String PATTERN = "{0} {1}{2}{3}";
+
+    private static final String FIELD_JOINER = ":";
+
+    private static final String PARAM_JOINER = "&";
+
+    private static Map<String, SignatureType> typeMap = new HashMap<>();
+
+    static {
+        for (SignatureType type : SignatureType.values()) {
+            typeMap.put(type.getName(), type);
+        }
+    }
+
+    private SignatureType type;
+
+    private String method;
+
+    private String uri;
+
+    private long timestamp;
+
+    private String ciphertext;
+
+    private String invoker;
+
+    private String secret;
+
+    private SignatureInfo() {
+    }
+
+    public SignatureType getType() {
+        return type;
+    }
+
+    private void setType(SignatureType type) {
+        this.type = type;
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    private void setMethod(String method) {
+        this.method = method;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    private void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    private void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    private String getCiphertext() {
+        return ciphertext;
+    }
+
+    private void setCiphertext(String ciphertext) {
+        this.ciphertext = ciphertext;
+    }
+
+    public String getInvoker() {
+        return invoker;
+    }
+
+    private void setInvoker(String invoker) {
+        this.invoker = invoker;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    private void setSecret(String secret) {
+        this.secret = secret;
+    }
+
+    private static String encrypt(String... values) {
+        return Base64Utils.encode(ShaUtils.sha1(StringUtils.join(values, PARAM_JOINER)));
+    }
+
+    /**
+     * 基于解析好的签名对象,使用传入的保密信息进行签名内容验证
+     *
+     * @param secret
+     * @return
+     */
+    public boolean validate(String secret) {
+        if (method != null && uri != null && timestamp >= 0 && secret != null && ciphertext != null) {
+            return encrypt(method, uri, String.valueOf(timestamp), secret).equals(ciphertext);
+        }
+        return false;
+    }
+
+    /**
+     * 根据标准参数构造最终的签名字符串
+     *
+     * @param type
+     * @param method
+     * @param uri
+     * @param timestamp
+     * @param invoker
+     * @param secret
+     * @return
+     */
+    public static String build(SignatureType type, String method, String uri, long timestamp, String invoker, String secret) {
+        if (type == null || method == null || uri == null || timestamp <= 0 || invoker == null || secret == null) {
+            return "";
+        }
+        return MessageFormat.format(PATTERN, type.getName(), invoker, FIELD_JOINER,
+                encrypt(method.toLowerCase(), uri, String.valueOf(timestamp), secret));
+    }
+
+    /**
+     * 根据当前接口的的基本信息、header中的时间戳与签名字符串,尝试解析并构造签名数据对象
+     *
+     * @param method
+     * @param uri
+     * @param timestamp
+     * @param signature
+     * @return
+     */
+    public static SignatureInfo parse(String method, String uri, long timestamp, String signature) {
+        if (method == null || uri == null || timestamp <= 0 || signature == null) {
+            return null;
+        }
+        String[] values = StringUtils.split(signature);
+        if (values != null && values.length == 2) {
+            SignatureType type = typeMap.get(values[0]);
+            if (type != null) {
+                String[] array = StringUtils.split(values[1], FIELD_JOINER);
+                if (array != null && array.length == 2) {
+                    SignatureInfo info = new SignatureInfo();
+                    info.setType(type);
+                    info.setMethod(method.toLowerCase());
+                    info.setUri(uri);
+                    info.setTimestamp(timestamp);
+                    info.setInvoker(array[0]);
+                    info.setCiphertext(array[1]);
+                    return info;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static void main(String[] args) {
+        SignatureInfo source = new SignatureInfo();
+        source.setType(SignatureType.TOKEN);
+//        source.setMethod("POST");
+        source.setMethod("GET");
+        source.setUri("/api/admin/invigilate/list");
+        source.setTimestamp(System.currentTimeMillis());
+        source.setInvoker("3_STUDENT_pc");
+        source.setSecret("R4IYArPwnU9hbeBev6CDDNC0S126sdb3");
+        System.out.println(source.getTimestamp());
+        long start = System.currentTimeMillis();
+        String signature = SignatureInfo
+                .build(source.getType(), source.getMethod(), source.getUri(), source.getTimestamp(), source.getInvoker(),
+                        source.getSecret());
+        System.out.println("signature:" + signature + "\ntime cost=" + (System.currentTimeMillis() - start));
+
+        start = System.currentTimeMillis();
+        SignatureInfo parse = SignatureInfo.parse(source.getMethod(), source.getUri(), source.getTimestamp(), signature);
+        System.out.println(
+                "validate:" + (parse != null && parse.validate(source.getSecret())) + "\ntime cost=" + (System.currentTimeMillis()
+                        - start));
+    }
+
+}

+ 17 - 0
themis-common/src/main/java/com/qmth/themis/common/signature/SignatureType.java

@@ -0,0 +1,17 @@
+package com.qmth.themis.common.signature;
+
+public enum SignatureType {
+
+    SECRET("Secret"), TOKEN("Token");
+
+    private String name;
+
+    private SignatureType(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+}

+ 1 - 1
themis-exam/src/main/java/com/qmth/themis/exam/config/SwaggerConfig.java

@@ -32,7 +32,7 @@ public class SwaggerConfig {
     public Docket createRestApi() {
         ParameterBuilder tokenPar = new ParameterBuilder();
         List<Parameter> pars = new ArrayList<>();
-        tokenPar.name(SystemConstant.TOKEN).description(SystemConstant.TOKEN).modelRef(new ModelRef("string")).parameterType("header").required(false).build();
+        tokenPar.name(SystemConstant.ACCESS_TOKEN).description(SystemConstant.ACCESS_TOKEN).modelRef(new ModelRef("string")).parameterType("header").required(false).build();
         pars.add(tokenPar.build());
 
         return new Docket(DocumentationType.SWAGGER_2)