6004 字
约 20 分钟
30
appendix_K
2026-09-26
无标签
附录K 网关服务模块源码详解
模块概览(面向 0 基础读者):
- 基于 Spring Cloud Gateway 统一接入、鉴权与限流。
- 集成 Spring Security、JWT 对请求做集中验证。
- 支持配置化路由、跨域、熔断以及审计日志。
核心目录:
xuecheng-plus-gateway/src/main/java/com/xuecheng/gateway/configxuecheng-plus-gateway/src/main/resourcesxuecheng-plus-gateway/pom.xml
文件:xuecheng-plus-gateway/main/com/xuecheng/gateway/config/GatewayAuthFilter.java
package com.xuecheng.gateway.config; // 声明类所在的包路径
import com.alibaba.fastjson.JSON; // 导入 JSON 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.apache.commons.lang.StringUtils; // 导入 StringUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.cloud.gateway.filter.GatewayFilterChain; // 导入 GatewayFilterChain 类
import org.springframework.cloud.gateway.filter.GlobalFilter; // 导入 GlobalFilter 类
import org.springframework.core.Ordered; // 导入 Ordered 类
import org.springframework.core.io.buffer.DataBuffer; // 导入 DataBuffer 类
import org.springframework.http.HttpStatus; // 导入 HttpStatus 类
import org.springframework.http.server.reactive.ServerHttpResponse; // 导入 ServerHttpResponse 类
import org.springframework.security.oauth2.common.OAuth2AccessToken; // 导入 OAuth2AccessToken 类
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; // 导入 InvalidTokenException 类
import org.springframework.security.oauth2.provider.token.TokenStore; // 导入 TokenStore 类
import org.springframework.stereotype.Component; // 导入 Component 类
import org.springframework.util.AntPathMatcher; // 导入 AntPathMatcher 类
import org.springframework.web.server.ServerWebExchange; // 导入 ServerWebExchange 类
import reactor.core.publisher.Mono; // 导入 Mono 类
import java.io.InputStream; // 导入 InputStream 类
import java.nio.charset.StandardCharsets; // 导入 StandardCharsets 类
import java.util.ArrayList; // 导入 ArrayList 类
import java.util.List; // 导入 List 类
import java.util.Properties; // 导入 Properties 类
import java.util.Set; // 导入 Set 类
/** // 保留原作者写的块注释
* @author Mr.M // 保留原作者写的块注释
* @version 1.0 // 保留原作者写的块注释
* @description 网关认证过虑器 // 保留原作者写的块注释
* @date 2022/9/27 12:10 // 保留原作者写的块注释
*/ // 保留原作者写的块注释
@Component // 使用注解 @Component
@Slf4j // 使用注解 @Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered { // 定义 class GatewayAuthFilter
//白名单 // 保留原作者写的行内注释
private static List<String> whitelist = null; // 把变量 whitelist 初始化为 null
static { // 代码逻辑
//加载白名单 // 保留原作者写的行内注释
try ( // 尝试执行可能抛出异常的代码
InputStream resourceAsStream = GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties"); // 把 GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties") 赋值给 resourceAsStream
) { // 代码逻辑
Properties properties = new Properties(); // 创建 Properties 对象并赋值给 properties
properties.load(resourceAsStream); // 执行语句
Set<String> strings = properties.stringPropertyNames(); // 把 properties.stringPropertyNames() 的返回值赋给 strings
whitelist= new ArrayList<>(strings); // 创建 ArrayList<> 对象并赋值给 whitelist
} catch (Exception e) { // 定义方法 catch(...)
log.error("加载/security-whitelist.properties出错:{}",e.getMessage()); // 执行语句
e.printStackTrace(); // 执行语句
} // 代码块结束
} // 代码块结束
@Autowired // 使用注解 @Autowired
private TokenStore tokenStore; // 执行语句
@Override // 使用注解 @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 定义方法 filter(...)
//请求的url // 保留原作者写的行内注释
String requestUrl = exchange.getRequest().getPath().value(); // 把 exchange.getRequest().getPath().value() 的返回值赋给 requestUrl
AntPathMatcher pathMatcher = new AntPathMatcher(); // 创建 AntPathMatcher 对象并赋值给 pathMatcher
//白名单放行 // 保留原作者写的行内注释
for (String url : whitelist) { // 定义方法 方法(...)
if (pathMatcher.match(url, requestUrl)) { // 定义方法 方法(...)
return chain.filter(exchange); // 返回 chain.filter(exchange)
} // 代码块结束
} // 代码块结束
//检查token是否存在 // 保留原作者写的行内注释
String token = getToken(exchange); // 把 getToken(exchange) 赋值给 token
if (StringUtils.isBlank(token)) { // 定义方法 方法(...)
return buildReturnMono("没有认证",exchange); // 返回 buildReturnMono("没有认证",exchange)
} // 代码块结束
//判断是否是有效的token // 保留原作者写的行内注释
OAuth2AccessToken oAuth2AccessToken; // 执行语句
try { // 尝试执行可能抛出异常的代码
oAuth2AccessToken = tokenStore.readAccessToken(token); // 把 tokenStore.readAccessToken(token) 赋值给 oAuth2AccessToken
boolean expired = oAuth2AccessToken.isExpired(); // 把 oAuth2AccessToken.isExpired() 的返回值赋给 expired
if (expired) { // 定义方法 方法(...)
return buildReturnMono("认证令牌已过期",exchange); // 返回 buildReturnMono("认证令牌已过期",exchange)
} // 代码块结束
return chain.filter(exchange); // 返回 chain.filter(exchange)
} catch (InvalidTokenException e) { // 定义方法 catch(...)
log.info("认证令牌无效: {}", token); // 执行语句
return buildReturnMono("认证令牌无效",exchange); // 返回 buildReturnMono("认证令牌无效",exchange)
} // 代码块结束
} // 代码块结束
/** // 保留原作者写的块注释
* 获取token // 保留原作者写的块注释
*/ // 保留原作者写的块注释
private String getToken(ServerWebExchange exchange) { // 定义方法 getToken(...)
String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization"); // 把 exchange.getRequest().getHeaders().getFirst("Authorization") 赋值给 tokenStr
if (StringUtils.isBlank(tokenStr)) { // 定义方法 方法(...)
return null; // 返回 null
} // 代码块结束
String token = tokenStr.split(" ")[1]; // 把 tokenStr.split(" ")[1] 赋值给 token
if (StringUtils.isBlank(token)) { // 定义方法 方法(...)
return null; // 返回 null
} // 代码块结束
return token; // 返回 token
} // 代码块结束
private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) { // 定义方法 buildReturnMono(...)
ServerHttpResponse response = exchange.getResponse(); // 把 exchange.getResponse() 的返回值赋给 response
String jsonString = JSON.toJSONString(new RestErrorResponse(error)); // 把 JSON.toJSONString(new RestErrorResponse(error)) 赋值给 jsonString
byte[] bits = jsonString.getBytes(StandardCharsets.UTF_8); // 把 jsonString.getBytes(StandardCharsets.UTF_8) 赋值给 bits
DataBuffer buffer = response.bufferFactory().wrap(bits); // 把 response.bufferFactory().wrap(bits) 赋值给 buffer
response.setStatusCode(HttpStatus.UNAUTHORIZED); // 执行语句
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); // 把 UTF-8") 赋值给 "application/json;charset
return response.writeWith(Mono.just(buffer)); // 返回 response.writeWith(Mono.just(buffer))
} // 代码块结束
@Override // 使用注解 @Override
public int getOrder() { // 定义方法 getOrder(...)
return 0; // 返回 0
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/main/com/xuecheng/gateway/config/RestErrorResponse.java
package com.xuecheng.gateway.config; // 声明类所在的包路径
import java.io.Serializable; // 导入 Serializable 类
/** // 保留原作者写的块注释
* 错误响应参数包装 // 保留原作者写的块注释
*/ // 保留原作者写的块注释
public class RestErrorResponse implements Serializable { // 定义 class RestErrorResponse
private String errMessage; // 执行语句
public RestErrorResponse(String errMessage){ // 定义方法 RestErrorResponse(...)
this.errMessage= errMessage; // 把 errMessage 赋值给 this.errMessage
} // 代码块结束
public String getErrMessage() { // 定义方法 getErrMessage(...)
return errMessage; // 返回 errMessage
} // 代码块结束
public void setErrMessage(String errMessage) { // 定义方法 setErrMessage(...)
this.errMessage = errMessage; // 把 errMessage 赋值给 this.errMessage
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/main/com/xuecheng/gateway/config/SecurityConfig.java
package com.xuecheng.gateway.config; // 声明类所在的包路径
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; // 导入 EnableWebFluxSecurity 类
import org.springframework.security.config.web.server.ServerHttpSecurity; // 导入 ServerHttpSecurity 类
import org.springframework.security.web.server.SecurityWebFilterChain; // 导入 SecurityWebFilterChain 类
/** // 保留原作者写的块注释
* @description 安全配置类 // 保留原作者写的块注释
* @author Mr.M // 保留原作者写的块注释
* @date 2022/9/27 12:07 // 保留原作者写的块注释
* @version 1.0 // 保留原作者写的块注释
*/ // 保留原作者写的块注释
@EnableWebFluxSecurity // 使用注解 @EnableWebFluxSecurity
@Configuration // 使用注解 @Configuration
public class SecurityConfig { // 定义 class SecurityConfig
//安全拦截配置 // 保留原作者写的行内注释
@Bean // 使用注解 @Bean
public SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) { // 定义方法 webFluxSecurityFilterChain(...)
return http.authorizeExchange() // 返回 http.authorizeExchange()
.pathMatchers("/**").permitAll() // 代码逻辑
.anyExchange().authenticated() // 代码逻辑
.and().csrf().disable().build(); // 执行语句
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/main/com/xuecheng/gateway/config/TokenConfig.java
package com.xuecheng.gateway.config; // 声明类所在的包路径
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.security.oauth2.provider.token.TokenStore; // 导入 TokenStore 类
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; // 导入 JwtAccessTokenConverter 类
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; // 导入 JwtTokenStore 类
/** // 保留原作者写的块注释
* @author Administrator // 保留原作者写的块注释
* @version 1.0 // 保留原作者写的块注释
**/ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
public class TokenConfig { // 定义 class TokenConfig
String SIGNING_KEY = "mq123"; // 把 "mq123" 赋值给 SIGNING_KEY
// @Bean // 保留原作者写的行内注释
// public TokenStore tokenStore() { // 保留原作者写的行内注释
// //使用内存存储令牌(普通令牌) // 保留原作者写的行内注释
// return new InMemoryTokenStore(); // 保留原作者写的行内注释
// } // 保留原作者写的行内注释
@Autowired // 使用注解 @Autowired
private JwtAccessTokenConverter accessTokenConverter; // 执行语句
@Bean // 使用注解 @Bean
public TokenStore tokenStore() { // 定义方法 tokenStore(...)
return new JwtTokenStore(accessTokenConverter()); // 返回 new JwtTokenStore(accessTokenConverter())
} // 代码块结束
@Bean // 使用注解 @Bean
public JwtAccessTokenConverter accessTokenConverter() { // 定义方法 accessTokenConverter(...)
JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); // 创建 JwtAccessTokenConverter 对象并赋值给 converter
converter.setSigningKey(SIGNING_KEY); // 执行语句
return converter; // 返回 converter
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/main/com/xuecheng/GatewayApplication.java
package com.xuecheng; // 声明类所在的包路径
import org.springframework.boot.SpringApplication; // 导入 SpringApplication 类
import org.springframework.boot.autoconfigure.SpringBootApplication; // 导入 SpringBootApplication 类
@SpringBootApplication // 使用注解 @SpringBootApplication
public class GatewayApplication { // 定义 class GatewayApplication
public static void main(String[] args) { // 定义方法 main(...)
SpringApplication.run(GatewayApplication.class, args); // 执行语句
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/pom.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- SQL 语句片段 -->
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <!-- SQL 语句片段 -->
<modelVersion>4.0.0</modelVersion> <!-- SQL 语句片段 -->
<parent> <!-- SQL 语句片段 -->
<groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
<artifactId>xuecheng-plus-parent</artifactId> <!-- SQL 语句片段 -->
<version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
<relativePath>../xuecheng-plus-parent</relativePath> <!-- SQL 语句片段 -->
</parent> <!-- SQL 语句片段 -->
<artifactId>xuecheng-plus-gateway</artifactId> <!-- SQL 语句片段 -->
<dependencies> <!-- SQL 语句片段 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>org.springframework.cloud</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-cloud-starter-security</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>org.springframework.cloud</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-cloud-starter-oauth2</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>org.projectlombok</groupId> <!-- SQL 语句片段 -->
<artifactId>lombok</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>com.alibaba</groupId> <!-- SQL 语句片段 -->
<artifactId>fastjson</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<!--网关-->
<dependency> <!-- SQL 语句片段 -->
<groupId>org.springframework.cloud</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-cloud-starter-gateway</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<!--服务发现中心-->
<dependency> <!-- SQL 语句片段 -->
<groupId>com.alibaba.cloud</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>com.alibaba.cloud</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>com.alibaba</groupId> <!-- SQL 语句片段 -->
<artifactId>fastjson</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>org.projectlombok</groupId> <!-- SQL 语句片段 -->
<artifactId>lombok</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<!-- 排除 Spring Boot 依赖的日志包冲突 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-boot-starter</artifactId> <!-- SQL 语句片段 -->
<exclusions> <!-- SQL 语句片段 -->
<exclusion> <!-- SQL 语句片段 -->
<groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-boot-starter-logging</artifactId> <!-- SQL 语句片段 -->
</exclusion> <!-- SQL 语句片段 -->
</exclusions> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
<!-- Spring Boot 集成 log4j2 -->
<dependency> <!-- SQL 语句片段 -->
<groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-boot-starter-log4j2</artifactId> <!-- SQL 语句片段 -->
</dependency> <!-- SQL 语句片段 -->
</dependencies> <!-- SQL 语句片段 -->
<build> <!-- SQL 语句片段 -->
<finalName>${project.artifactId}-${project.version}</finalName> <!-- SQL 语句片段 -->
<plugins> <!-- SQL 语句片段 -->
<plugin> <!-- SQL 语句片段 -->
<groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
<artifactId>spring-boot-maven-plugin</artifactId> <!-- SQL 语句片段 -->
<version>${spring-boot.version}</version> <!-- SQL 语句片段 -->
<executions> <!-- SQL 语句片段 -->
<execution> <!-- SQL 语句片段 -->
<goals> <!-- SQL 语句片段 -->
<goal>repackage</goal> <!-- SQL 语句片段 -->
</goals> <!-- SQL 语句片段 -->
</execution> <!-- SQL 语句片段 -->
</executions> <!-- SQL 语句片段 -->
</plugin> <!-- SQL 语句片段 -->
<plugin> <!-- SQL 语句片段 -->
<groupId>com.spotify</groupId> <!-- SQL 语句片段 -->
<artifactId>docker-maven-plugin</artifactId> <!-- SQL 语句片段 -->
<version>1.2.2</version> <!-- SQL 语句片段 -->
<configuration> <!-- SQL 语句片段 -->
<!--修改imageName节点的内容,改为私有仓库地址和端口,再加上镜像id和 TAG,我们要直接传到私服-->
<!--配置最后生成的镜像名,docker images里的,我们这边取项目名:版本-->
<!--<imageName>${project.artifactId}:${project.version}</imageName>-->
<imageName>192.168.101.65:5000/${project.artifactId}:${project.version}</imageName> <!-- SQL 语句片段 -->
<!--也可以通过以下方式定义image的tag信息。 -->
<!-- <imageTags>
<imageTag>${project.version}</imageTag> <!-- SQL 语句片段 -->
<!–build 时强制覆盖 tag,配合 imageTags 使用–> <!-- SQL 语句片段 -->
<forceTags>true</forceTags> <!-- SQL 语句片段 -->
<!–build 完成后,push 指定 tag 的镜像,配合 imageTags 使用–> <!-- SQL 语句片段 -->
<pushImageTag>true</pushImageTag> <!-- SQL 语句片段 -->
</imageTags>--> <!-- SQL 语句片段 -->
<baseImage>java:8u20</baseImage> <!-- SQL 语句片段 -->
<maintainer>docker_maven [email protected]</maintainer> <!-- SQL 语句片段 -->
<workdir>/root</workdir> <!-- SQL 语句片段 -->
<cmd>["java", "-version"]</cmd> <!-- SQL 语句片段 -->
<!--来指明Dockerfile文件的所在目录,如果配置了dockerDirectory则忽略baseImage,maintainer等配置-->
<!--<dockerDirectory>./</dockerDirectory>-->
<!--2375是docker的远程端口,插件生成镜像时连接docker,这里需要指定docker远程端口-->
<dockerHost>http://192.168.101.65:2375</dockerHost> <!-- SQL 语句片段 -->
<!--入口点,project.build.finalName就是project标签下的build标签下 的filename标签内容,testDocker-->
<!--相当于启动容器后,会自动执行java -jar ...-->
<entryPoint>["java", "-Dfile.encoding=utf-8","-jar", "/root/${project.build.finalName}.jar"]</entryPoint> <!-- SQL 语句片段 -->
<!--是否推送到docker私有仓库,旧版本插件要配置maven的settings文件。 -->
<pushImage>true</pushImage> <!-- SQL 语句片段 -->
<registryUrl>192.168.101.65:5000</registryUrl> <!-- 这里是复制 jar 包到 docker 容器指定目录配置 --> <!-- SQL 语句片段 -->
<resources> <!-- SQL 语句片段 -->
<resource> <!-- SQL 语句片段 -->
<targetPath>/root</targetPath> <!-- SQL 语句片段 -->
<directory>${project.build.directory}</directory> <!-- SQL 语句片段 -->
<!--把哪个文件上传到docker,相当于Dockerfile里的add app.jar /-->
<include>${project.build.finalName}.jar</include> <!-- 引用公共 SQL 片段 -->
</resource> <!-- SQL 语句片段 -->
</resources> <!-- SQL 语句片段 -->
</configuration> <!-- SQL 语句片段 -->
</plugin> <!-- SQL 语句片段 -->
</plugins> <!-- SQL 语句片段 -->
</build> <!-- SQL 语句片段 -->
</project> <!-- SQL 语句片段 -->
文件:xuecheng-plus-gateway/resources/bootstrap.yml
#微服务配置
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 192.168.101.65:8848
discovery:
namespace: dev402
group: xuecheng-plus-project
config:
namespace: dev402
group: xuecheng-plus-project
file-extension: yaml
refresh-enabled: true
shared-configs:
- data-id: logging-${spring.profiles.active}.yaml
group: xuecheng-plus-common
refresh: true
profiles:
active: dev
文件:xuecheng-plus-gateway/resources/log4j2-dev.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="180" packages="">
<properties>
<property name="logdir">logs</property>
<property name="PATTERN">%date{YYYY-MM-dd HH:mm:ss,SSS} %level [%thread][%file:%line] - %msg%n%throwable</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${PATTERN}"/>
</Console>
<RollingFile name="ErrorAppender" fileName="${logdir}/error.log"
filePattern="${logdir}/$${date:yyyy-MM-dd}/error.%d{yyyy-MM-dd-HH}.log" append="true">
<PatternLayout pattern="${PATTERN}"/>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
</RollingFile>
<RollingFile name="DebugAppender" fileName="${logdir}/info.log"
filePattern="${logdir}/$${date:yyyy-MM-dd}/info.%d{yyyy-MM-dd-HH}.log" append="true">
<PatternLayout pattern="${PATTERN}"/>
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
</RollingFile>
<!--异步appender-->
<Async name="AsyncAppender" includeLocation="true">
<AppenderRef ref="ErrorAppender"/>
<AppenderRef ref="DebugAppender"/>
</Async>
</Appenders>
<Loggers>
<!--过滤掉spring和mybatis的一些无用的debug信息-->
<logger name="org.springframework" level="INFO">
</logger>
<logger name="org.mybatis" level="INFO">
</logger>
<logger name="cn.itcast.wanxinp2p.consumer.mapper" level="DEBUG">
</logger>
<logger name="springfox" level="INFO">
</logger>
<logger name="org.apache.http" level="INFO">
</logger>
<logger name="com.netflix.discovery" level="INFO">
</logger>
<logger name="RocketmqCommon" level="INFO" >
</logger>
<logger name="RocketmqRemoting" level="INFO" >
</logger>
<logger name="RocketmqClient" level="WARN">
</logger>
<logger name="org.dromara.hmily" level="WARN">
</logger>
<logger name="org.dromara.hmily.lottery" level="WARN">
</logger>
<logger name="org.dromara.hmily.bonuspoint" level="WARN">
</logger>
<!--OFF 0-->
<!--FATAL 100-->
<!--ERROR 200-->
<!--WARN 300-->
<!--INFO 400-->
<!--DEBUG 500-->
<!--TRACE 600-->
<!--ALL Integer.MAX_VALUE-->
<Root level="DEBUG" includeLocation="true">
<AppenderRef ref="AsyncAppender"/>
<AppenderRef ref="Console"/>
<AppenderRef ref="DebugAppender"/>
</Root>
</Loggers>
</Configuration>
文件:xuecheng-plus-gateway/resources/security-whitelist.properties
/**=\u4E34\u65F6\u5168\u90E8\u6253\u5F00
/auth/**=\u8BA4\u8BC1\u5730\u5740
/content/open/**=\u5185\u5BB9\u7BA1\u7406\u516C\u5F00\u8BBF\u95EE\u63A5\u53E3
/media/open/**=\u5A92\u8D44\u7BA1\u7406\u516C\u5F00\u8BBF\u95EE\u63A5\u53E3
文件:xuecheng-plus-gateway/src/main/java/com/xuecheng/gateway/config/GatewayAuthFilter.java
package com.xuecheng.gateway.config; // 声明类所在的包路径
import com.alibaba.fastjson.JSON; // 导入 JSON 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.apache.commons.lang.StringUtils; // 导入 StringUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.cloud.gateway.filter.GatewayFilterChain; // 导入 GatewayFilterChain 类
import org.springframework.cloud.gateway.filter.GlobalFilter; // 导入 GlobalFilter 类
import org.springframework.core.Ordered; // 导入 Ordered 类
import org.springframework.core.io.buffer.DataBuffer; // 导入 DataBuffer 类
import org.springframework.http.HttpStatus; // 导入 HttpStatus 类
import org.springframework.http.server.reactive.ServerHttpResponse; // 导入 ServerHttpResponse 类
import org.springframework.security.oauth2.common.OAuth2AccessToken; // 导入 OAuth2AccessToken 类
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; // 导入 InvalidTokenException 类
import org.springframework.security.oauth2.provider.token.TokenStore; // 导入 TokenStore 类
import org.springframework.stereotype.Component; // 导入 Component 类
import org.springframework.util.AntPathMatcher; // 导入 AntPathMatcher 类
import org.springframework.web.server.ServerWebExchange; // 导入 ServerWebExchange 类
import reactor.core.publisher.Mono; // 导入 Mono 类
import java.io.InputStream; // 导入 InputStream 类
import java.nio.charset.StandardCharsets; // 导入 StandardCharsets 类
import java.util.ArrayList; // 导入 ArrayList 类
import java.util.List; // 导入 List 类
import java.util.Properties; // 导入 Properties 类
import java.util.Set; // 导入 Set 类
/** // 保留原作者写的块注释
* @author Mr.M // 保留原作者写的块注释
* @version 1.0 // 保留原作者写的块注释
* @description 网关认证过虑器 // 保留原作者写的块注释
* @date 2022/9/27 12:10 // 保留原作者写的块注释
*/ // 保留原作者写的块注释
@Component // 使用注解 @Component
@Slf4j // 使用注解 @Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered { // 定义 class GatewayAuthFilter
//白名单 // 保留原作者写的行内注释
private static List<String> whitelist = null; // 把变量 whitelist 初始化为 null
static { // 代码逻辑
//加载白名单 // 保留原作者写的行内注释
try ( // 尝试执行可能抛出异常的代码
InputStream resourceAsStream = GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties"); // 把 GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties") 赋值给 resourceAsStream
) { // 代码逻辑
Properties properties = new Properties(); // 创建 Properties 对象并赋值给 properties
properties.load(resourceAsStream); // 执行语句
Set<String> strings = properties.stringPropertyNames(); // 把 properties.stringPropertyNames() 的返回值赋给 strings
whitelist= new ArrayList<>(strings); // 创建 ArrayList<> 对象并赋值给 whitelist
} catch (Exception e) { // 定义方法 catch(...)
log.error("加载/security-whitelist.properties出错:{}",e.getMessage()); // 执行语句
e.printStackTrace(); // 执行语句
} // 代码块结束
} // 代码块结束
@Autowired // 使用注解 @Autowired
private TokenStore tokenStore; // 执行语句
@Override // 使用注解 @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 定义方法 filter(...)
//请求的url // 保留原作者写的行内注释
String requestUrl = exchange.getRequest().getPath().value(); // 把 exchange.getRequest().getPath().value() 的返回值赋给 requestUrl
AntPathMatcher pathMatcher = new AntPathMatcher(); // 创建 AntPathMatcher 对象并赋值给 pathMatcher
//白名单放行 // 保留原作者写的行内注释
for (String url : whitelist) { // 定义方法 方法(...)
if (pathMatcher.match(url, requestUrl)) { // 定义方法 方法(...)
return chain.filter(exchange); // 返回 chain.filter(exchange)
} // 代码块结束
} // 代码块结束
//检查token是否存在 // 保留原作者写的行内注释
String token = getToken(exchange); // 把 getToken(exchange) 赋值给 token
if (StringUtils.isBlank(token)) { // 定义方法 方法(...)
return buildReturnMono("没有认证",exchange); // 返回 buildReturnMono("没有认证",exchange)
} // 代码块结束
//判断是否是有效的token // 保留原作者写的行内注释
OAuth2AccessToken oAuth2AccessToken; // 执行语句
try { // 尝试执行可能抛出异常的代码
oAuth2AccessToken = tokenStore.readAccessToken(token); // 把 tokenStore.readAccessToken(token) 赋值给 oAuth2AccessToken
boolean expired = oAuth2AccessToken.isExpired(); // 把 oAuth2AccessToken.isExpired() 的返回值赋给 expired
if (expired) { // 定义方法 方法(...)
return buildReturnMono("认证令牌已过期",exchange); // 返回 buildReturnMono("认证令牌已过期",exchange)
} // 代码块结束
return chain.filter(exchange); // 返回 chain.filter(exchange)
} catch (InvalidTokenException e) { // 定义方法 catch(...)
log.info("认证令牌无效: {}", token); // 执行语句
return buildReturnMono("认证令牌无效",exchange); // 返回 buildReturnMono("认证令牌无效",exchange)
} // 代码块结束
} // 代码块结束
/** // 保留原作者写的块注释
* 获取token // 保留原作者写的块注释
*/ // 保留原作者写的块注释
private String getToken(ServerWebExchange exchange) { // 定义方法 getToken(...)
String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization"); // 把 exchange.getRequest().getHeaders().getFirst("Authorization") 赋值给 tokenStr
if (StringUtils.isBlank(tokenStr)) { // 定义方法 方法(...)
return null; // 返回 null
} // 代码块结束
String token = tokenStr.split(" ")[1]; // 把 tokenStr.split(" ")[1] 赋值给 token
if (StringUtils.isBlank(token)) { // 定义方法 方法(...)
return null; // 返回 null
} // 代码块结束
return token; // 返回 token
} // 代码块结束
private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) { // 定义方法 buildReturnMono(...)
ServerHttpResponse response = exchange.getResponse(); // 把 exchange.getResponse() 的返回值赋给 response
String jsonString = JSON.toJSONString(new RestErrorResponse(error)); // 把 JSON.toJSONString(new RestErrorResponse(error)) 赋值给 jsonString
byte[] bits = jsonString.getBytes(StandardCharsets.UTF_8); // 把 jsonString.getBytes(StandardCharsets.UTF_8) 赋值给 bits
DataBuffer buffer = response.bufferFactory().wrap(bits); // 把 response.bufferFactory().wrap(bits) 赋值给 buffer
response.setStatusCode(HttpStatus.UNAUTHORIZED); // 执行语句
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); // 把 UTF-8") 赋值给 "application/json;charset
return response.writeWith(Mono.just(buffer)); // 返回 response.writeWith(Mono.just(buffer))
} // 代码块结束
@Override // 使用注解 @Override
public int getOrder() { // 定义方法 getOrder(...)
return 0; // 返回 0
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/src/main/java/com/xuecheng/gateway/config/RestErrorResponse.java
package com.xuecheng.gateway.config; // 声明类所在的包路径
import java.io.Serializable; // 导入 Serializable 类
/** // 保留原作者写的块注释
* 错误响应参数包装 // 保留原作者写的块注释
*/ // 保留原作者写的块注释
public class RestErrorResponse implements Serializable { // 定义 class RestErrorResponse
private String errMessage; // 执行语句
public RestErrorResponse(String errMessage){ // 定义方法 RestErrorResponse(...)
this.errMessage= errMessage; // 把 errMessage 赋值给 this.errMessage
} // 代码块结束
public String getErrMessage() { // 定义方法 getErrMessage(...)
return errMessage; // 返回 errMessage
} // 代码块结束
public void setErrMessage(String errMessage) { // 定义方法 setErrMessage(...)
this.errMessage = errMessage; // 把 errMessage 赋值给 this.errMessage
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/src/main/java/com/xuecheng/gateway/config/SecurityConfig.java
package com.xuecheng.gateway.config; // 声明类所在的包路径
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; // 导入 EnableWebFluxSecurity 类
import org.springframework.security.config.web.server.ServerHttpSecurity; // 导入 ServerHttpSecurity 类
import org.springframework.security.web.server.SecurityWebFilterChain; // 导入 SecurityWebFilterChain 类
/** // 保留原作者写的块注释
* @description 安全配置类 // 保留原作者写的块注释
* @author Mr.M // 保留原作者写的块注释
* @date 2022/9/27 12:07 // 保留原作者写的块注释
* @version 1.0 // 保留原作者写的块注释
*/ // 保留原作者写的块注释
@EnableWebFluxSecurity // 使用注解 @EnableWebFluxSecurity
@Configuration // 使用注解 @Configuration
public class SecurityConfig { // 定义 class SecurityConfig
//安全拦截配置 // 保留原作者写的行内注释
@Bean // 使用注解 @Bean
public SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) { // 定义方法 webFluxSecurityFilterChain(...)
return http.authorizeExchange() // 返回 http.authorizeExchange()
.pathMatchers("/**").permitAll() // 代码逻辑
.anyExchange().authenticated() // 代码逻辑
.and().csrf().disable().build(); // 执行语句
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/src/main/java/com/xuecheng/gateway/config/TokenConfig.java
package com.xuecheng.gateway.config; // 声明类所在的包路径
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.security.oauth2.provider.token.TokenStore; // 导入 TokenStore 类
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; // 导入 JwtAccessTokenConverter 类
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; // 导入 JwtTokenStore 类
/** // 保留原作者写的块注释
* @author Administrator // 保留原作者写的块注释
* @version 1.0 // 保留原作者写的块注释
**/ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
public class TokenConfig { // 定义 class TokenConfig
String SIGNING_KEY = "mq123"; // 把 "mq123" 赋值给 SIGNING_KEY
// @Bean // 保留原作者写的行内注释
// public TokenStore tokenStore() { // 保留原作者写的行内注释
// //使用内存存储令牌(普通令牌) // 保留原作者写的行内注释
// return new InMemoryTokenStore(); // 保留原作者写的行内注释
// } // 保留原作者写的行内注释
@Autowired // 使用注解 @Autowired
private JwtAccessTokenConverter accessTokenConverter; // 执行语句
@Bean // 使用注解 @Bean
public TokenStore tokenStore() { // 定义方法 tokenStore(...)
return new JwtTokenStore(accessTokenConverter()); // 返回 new JwtTokenStore(accessTokenConverter())
} // 代码块结束
@Bean // 使用注解 @Bean
public JwtAccessTokenConverter accessTokenConverter() { // 定义方法 accessTokenConverter(...)
JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); // 创建 JwtAccessTokenConverter 对象并赋值给 converter
converter.setSigningKey(SIGNING_KEY); // 执行语句
return converter; // 返回 converter
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/src/main/java/com/xuecheng/GatewayApplication.java
package com.xuecheng; // 声明类所在的包路径
import org.springframework.boot.SpringApplication; // 导入 SpringApplication 类
import org.springframework.boot.autoconfigure.SpringBootApplication; // 导入 SpringBootApplication 类
@SpringBootApplication // 使用注解 @SpringBootApplication
public class GatewayApplication { // 定义 class GatewayApplication
public static void main(String[] args) { // 定义方法 main(...)
SpringApplication.run(GatewayApplication.class, args); // 执行语句
} // 代码块结束
} // 代码块结束
文件:xuecheng-plus-gateway/src/main/resources/bootstrap.yml
#微服务配置
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 192.168.101.65:8848
discovery:
namespace: dev402
group: xuecheng-plus-project
config:
namespace: dev402
group: xuecheng-plus-project
file-extension: yaml
refresh-enabled: true
shared-configs:
- data-id: logging-${spring.profiles.active}.yaml
group: xuecheng-plus-common
refresh: true
profiles:
active: dev
文件:xuecheng-plus-gateway/src/main/resources/log4j2-dev.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<Configuration monitorInterval="180" packages=""> <!-- SQL 语句片段 -->
<properties> <!-- SQL 语句片段 -->
<property name="logdir">logs</property> <!-- SQL 语句片段 -->
<property name="PATTERN">%date{YYYY-MM-dd HH:mm:ss,SSS} %level [%thread][%file:%line] - %msg%n%throwable</property> <!-- SQL 语句片段 -->
</properties> <!-- SQL 语句片段 -->
<Appenders> <!-- SQL 语句片段 -->
<Console name="Console" target="SYSTEM_OUT"> <!-- SQL 语句片段 -->
<PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
</Console> <!-- SQL 语句片段 -->
<RollingFile name="ErrorAppender" fileName="${logdir}/error.log" <!-- SQL 语句片段 -->
filePattern="${logdir}/$${date:yyyy-MM-dd}/error.%d{yyyy-MM-dd-HH}.log" append="true"> <!-- SQL 语句片段 -->
<PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <!-- SQL 语句片段 -->
<Policies> <!-- SQL 语句片段 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true" /> <!-- SQL 语句片段 -->
</Policies> <!-- SQL 语句片段 -->
</RollingFile> <!-- SQL 语句片段 -->
<RollingFile name="DebugAppender" fileName="${logdir}/info.log" <!-- SQL 语句片段 -->
filePattern="${logdir}/$${date:yyyy-MM-dd}/info.%d{yyyy-MM-dd-HH}.log" append="true"> <!-- SQL 语句片段 -->
<PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> <!-- SQL 语句片段 -->
<Policies> <!-- SQL 语句片段 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true" /> <!-- SQL 语句片段 -->
</Policies> <!-- SQL 语句片段 -->
</RollingFile> <!-- SQL 语句片段 -->
<!--异步appender-->
<Async name="AsyncAppender" includeLocation="true"> <!-- SQL 语句片段 -->
<AppenderRef ref="ErrorAppender"/> <!-- SQL 语句片段 -->
<AppenderRef ref="DebugAppender"/> <!-- SQL 语句片段 -->
</Async> <!-- SQL 语句片段 -->
</Appenders> <!-- SQL 语句片段 -->
<Loggers> <!-- SQL 语句片段 -->
<!--过滤掉spring和mybatis的一些无用的debug信息-->
<logger name="org.springframework" level="INFO"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="org.mybatis" level="INFO"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="cn.itcast.wanxinp2p.consumer.mapper" level="DEBUG"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="springfox" level="INFO"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="org.apache.http" level="INFO"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="com.netflix.discovery" level="INFO"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="RocketmqCommon" level="INFO" > <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="RocketmqRemoting" level="INFO" > <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="RocketmqClient" level="WARN"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="org.dromara.hmily" level="WARN"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="org.dromara.hmily.lottery" level="WARN"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<logger name="org.dromara.hmily.bonuspoint" level="WARN"> <!-- SQL 语句片段 -->
</logger> <!-- SQL 语句片段 -->
<!--OFF 0-->
<!--FATAL 100-->
<!--ERROR 200-->
<!--WARN 300-->
<!--INFO 400-->
<!--DEBUG 500-->
<!--TRACE 600-->
<!--ALL Integer.MAX_VALUE-->
<Root level="DEBUG" includeLocation="true"> <!-- SQL 语句片段 -->
<AppenderRef ref="AsyncAppender"/> <!-- SQL 语句片段 -->
<AppenderRef ref="Console"/> <!-- SQL 语句片段 -->
<AppenderRef ref="DebugAppender"/> <!-- SQL 语句片段 -->
</Root> <!-- SQL 语句片段 -->
</Loggers> <!-- SQL 语句片段 -->
</Configuration> <!-- SQL 语句片段 -->
评论
0 条
还没有评论,先写一条吧。