作者 朱兆平

1. 优化路由判定

2. 安全配置
@@ -3,6 +3,7 @@ spring: @@ -3,6 +3,7 @@ spring:
3 host: 192.168.1.53 3 host: 192.168.1.53
4 port: 6379 4 port: 6379
5 timeout: 60000 5 timeout: 60000
  6 + password: vmvnv1v2VV.
6 application: 7 application:
7 name: gateway-server-v12 8 name: gateway-server-v12
8 profiles: 9 profiles:
@@ -73,6 +74,10 @@ management: @@ -73,6 +74,10 @@ management:
73 gateway: 74 gateway:
74 enabled: false 75 enabled: false
75 endpoint: 76 endpoint:
  77 + gateway:
  78 + enabled: false
  79 + env:
  80 + enabled: false
76 health: 81 health:
77 enabled: true # 启用健康检查端点 82 enabled: true # 启用健康检查端点
78 info: 83 info:
@@ -11,13 +11,13 @@ @@ -11,13 +11,13 @@
11 </parent> 11 </parent>
12 <groupId>com.example</groupId> 12 <groupId>com.example</groupId>
13 <artifactId>gateway</artifactId> 13 <artifactId>gateway</artifactId>
14 - <version>1.0-WIN</version> 14 + <version>1.0-linux</version>
15 <name>gateway</name> 15 <name>gateway</name>
16 <description>gateway project for Spring Boot</description> 16 <description>gateway project for Spring Boot</description>
17 17
18 <properties> 18 <properties>
19 <java.version>1.8</java.version> 19 <java.version>1.8</java.version>
20 - <spring-cloud.version>Greenwich.SR2</spring-cloud.version> 20 + <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
21 </properties> 21 </properties>
22 22
23 <dependencies> 23 <dependencies>
@@ -58,7 +58,7 @@ public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered { @@ -58,7 +58,7 @@ public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
58 if(request.getPath().toString().contains(LOGIN)){ 58 if(request.getPath().toString().contains(LOGIN)){
59 return chain.filter(exchange); 59 return chain.filter(exchange);
60 } 60 }
61 - boolean flag = UrlFilter(request); 61 + boolean flag = urlFilter(request);
62 if(!flag){ 62 if(!flag){
63 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); 63 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
64 return exchange.getResponse().setComplete(); 64 return exchange.getResponse().setComplete();
@@ -119,36 +119,50 @@ public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered { @@ -119,36 +119,50 @@ public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
119 return -2; 119 return -2;
120 } 120 }
121 121
122 - public boolean UrlFilter(ServerHttpRequest request) {  
123 - //先鉴权匿名者可访问的接口  
124 - if (anonymousUrlFilter(request)){ 122 + public boolean urlFilter(ServerHttpRequest request) {
  123 + // 鉴权匿名者可访问的接口
  124 + if (anonymousUrlFilter(request)) {
125 return true; 125 return true;
126 } 126 }
127 - String token = "";  
128 - if (!request.getPath().toString().contains(LOGIN)) {  
129 - HttpHeaders rqHeader = request.getHeaders();  
130 - if(rqHeader.containsKey("Authorization")){  
131 - token = rqHeader.get("Authorization").toString();  
132 - token = token.substring(1, token.length() - 1);  
133 - log.info("[TOKEN]-Request Authorization INFO is:[{}]",token);  
134 - }  
135 - }  
136 - String redisKey = token.replace("Bearer ", "");  
137 - log.info("[REDIS-KEY]-is:[{}]",redisKey);  
138 - String json = stringRedisTemplate.opsForValue().get(redisKey);  
139 - if (json != null) {  
140 - List<PERMISSION> permissionList = JsonToBean.jsonToUser(json);  
141 - for (PERMISSION permission : permissionList) {  
142 - log.trace("访问url:[{}]<->权限[{}]",request.getPath().toString(),permission.getUrl());  
143 - if (pathMatcher.match(permission.getUrl(), request.getPath().toString())) {  
144 - log.info("[FILTER]-[URL:{}]->鉴权成功",request.getPath().toString());  
145 - return true; 127 +
  128 + // 获取请求路径
  129 + String path = request.getPath().toString();
  130 +
  131 + // 如果不是登录请求,尝试获取并处理Authorization头
  132 + if (!path.contains(LOGIN)) {
  133 + HttpHeaders headers = request.getHeaders();
  134 + String token = headers.getFirst("Authorization");
  135 +
  136 + // 如果存在Authorization头,并且是Bearer类型
  137 + if (token != null && token.startsWith("Bearer ")) {
  138 + token = token.substring(7); // "Bearer ".length()
  139 + log.info("[TOKEN]-Request Authorization INFO is:[{}]", token);
  140 +
  141 + // 构建Redis键
  142 + String redisKey = token;
  143 + log.info("[REDIS-KEY]-is:[{}]", redisKey);
  144 +
  145 + // 尝试从Redis中获取用户信息
  146 + String json = stringRedisTemplate.opsForValue().get(redisKey);
  147 + if (json != null) {
  148 + List<PERMISSION> permissionList = JsonToBean.jsonToUser(json);
  149 +
  150 + // 使用并行流优化循环
  151 + if (permissionList.parallelStream().anyMatch(permission -> {
  152 + log.trace("访问url:[{}]<->权限[{}]", path, permission.getUrl());
  153 + return pathMatcher.match(permission.getUrl(), path);
  154 + })) {
  155 + log.info("[FILTER]-[URL:{}]->鉴权成功", path);
  156 + return true;
  157 + }
  158 + } else {
  159 + log.warn("[FILTER]-没有对应token的redis缓存,鉴权失败");
146 } 160 }
147 } 161 }
148 - }else {  
149 - log.info("[FILTER]-没有对应token的redis缓存,鉴权失败");  
150 } 162 }
151 - log.info("[FILTER]-[URL:{}]->鉴权失败",request.getPath().toString()); 163 +
  164 + // 如果未通过任何鉴权检查,记录失败并返回false
  165 + log.warn("[FILTER]-[URL:{}]->鉴权失败", path);
152 return false; 166 return false;
153 } 167 }
154 168
@@ -159,6 +173,7 @@ public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered { @@ -159,6 +173,7 @@ public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
159 */ 173 */
160 public boolean anonymousUrlFilter(ServerHttpRequest request){ 174 public boolean anonymousUrlFilter(ServerHttpRequest request){
161 //内部服务接口,不允许外部访问 175 //内部服务接口,不允许外部访问
  176 + System.out.println("request.getPath().toString() = " + request.getPath().toString());
162 if(pathMatcher.match("/**/anonymous/**", request.getPath().toString())) { 177 if(pathMatcher.match("/**/anonymous/**", request.getPath().toString())) {
163 return true; 178 return true;
164 } 179 }
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!--参考文档链接:https://blog.csdn.net/qq_34912478/article/details/80877132-->
  3 +<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
  4 +<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
  5 +<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
  6 +<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
  7 +<configuration scan="true" scanPeriod="10 seconds">
  8 +
  9 + <!--<include resource="org/springframework/boot/logging/logback/base.xml" />-->
  10 +
  11 + <contextName>logback</contextName>
  12 + <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
  13 + <property name="log.path" value="./logs" />
  14 +
  15 + <!-- 彩色日志 -->
  16 + <!-- 彩色日志依赖的渲染类 -->
  17 + <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
  18 + <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
  19 + <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
  20 + <!-- 彩色日志格式 -->
  21 + <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
  22 +
  23 +
  24 + <!--输出到控制台-->
  25 + <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  26 + <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
  27 + <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
  28 + <level>INFO</level>
  29 + </filter>
  30 + <encoder>
  31 + <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
  32 + <!-- 设置字符集 -->
  33 + <charset>UTF-8</charset>
  34 + </encoder>
  35 + </appender>
  36 +
  37 +
  38 + <!--输出到文件-->
  39 +
  40 + <!-- 时间滚动输出 level为 DEBUG 日志 -->
  41 + <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  42 + <!-- 正在记录的日志文件的路径及文件名 -->
  43 + <file>${log.path}/log_debug.log</file>
  44 + <!--日志文件输出格式-->
  45 + <encoder>
  46 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  47 + <charset>UTF-8</charset> <!-- 设置字符集 -->
  48 + </encoder>
  49 + <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
  50 + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  51 + <!-- 日志归档 -->
  52 + <fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  53 + <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
  54 + <maxFileSize>100MB</maxFileSize>
  55 + </timeBasedFileNamingAndTriggeringPolicy>
  56 + <!--日志文件保留天数-->
  57 + <maxHistory>15</maxHistory>
  58 + </rollingPolicy>
  59 + <!-- 此日志文件只记录debug级别的 -->
  60 + <filter class="ch.qos.logback.classic.filter.LevelFilter">
  61 + <level>debug</level>
  62 + <onMatch>ACCEPT</onMatch>
  63 + <onMismatch>DENY</onMismatch>
  64 + </filter>
  65 + </appender>
  66 +
  67 + <!-- 时间滚动输出 level为 INFO 日志 -->
  68 + <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  69 + <!-- 正在记录的日志文件的路径及文件名 -->
  70 + <file>${log.path}/log_info.log</file>
  71 + <!--日志文件输出格式-->
  72 + <encoder>
  73 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  74 + <charset>UTF-8</charset>
  75 + </encoder>
  76 + <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
  77 + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  78 + <!-- 每天日志归档路径以及格式 -->
  79 + <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  80 + <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
  81 + <maxFileSize>100MB</maxFileSize>
  82 + </timeBasedFileNamingAndTriggeringPolicy>
  83 + <!--日志文件保留天数-->
  84 + <maxHistory>15</maxHistory>
  85 + </rollingPolicy>
  86 + <!-- 此日志文件只记录info级别的 -->
  87 + <filter class="ch.qos.logback.classic.filter.LevelFilter">
  88 + <level>info</level>
  89 + <onMatch>ACCEPT</onMatch>
  90 + <onMismatch>DENY</onMismatch>
  91 + </filter>
  92 + </appender>
  93 +
  94 + <!-- 时间滚动输出 level为 WARN 日志 -->
  95 + <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  96 + <!-- 正在记录的日志文件的路径及文件名 -->
  97 + <file>${log.path}/log_warn.log</file>
  98 + <!--日志文件输出格式-->
  99 + <encoder>
  100 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  101 + <charset>UTF-8</charset> <!-- 此处设置字符集 -->
  102 + </encoder>
  103 + <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
  104 + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  105 + <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  106 + <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
  107 + <maxFileSize>100MB</maxFileSize>
  108 + </timeBasedFileNamingAndTriggeringPolicy>
  109 + <!--日志文件保留天数-->
  110 + <maxHistory>15</maxHistory>
  111 + </rollingPolicy>
  112 + <!-- 此日志文件只记录warn级别的 -->
  113 + <filter class="ch.qos.logback.classic.filter.LevelFilter">
  114 + <level>warn</level>
  115 + <onMatch>ACCEPT</onMatch>
  116 + <onMismatch>DENY</onMismatch>
  117 + </filter>
  118 + </appender>
  119 +
  120 +
  121 + <!-- 时间滚动输出 level为 ERROR 日志 -->
  122 + <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  123 + <!-- 正在记录的日志文件的路径及文件名 -->
  124 + <file>${log.path}/log_error.log</file>
  125 + <!--日志文件输出格式-->
  126 + <encoder>
  127 + <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  128 + <charset>UTF-8</charset> <!-- 此处设置字符集 -->
  129 + </encoder>
  130 + <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
  131 + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  132 + <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  133 + <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
  134 + <maxFileSize>100MB</maxFileSize>
  135 + </timeBasedFileNamingAndTriggeringPolicy>
  136 + <!--日志文件保留天数-->
  137 + <maxHistory>15</maxHistory>
  138 + </rollingPolicy>
  139 + <!-- 此日志文件只记录ERROR级别的 -->
  140 + <filter class="ch.qos.logback.classic.filter.LevelFilter">
  141 + <level>ERROR</level>
  142 + <onMatch>ACCEPT</onMatch>
  143 + <onMismatch>DENY</onMismatch>
  144 + </filter>
  145 + </appender>
  146 +
  147 + <!--
  148 + <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
  149 + 以及指定<appender>。<logger>仅有一个name属性,
  150 + 一个可选的level和一个可选的addtivity属性。
  151 + name:用来指定受此logger约束的某一个包或者具体的某一个类。
  152 + level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
  153 + 还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
  154 + 如果未设置此属性,那么当前logger将会继承上级的级别。
  155 + addtivity:是否向上级logger传递打印信息。默认是true。
  156 + -->
  157 + <!--<logger name="org.springframework.web" level="info"/>-->
  158 + <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
  159 + <!--
  160 + 使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
  161 + 第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
  162 + 第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
  163 + -->
  164 +
  165 +
  166 + <!--
  167 + root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
  168 + level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
  169 + 不能设置为INHERITED或者同义词NULL。默认是DEBUG
  170 + 可以包含零个或多个元素,标识这个appender将会添加到这个logger。
  171 + -->
  172 +
  173 + <!--开发环境:打印控制台-->
  174 + <springProfile name="dev">
  175 + <root level="INFO">
  176 + <appender-ref ref="CONSOLE" />
  177 + <appender-ref ref="DEBUG_FILE" />
  178 + <appender-ref ref="INFO_FILE" />
  179 + <appender-ref ref="WARN_FILE" />
  180 + <appender-ref ref="ERROR_FILE" />
  181 + </root>
  182 + </springProfile>
  183 +
  184 + <!--生产环境:输出到文件-->
  185 + <springProfile name="pro">
  186 + <root level="info">
  187 + <appender-ref ref="CONSOLE" />
  188 + <appender-ref ref="DEBUG_FILE" />
  189 + <appender-ref ref="INFO_FILE" />
  190 + <appender-ref ref="ERROR_FILE" />
  191 + <appender-ref ref="WARN_FILE" />
  192 + </root>
  193 + </springProfile>
  194 +
  195 +</configuration>