diff --git a/cloud-common/cloud-common-core/src/main/java/cn/sh/stc/sict/cloud/common/core/constant/enums/BizCodeConstant.java b/cloud-common/cloud-common-core/src/main/java/cn/sh/stc/sict/cloud/common/core/constant/enums/BizCodeConstant.java
index 8ce574165643d2a6ceef1b80758c72987f52af81..006bcca574b7cd56a38b8850ef204ae8ea85a392 100644
--- a/cloud-common/cloud-common-core/src/main/java/cn/sh/stc/sict/cloud/common/core/constant/enums/BizCodeConstant.java
+++ b/cloud-common/cloud-common-core/src/main/java/cn/sh/stc/sict/cloud/common/core/constant/enums/BizCodeConstant.java
@@ -12,6 +12,7 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum BizCodeConstant {
     /* */
+    USER_PWD_LIMIT(99,""),
     TEMP(0,"");
 
     private Integer code;
diff --git a/cloud-common/cloud-common-core/src/main/java/cn/sh/stc/sict/cloud/common/core/util/WebUtils.java b/cloud-common/cloud-common-core/src/main/java/cn/sh/stc/sict/cloud/common/core/util/WebUtils.java
index 693f76e5f9f7ec21906e46418b6cab13b231f7f5..30980ec7c35da98a7ebbc2936b6274906cfd5643 100644
--- a/cloud-common/cloud-common-core/src/main/java/cn/sh/stc/sict/cloud/common/core/util/WebUtils.java
+++ b/cloud-common/cloud-common-core/src/main/java/cn/sh/stc/sict/cloud/common/core/util/WebUtils.java
@@ -193,6 +193,37 @@ public class WebUtils extends org.springframework.web.util.WebUtils {
 		return StringUtils.isBlank(ip) ? null : ip.split(",")[0];
 	}
 
+	public String getIP(ServerHttpRequest request) {
+		HttpHeaders headers = request.getHeaders();
+		String ip = headers.getFirst("x-forwarded-for");
+		if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
+			// 多次反向代理后会有多个ip值,第一个ip才是真实ip
+			if (ip.indexOf(",") != -1) {
+				ip = ip.split(",")[0];
+			}
+		}
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = headers.getFirst("Proxy-Client-IP");
+		}
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = headers.getFirst("WL-Proxy-Client-IP");
+		}
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = headers.getFirst("HTTP_CLIENT_IP");
+		}
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
+		}
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = headers.getFirst("X-Real-IP");
+		}
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getRemoteAddress().getAddress().getHostAddress();
+		}
+		return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
+	}
+
+
 	/**
 	 * 从request 获取CLIENT_ID
 	 *
diff --git a/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/config/WhitIPConfig.java b/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/config/WhitIPConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..2dcf10386cb5ee84c34af4f871657510c92f9040
--- /dev/null
+++ b/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/config/WhitIPConfig.java
@@ -0,0 +1,24 @@
+package cn.sh.stc.sict.cloud.common.gateway.config;
+
+import lombok.Data;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Description 网关不校验终端配置
+ * @Author
+ * @Date
+ */
+@Data
+@Configuration
+@RefreshScope
+@ConditionalOnExpression("!'${whiteip}'.isEmpty()")
+@ConfigurationProperties(prefix = "whiteip")
+public class WhitIPConfig {
+    private List<String> whites = new ArrayList<>();
+}
diff --git a/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/filter/RequestGlobalFilter.java b/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/filter/RequestGlobalFilter.java
index e063637cb4f0bf5b54d5c18d7577aeec2b25661c..6cf236a482f87d54f0348c2b5d2922df7b228160 100644
--- a/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/filter/RequestGlobalFilter.java
+++ b/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/filter/RequestGlobalFilter.java
@@ -1,11 +1,21 @@
 package cn.sh.stc.sict.cloud.common.gateway.filter;
 
+import cn.sh.stc.sict.cloud.common.core.constant.Constant;
 import cn.sh.stc.sict.cloud.common.core.constant.SecurityConstants;
+import cn.sh.stc.sict.cloud.common.core.util.R;
+import cn.sh.stc.sict.cloud.common.core.util.WebUtils;
+import cn.sh.stc.sict.cloud.common.gateway.config.WhitIPConfig;
+import cn.sh.stc.sict.cloud.common.gateway.util.IPStrUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.cloud.gateway.filter.GatewayFilterChain;
 import org.springframework.cloud.gateway.filter.GlobalFilter;
 import org.springframework.core.Ordered;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 import org.springframework.web.server.ServerWebExchange;
@@ -18,57 +28,85 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G
 import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
 
 /**
- * @Description
- * 		<p>
- * 		全局拦截器,作用所有的微服务
- * 		<p>
- * 		1. 对请求头中参数进行处理 from 参数进行清洗
- * 		2. 重写StripPrefix = 1,支持全局
- * 		<p>
- * 		支持swagger添加X-Forwarded-Prefix header  (F SR2 已经支持,不需要自己维护)
+ * @Description <p>
+ * 全局拦截器,作用所有的微服务
+ * <p>
+ * 1. 对请求头中参数进行处理 from 参数进行清洗
+ * 2. 重写StripPrefix = 1,支持全局
+ * <p>
+ * 支持swagger添加X-Forwarded-Prefix header  (F SR2 已经支持,不需要自己维护)
  * @Author
  * @Date
  */
 @Slf4j
 @Component
+@AllArgsConstructor
 public class RequestGlobalFilter implements GlobalFilter, Ordered {
-	private static final String HEADER_NAME = "X-Forwarded-Prefix";
+    private static final String HEADER_NAME = "X-Forwarded-Prefix";
+    private final WhitIPConfig whitIPConfig;
+    private final ObjectMapper objectMapper;
 
-	/**
-	 * Process the Web request and (optionally) delegate to the next
-	 * {@code WebFilter} through the given {@link GatewayFilterChain}.
-	 *
-	 * @param exchange the current server exchange
-	 * @param chain    provides a way to delegate to the next filter
-	 * @return {@code Mono<Void>} to indicate when request processing is complete
-	 */
-	@Override
-	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
-		// 1. 清洗请求头中from 参数
-		ServerHttpRequest request = exchange.getRequest().mutate()
-			.headers(httpHeaders -> {httpHeaders.remove(SecurityConstants.FROM);})
-			.build();
+    /**
+     * Process the Web request and (optionally) delegate to the next
+     * {@code WebFilter} through the given {@link GatewayFilterChain}.
+     *
+     * @param exchange the current server exchange
+     * @param chain    provides a way to delegate to the next filter
+     * @return {@code Mono<Void>} to indicate when request processing is complete
+     */
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        // 1. 清洗请求头中from 参数
+        ServerHttpRequest request = exchange.getRequest().mutate()
+                .headers(httpHeaders -> {
+                    httpHeaders.remove(SecurityConstants.FROM);
+                })
+                .build();
 
-		// IP白名单
-		log.error("RemoteAddress ===================> {}", request.getRemoteAddress());
+        // IP白名单
+        String ip = WebUtils.getIP(request);
+        log.error("RemoteAddress ===================> {}", request.getRemoteAddress());
+        try {
+            if (!IPStrUtil.matches(ip, whitIPConfig.getWhites())) {
+                ServerHttpResponse response = exchange.getResponse();
+                response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED);
+                response.getHeaders().set("Content-type", "application/json; charset=utf-8");
+                return response.writeWith(Mono.just(response.bufferFactory()
+                        .wrap(objectMapper.writeValueAsBytes(
+                                R.builder().msg("没有权限访问")
+                                        .code(Constant.BYTE_NO).build()))));
+            }
+        } catch (
+                Exception e) {
+            ServerHttpResponse response = exchange.getResponse();
+            response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED);
+            response.getHeaders().set("Content-type", "application/json; charset=utf-8");
+            try {
+                return response.writeWith(Mono.just(response.bufferFactory()
+                        .wrap(objectMapper.writeValueAsBytes(
+                                R.builder().msg(e.getMessage())
+                                        .code(Constant.BYTE_NO).build()))));
+            } catch (JsonProcessingException e1) {
+                log.error("对象输出异常", e1);
+            }
+        }
+        // 2. 重写StripPrefix
+        addOriginalRequestUrl(exchange, request.getURI());
+        String rawPath = request.getURI().getRawPath();
+        String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/"))
+                .skip(1L).collect(Collectors.joining("/"));
+        ServerHttpRequest newRequest = request.mutate()
+                .path(newPath)
+                .build();
+        exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
 
-		// 2. 重写StripPrefix
-		addOriginalRequestUrl(exchange, request.getURI());
-		String rawPath = request.getURI().getRawPath();
-		String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/"))
-			.skip(1L).collect(Collectors.joining("/"));
-		ServerHttpRequest newRequest = request.mutate()
-			.path(newPath)
-			.build();
-		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
+        return chain.filter(exchange.mutate()
+                .request(newRequest.mutate()
+                        .build()).build());
+    }
 
-		return chain.filter(exchange.mutate()
-			.request(newRequest.mutate()
-				.build()).build());
-	}
-
-	@Override
-	public int getOrder() {
-		return -1000;
-	}
+    @Override
+    public int getOrder() {
+        return -1000;
+    }
 }
diff --git a/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/util/IPStrUtil.java b/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/util/IPStrUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a252545487bf9abcb927561c9b3d8606b4b3c8f
--- /dev/null
+++ b/cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/util/IPStrUtil.java
@@ -0,0 +1,114 @@
+package cn.sh.stc.sict.cloud.common.gateway.util;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.List;
+
+public class IPStrUtil {
+    static char START = '*';
+    /**
+     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+     *
+     * @param str  指定字符串
+     * @param strs 需要检查的字符串数组
+     * @return 是否匹配
+     */
+    public static boolean matches(String str, List<String> strs) {
+        if (StrUtil.isEmpty(str) || CollUtil.isEmpty(strs)) {
+            return false;
+        }
+        for (String testStr : strs) {
+            if (matches(str, testStr)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 查找指定字符串是否匹配指定字符串数组中的任意一个字符串
+     *
+     * @param str  指定字符串
+     * @param strs 需要检查的字符串数组
+     * @return 是否匹配
+     */
+    public static boolean matches(String str, String... strs) {
+        if (StrUtil.isEmpty(str) || StrUtil.isAllEmpty(str)) {
+            return false;
+        }
+        for (String testStr : strs) {
+            if (matches(str, testStr)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 查找指定字符串是否匹配
+     *
+     * @param str     指定字符串
+     * @param pattern 需要检查的字符串
+     * @return 是否匹配
+     */
+    public static boolean matches(String str, String pattern) {
+        if (StrUtil.isEmpty(pattern) || StrUtil.isEmpty(str)) {
+            return false;
+        }
+        pattern = pattern.replaceAll("\\s*", ""); // 替换空格
+        int beginOffset = 0; // pattern截取开始位置
+        int formerStarOffset = -1; // 前星号的偏移位置
+        int latterStarOffset = -1; // 后星号的偏移位置
+
+        String remainingURI = str;
+        String prefixPattern = "";
+        String suffixPattern = "";
+
+        boolean result = false;
+        do {
+            formerStarOffset = StrUtil.indexOf(pattern, START, beginOffset);
+            prefixPattern = StrUtil.sub(pattern, beginOffset, formerStarOffset > -1 ? formerStarOffset : pattern.length());
+
+            // 匹配前缀Pattern
+            result = remainingURI.contains(prefixPattern);
+            // 已经没有星号,直接返回
+            if (formerStarOffset == -1) {
+                return result;
+            }
+
+            // 匹配失败,直接返回
+            if (!result)
+                return false;
+
+            if (!StrUtil.isEmpty(prefixPattern)) {
+                remainingURI = StrUtil.subAfter(str, prefixPattern, true);
+            }
+
+            // 匹配后缀Pattern
+            latterStarOffset = StrUtil.indexOf(pattern, START, formerStarOffset + 1);
+            suffixPattern = StrUtil.sub(pattern, formerStarOffset + 1, latterStarOffset > -1 ? latterStarOffset : pattern.length());
+
+            result = remainingURI.contains(suffixPattern);
+            // 匹配失败,直接返回
+            if (!result)
+                return false;
+
+            if (!StrUtil.isEmpty(suffixPattern)) {
+                remainingURI = StrUtil.subAfter(str, suffixPattern, true);
+            }
+
+            // 移动指针
+            beginOffset = latterStarOffset + 1;
+
+        }
+        while (!StrUtil.isEmpty(suffixPattern) && !StrUtil.isEmpty(remainingURI));
+
+        return true;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(Object obj) {
+        return (T) obj;
+    }
+}
diff --git a/smart-health-modules/cloud-upms/cloud-upms-api/src/main/java/cn/sh/stc/sict/cloud/upms/model/SysUserBase.java b/smart-health-modules/cloud-upms/cloud-upms-api/src/main/java/cn/sh/stc/sict/cloud/upms/model/SysUserBase.java
index 4f970994889573dadd252996629b8bfade561f1b..ecd66e229b3f74b5f42dd519717c7a5e6c7d3064 100644
--- a/smart-health-modules/cloud-upms/cloud-upms-api/src/main/java/cn/sh/stc/sict/cloud/upms/model/SysUserBase.java
+++ b/smart-health-modules/cloud-upms/cloud-upms-api/src/main/java/cn/sh/stc/sict/cloud/upms/model/SysUserBase.java
@@ -142,6 +142,8 @@ public class SysUserBase extends Model<SysUserBase> {
     @ApiModelProperty(hidden = true, value = "")
     private Date updateTime;
 
+    private Date lastPwdTime;
+
     /**
      * 主键值
      */
diff --git a/smart-health-modules/cloud-upms/cloud-upms-biz/src/main/java/cn/sh/stc/sict/cloud/upms/controller/web/SysUserBaseController.java b/smart-health-modules/cloud-upms/cloud-upms-biz/src/main/java/cn/sh/stc/sict/cloud/upms/controller/web/SysUserBaseController.java
index a82cbb98c3f0d3315c0b21b112581f28895f92de..b4013cf25b9aaed5fec4cf1b6bc77de725ec5cce 100644
--- a/smart-health-modules/cloud-upms/cloud-upms-biz/src/main/java/cn/sh/stc/sict/cloud/upms/controller/web/SysUserBaseController.java
+++ b/smart-health-modules/cloud-upms/cloud-upms-biz/src/main/java/cn/sh/stc/sict/cloud/upms/controller/web/SysUserBaseController.java
@@ -2,6 +2,7 @@ package cn.sh.stc.sict.cloud.upms.controller.web;
 
 import cn.hutool.core.util.StrUtil;
 import cn.sh.stc.sict.cloud.common.core.constant.Constant;
+import cn.sh.stc.sict.cloud.common.core.constant.enums.BizCodeConstant;
 import cn.sh.stc.sict.cloud.common.core.util.NumberUtil;
 import cn.sh.stc.sict.cloud.common.log.annotation.SysLog;
 import cn.sh.stc.sict.cloud.common.security.util.SecurityUtils;
@@ -118,7 +119,7 @@ public class SysUserBaseController {
         dto.setRoleList(roleList);
         dto.setMenuList(menuList);
 
-        return new R(dto);
+        return new R(dto).setBizCode(BizCodeConstant.USER_PWD_LIMIT.getCode());
     }