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()); }