From 5db3bca027c3471915ce6462f2b0fd81c92827b5 Mon Sep 17 00:00:00 2001 From: fshenye <512914587@qq.com> Date: Thu, 20 Oct 2022 16:12:40 +0800 Subject: [PATCH] =?UTF-8?q?1.=20IP=E7=99=BD=E5=90=8D=E5=8D=9503?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/constant/enums/BizCodeConstant.java | 1 + .../sict/cloud/common/core/util/WebUtils.java | 31 +++++ .../common/gateway/config/WhitIPConfig.java | 24 ++++ .../gateway/filter/RequestGlobalFilter.java | 124 ++++++++++++------ .../cloud/common/gateway/util/IPStrUtil.java | 114 ++++++++++++++++ .../sict/cloud/upms/model/SysUserBase.java | 2 + .../controller/web/SysUserBaseController.java | 3 +- 7 files changed, 255 insertions(+), 44 deletions(-) create mode 100644 cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/config/WhitIPConfig.java create mode 100644 cloud-common/cloud-common-gateway/src/main/java/cn/sh/stc/sict/cloud/common/gateway/util/IPStrUtil.java 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 8ce5741..006bcca 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 693f76e..30980ec 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 0000000..2dcf103 --- /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 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 e063637..6cf236a 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 - *

- * 全局拦截器,作用所有的微服务 - *

- * 1. 对请求头中参数进行处理 from 参数进行清洗 - * 2. 重写StripPrefix = 1,支持全局 - *

- * 支持swagger添加X-Forwarded-Prefix header (F SR2 已经支持,不需要自己维护) + * @Description

+ * 全局拦截器,作用所有的微服务 + *

+ * 1. 对请求头中参数进行处理 from 参数进行清洗 + * 2. 重写StripPrefix = 1,支持全局 + *

+ * 支持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} to indicate when request processing is complete - */ - @Override - public Mono 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} to indicate when request processing is complete + */ + @Override + public Mono 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 0000000..0a25254 --- /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 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 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 4f97099..ecd66e2 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 { @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 a82cbb9..b4013cf 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()); } -- 2.22.0