瀏覽代碼

Merge branch 'develop' into dev/20211117_2.1.4

songxinlu 3 年之前
父節點
當前提交
939d4ed8dd

+ 9 - 0
doc/038.20211129_2.1.3/qc_initv2.1.3.sql

@@ -0,0 +1,9 @@
+
+use `qc`;
+-- 执行前请看注意事项!
+-- 通用版本token有效时间配置化
+/**
+sys_dictionary_info表新增token时间配置
+ */
+INSERT INTO `sys_dictionary_info` (`group_type`, `name`, `val`, `return_type`, `remark`) VALUES ('31', 'accessToken', '86400', '2', 'accessToken有效期(单位秒)');
+INSERT INTO `sys_dictionary_info` (`group_type`, `name`, `val`, `return_type`, `remark`) VALUES ('31', 'refreshToken', '604800', '2', 'refreshToken有效期(单位秒)');

+ 44 - 0
src/main/java/com/diagbot/config/AuthExceptionEntryPoint.java

@@ -0,0 +1,44 @@
+package com.diagbot.config;
+
+import com.diagbot.util.StringUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Description:
+ * @Author songxl
+ * @Date 2021/11/30
+ */
+public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {
+
+
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse response,
+                         AuthenticationException authException)
+            throws ServletException {
+        Map map = new HashMap();
+        if (StringUtil.isNotEmpty(authException.getMessage())&&authException.getMessage().contains("Access token expired")) {
+            map.put("code", "10020011");
+            map.put("msg", "登录超时。为确保您的账户安全,系统已自动退出,请重新登录。");
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        }else {
+            response.setStatus(HttpServletResponse.SC_OK);
+            map.put("code", "00000001");
+            map.put("msg", authException.getMessage());
+        }
+        response.setContentType("application/json");
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.writeValue(response.getOutputStream(), map);
+        } catch (Exception e) {
+            throw new ServletException();
+        }
+    }
+}

+ 14 - 2
src/main/java/com/diagbot/config/OAuth2Configurer.java

@@ -1,5 +1,6 @@
 package com.diagbot.config;
 
+import com.diagbot.facade.SysDictionaryFacade;
 import com.diagbot.service.UrlUserService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -33,17 +34,28 @@ import java.util.Arrays;
 public class OAuth2Configurer extends AuthorizationServerConfigurerAdapter {
     @Autowired
     private UrlUserService urlUserService;
+    @Autowired
+    private SysDictionaryFacade sysDictionaryFacade;
 
     @Override
     public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
+        int accessToken = 24 * 3600;
+        int refreshToken = 30 * 24 * 3600;
+        if (sysDictionaryFacade.getDictionaryWithKey() != null
+                && sysDictionaryFacade.getDictionaryWithKey().containsKey("31")
+                && sysDictionaryFacade.getDictionaryWithKey().get("31").containsKey("accessToken")
+                && sysDictionaryFacade.getDictionaryWithKey().get("31").containsKey("refreshToken")) {
+            accessToken = Integer.parseInt(sysDictionaryFacade.getDictionaryWithKey().get("31").get("accessToken"));
+            refreshToken = Integer.parseInt(sysDictionaryFacade.getDictionaryWithKey().get("31").get("refreshToken"));
+        }
         clients.inMemory()
                 .withClient("uaa-service")
                 .secret("{noop}123456")
                 .scopes("service")
                 .autoApprove(true)
                 .authorizedGrantTypes("implicit", "refresh_token", "password", "authorization_code")
-                .accessTokenValiditySeconds(24 * 3600)
-                .refreshTokenValiditySeconds(30 * 24 * 3600);
+                .accessTokenValiditySeconds(accessToken)
+                .refreshTokenValiditySeconds(refreshToken);
     }
 
     /**

+ 4 - 0
src/main/java/com/diagbot/config/ResourceServerConfigurer.java

@@ -13,6 +13,7 @@ import org.springframework.security.jwt.crypto.sign.RsaVerifier;
 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
 import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
 import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
 import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
 import org.springframework.util.FileCopyUtils;
@@ -33,6 +34,8 @@ public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
     @Override
     public void configure(HttpSecurity http) throws Exception {
         http.cors()
+                .and()
+                .exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint())
                 .and()
                 .csrf().disable()
                 .authorizeRequests()
@@ -284,6 +287,7 @@ public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
     public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
         log.info("Configuring ResourceServerSecurityConfigurer");
         resources.resourceId("user-service").tokenStore(new JwtTokenStore(jwtTokenEnhancerClient()));
+        resources.authenticationEntryPoint(new AuthExceptionEntryPoint());
     }
     @Autowired
     private CustomAccessTokenConverter customAccessTokenConverter;

+ 41 - 3
src/main/java/com/diagbot/config/security/UrlAccessDecisionManager.java

@@ -1,5 +1,8 @@
 package com.diagbot.config.security;
 
+import com.diagbot.exception.CommonErrorCode;
+import com.diagbot.exception.CommonException;
+import com.diagbot.exception.ServiceErrorCode;
 import com.diagbot.facade.TokenFacade;
 import com.diagbot.util.HttpUtils;
 import com.diagbot.util.StringUtil;
@@ -32,17 +35,28 @@ public class UrlAccessDecisionManager implements AccessDecisionManager {
     public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
         HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
         String url, method;
+        String tokenStr = HttpUtils.getHeaders(request).get("Authorization");
+        //用户是否被顶掉校验
+        if (StringUtil.isNotEmpty(tokenStr) && !matchNotCheckUrl(request)) {
+            tokenStr = tokenStr.replaceFirst("Bearer ", "");
+            int res = tokenFacade.newVerifyToken(tokenStr, 1);
+            if (-1 == res) {
+                throw new CommonException(ServiceErrorCode.LONGIN_ERROE);
+            }
+        }
         if (matchPermitAllUrl(request)) {
             return;
         }
         if ("anonymousUser".equals(authentication.getPrincipal())) {
             throw new AccessDeniedException("no right");
         } else {
-            String tokenStr = HttpUtils.getHeaders(request).get("Authorization");
             if (StringUtil.isNotEmpty(tokenStr)) {
                 tokenStr = tokenStr.replaceFirst("Bearer ", "");
-                Boolean res = tokenFacade.verifyToken(tokenStr, 1);
-                if (!res) {
+//                Boolean res = tokenFacade.verifyToken(tokenStr, 1);
+                int res = tokenFacade.newVerifyToken(tokenStr, 1);
+                if (-1 == res) {
+                    throw new CommonException(CommonErrorCode.SERVER_IS_ERROR, "该账号在其他地方登录。");
+                } else if (1 != res) {
                     throw new AccountExpiredException("token expire");
                 }
             }
@@ -334,4 +348,28 @@ public class UrlAccessDecisionManager implements AccessDecisionManager {
         }
         return false;
     }
+    private boolean matchNotCheckUrl(HttpServletRequest request) {
+        if (matchers("/swagger/**", request)
+                || matchers("/v2/**", request)
+                || matchers("/swagger-ui.html/**", request)
+                || matchers("/swagger-resources/**", request)
+                || matchers("/webjars/**", request)
+                || matchers("/druid/**", request)
+                || matchers("/actuator/**", request)
+                || matchers("/hystrix/**", request)
+                || matchers("/sys/user/getJwt", request)
+                || matchers("/sys/user/logout", request)
+                || matchers("/sys/user/getCaptcha", request)
+                || matchers("/sys/user/getHospitalMark", request)
+                || matchers("/sys/user/getJwtNoPass", request)
+                || matchers("/sys/user/refreshJwt", request)
+                || matchers("/sys/dictionaryInfo/getDictionary", request)
+                || matchers("/sys/user/checkToken", request)
+                || matchers("/oauth/token", request)
+                || matchers("/oauth/check_token", request)
+                || matchers("/cache/clear", request)) {
+            return true;
+        }
+        return false;
+    }
 }

+ 13 - 1
src/main/java/com/diagbot/config/security/UrlFilterSecurityInterceptor.java

@@ -1,5 +1,7 @@
 package com.diagbot.config.security;
 
+import com.alibaba.fastjson.JSONObject;
+import com.diagbot.exception.CommonException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.SecurityMetadataSource;
 import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
@@ -43,7 +45,17 @@ public class UrlFilterSecurityInterceptor extends AbstractSecurityInterceptor im
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 
         FilterInvocation fi = new FilterInvocation(request, response, chain);
-        invoke(fi);
+        try {
+            invoke(fi);
+        }catch (CommonException e)
+        {
+            JSONObject error = new JSONObject();
+            response.setContentType("text/html;charset=utf-8");
+            error.put("code",e.getCode());
+            error.put("msg", e.getMessage());
+            response.getWriter().println(error);
+            response.flushBuffer();
+        }
     }
 
 

+ 1 - 0
src/main/java/com/diagbot/exception/ServiceErrorCode.java

@@ -12,6 +12,7 @@ public enum ServiceErrorCode implements ErrorCode {
     USER_PASSWORD_ERROR("10020001", "账号或密码不正确"),
     GET_TOKEN_FAIL("10020002", "获取token失败"),
     TOKEN_IS_NOT_MATCH_USER("10020003", "请使用自己的token进行接口请求"),
+    LONGIN_ERROE("10020012", "您的账号在其它地方已登录,您已被迫下线,请重新登录。如非本人授权,登录后请及时修改密码。"),
 
     SMS_SEND_ERROR("10020004", "短信发送错误"),
     USER_BIND_ERROR("10020005", "用户手机号已经绑定无需再次验证"),

+ 13 - 0
src/main/java/com/diagbot/facade/BehospitalInfoFacade.java

@@ -342,7 +342,20 @@ public class BehospitalInfoFacade extends BehospitalInfoServiceImpl {
         if (ListUtil.isEmpty(paramList) || paramList.contains("msg")) {
             AnalyzeVO analyzeVO = new AnalyzeVO();
             BeanUtil.copyProperties(getDetailVO, analyzeVO);
+            QcresultInfo qcresultInfo = qcresultInfoFacade.getOne(new QueryWrapper<QcresultInfo>()
+                    .eq("hospital_id", analyzeVO.getHospitalId())
+                    .eq("behospital_code", analyzeVO.getBehospitalCode())
+                    .eq("is_deleted", IsDeleteEnum.N.getKey()));
+            if (qcresultInfo != null && qcresultInfo.getId() != null) {
+                analyzeVO.setQcresultInfoId(qcresultInfo.getId());
+            }
             List<MsgDTO> msgDTOList = getMsg(analyzeVO);
+
+            if (ListUtil.isEmpty(msgDTOList)) {
+                analyzeVO.setQcresultInfoId(null);
+                msgDTOList = getMsg(analyzeVO);
+            }
+
             if (ListUtil.isNotEmpty(msgDTOList)) {
                 // 从qc_question_info的cases_entry_ids获取
                 Map<String, Object> paramMap = new HashMap<>();

+ 8 - 0
src/main/java/com/diagbot/service/SysTokenService.java

@@ -27,6 +27,14 @@ public interface SysTokenService {
      * @return
      */
     Boolean verifyToken(String token, Integer type);
+    /**
+     * 验证token是否有效
+     *
+     * @param token 待验证的token
+     * @param type  1:accessToken,2:refreshToken
+     * @return
+     */
+    int newVerifyToken(String token, Integer type);
 
     /**
      * 删除用户token

+ 54 - 0
src/main/java/com/diagbot/service/impl/SysTokenServiceImpl.java

@@ -127,6 +127,60 @@ public class SysTokenServiceImpl implements SysTokenService {
         return res;
     }
 
+    /**
+     * 验证token是否有效
+     *
+     * @param token 待验证的token
+     * @param type  1:accessToken,2:refreshToken
+     * @return -1:token无效(与服务器token不一致,异地登录),1:token有效,0:其他
+     */
+    @Override
+    public int newVerifyToken(String token, Integer type) {
+        Integer res = 0;
+        if (null == token) {
+            return 0;
+        }
+        String userId = JwtUtil.getUserId(token);
+        //从redis中取出
+        final byte[] redis_key = getUserTokenKey(userId);
+        JwtStore tokenStore = (JwtStore) redisForToken.execute(new RedisCallback<JwtStore>() {
+            @Override
+            public JwtStore doInRedis(RedisConnection connection) throws DataAccessException {
+                byte[] bytes = connection.get(redis_key);
+                if (bytes == null) {
+                    return null;
+                }
+                return (JwtStore) deserializeValue(bytes);
+            }
+        });
+
+        if (null != tokenStore) {
+            if (type == 1) {
+                if (null != tokenStore.getAccessToken()) {
+                    if (tokenStore.getAccessToken().equals(token)) {
+                        res = 1;
+                    } else {
+                        res = -1;
+                    }
+                }
+            }
+
+            if (type == 2) {
+                if (null != tokenStore.getRefreshToken()) {
+                    if (tokenStore.getRefreshToken().equals(token)) {
+                        res = 1;
+                    } else {
+                        res = -1;
+                    }
+                }
+            }
+        } else {
+            res = -1;
+        }
+
+        return res;
+    }
+
     /**
      * 删除用户token
      *

+ 2 - 0
src/main/java/com/diagbot/vo/AnalyzeVO.java

@@ -13,6 +13,8 @@ public class AnalyzeVO {
 
     private String behospitalCode; // 病历id
     @ApiModelProperty(hidden = true)
+    private Long qcresultInfoId; // 病历主表id
+    @ApiModelProperty(hidden = true)
     private Long hospitalId; //医院ID
     @ApiModelProperty(hidden = true)
     private Boolean isTask = false;

+ 3 - 6
src/main/resources/application-test.yml

@@ -109,12 +109,9 @@ spring:
   #redis
   redis:
     database:
-#      cache: 8 # cache索引
-#      token: 8 # Token索引
-#    host: 192.168.2.241  #Redis服务器地址
-      cache: 3 # cache索引
-      token: 3 # Token索引
-    host: 192.168.2.236  #Redis服务器地址
+      cache: 8 # cache索引
+      token: 8 # Token索引
+    host: 192.168.2.241  #Redis服务器地址
     port: 6379 # Redis服务器连接端口(本地环境端口6378,其他环境端口是6379)
     password: lantone # Redis服务器连接密码(默认为空)
     lettuce:

+ 3 - 0
src/main/resources/mapper/BehospitalInfoMapper.xml

@@ -693,6 +693,9 @@
         and a.id = c.cases_entry_id and a.mode_id = b.id
         AND c.cases_id = d.cases_id and c.hospital_id = d.hospital_id
         and c.hospital_id = #{hospitalId}
+        <if test="qcresultInfoId != null">
+            AND c.qcresult_info_id = #{qcresultInfoId}
+        </if>
         and c.behospital_code = #{behospitalCode}
         order by b.order_no, c.grade_type desc, a.order_no) a
         LEFT JOIN sys_user u  on u.id = a.modifier  and u.is_deleted = 'N'