Procházet zdrojové kódy

feat(zhaozhaonews): 新增网关模块 - admin-gateway(管理端网关/鉴权过滤/CORS) - app-gateway(用户端网关/鉴权过滤) - wemedia-gateway(自媒体网关/鉴权过滤)

WanJL před 3 týdny
rodič
revize
63872e3c1d
25 změnil soubory, kde provedl 1070 přidání a 0 odebrání
  1. 43 0
      zhaozhaonews/zhaozhaonews-gateway/pom.xml
  2. 20 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/pom.xml
  3. 21 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/java/com/zhaozhaonews/admin/gateway/AdminGateWayApplication.java
  4. 24 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/java/com/zhaozhaonews/admin/gateway/config/CorsConfig.java
  5. 63 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/java/com/zhaozhaonews/admin/gateway/filter/AuthorizeFilter.java
  6. 69 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/java/com/zhaozhaonews/admin/gateway/util/AppJwtUtil.java
  7. 10 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/resources/application.yml
  8. 12 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/resources/bootstrap.yml
  9. 52 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/resources/logback-spring.xml
  10. 20 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/pom.xml
  11. 13 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/java/com/zhaozhaonews/app/gateway/AppGateWayApplication.java
  12. 50 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/java/com/zhaozhaonews/app/gateway/config/CorsConfig.java
  13. 90 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/java/com/zhaozhaonews/app/gateway/filter/AuthorizeFilter.java
  14. 120 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/java/com/zhaozhaonews/app/gateway/util/AppJwtUtil.java
  15. 22 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/resources/application.yml
  16. 12 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/resources/bootstrap.yml
  17. 52 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/resources/logback-spring.xml
  18. 20 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/pom.xml
  19. 22 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/java/com/zhaozhaonews/wemedia/gateway/WemediaGatewayApplication.java
  20. 50 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/java/com/zhaozhaonews/wemedia/gateway/config/CorsConfig.java
  21. 90 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/java/com/zhaozhaonews/wemedia/gateway/filter/AuthorizeFilter.java
  22. 120 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/java/com/zhaozhaonews/wemedia/gateway/util/AppJwtUtil.java
  23. 10 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/resources/application.yml
  24. 13 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/resources/bootstrap.yml
  25. 52 0
      zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/resources/logback-spring.xml

+ 43 - 0
zhaozhaonews/zhaozhaonews-gateway/pom.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.zhaozhaonews</groupId>
+        <artifactId>zhaozhaonews</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>zhaozhaonews-gateway</artifactId>
+    <packaging>pom</packaging>
+    <modules>
+        <module>zhaozhaonews-app-gateway</module>
+        <module>zhaozhaonews-admin-gateway</module>
+        <module>zhaozhaonews-wemedia-gateway</module>
+    </modules>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 20 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/pom.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.zhaozhaonews</groupId>
+        <artifactId>zhaozhaonews-gateway</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>zhaozhaonews-admin-gateway</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+</project>

+ 21 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/java/com/zhaozhaonews/admin/gateway/AdminGateWayApplication.java

@@ -0,0 +1,21 @@
+package com.zhaozhaonews.admin.gateway;
+
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title AdminApplication
+ * @description
+ * @create 2025/2/13
+ */
+@SpringBootApplication
+@EnableDiscoveryClient
+public class AdminGateWayApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(AdminGateWayApplication.class, args);
+    }
+}

+ 24 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/java/com/zhaozhaonews/admin/gateway/config/CorsConfig.java

@@ -0,0 +1,24 @@
+package com.zhaozhaonews.admin.gateway.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.reactive.CorsWebFilter;
+import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
+
+@Configuration
+public class CorsConfig {
+
+    @Bean
+    public CorsWebFilter corsWebFilter() {
+        CorsConfiguration config = new CorsConfiguration();
+        config.addAllowedOrigin("*");
+        config.addAllowedMethod("*");
+        config.addAllowedHeader("*");
+        config.setAllowCredentials(true);
+
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", config);
+        return new CorsWebFilter(source);
+    }
+}

+ 63 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/java/com/zhaozhaonews/admin/gateway/filter/AuthorizeFilter.java

@@ -0,0 +1,63 @@
+package com.zhaozhaonews.admin.gateway.filter;
+
+import com.zhaozhaonews.admin.gateway.util.AppJwtUtil;
+import io.jsonwebtoken.Claims;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+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.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+@Component
+@Slf4j
+public class AuthorizeFilter implements Ordered, GlobalFilter {
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        ServerHttpRequest request = exchange.getRequest();
+        ServerHttpResponse response = exchange.getResponse();
+
+        String path = request.getURI().getPath();
+        if (path.contains("/login")) {
+            return chain.filter(exchange);
+        }
+
+        String token = request.getHeaders().getFirst("token");
+        if (StringUtils.isBlank(token)) {
+            response.setStatusCode(HttpStatus.UNAUTHORIZED);
+            return response.setComplete();
+        }
+
+        try {
+            Claims claimsBody = AppJwtUtil.getClaimsBody(token);
+            int result = AppJwtUtil.verifyToken(claimsBody);
+            if (result == 1 || result == 2) {
+                response.setStatusCode(HttpStatus.UNAUTHORIZED);
+                return response.setComplete();
+            }
+            Object userId = claimsBody.get("id");
+            if (userId != null) {
+                ServerHttpRequest mutatedRequest = request.mutate()
+                        .headers(httpHeaders -> httpHeaders.add("userId", userId.toString()))
+                        .build();
+                exchange = exchange.mutate().request(mutatedRequest).build();
+            }
+        } catch (Exception e) {
+            log.error("token解析失败", e);
+            response.setStatusCode(HttpStatus.UNAUTHORIZED);
+            return response.setComplete();
+        }
+
+        return chain.filter(exchange);
+    }
+
+    @Override
+    public int getOrder() {
+        return -10;
+    }
+}

+ 69 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/java/com/zhaozhaonews/admin/gateway/util/AppJwtUtil.java

@@ -0,0 +1,69 @@
+package com.zhaozhaonews.admin.gateway.util;
+
+import io.jsonwebtoken.*;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.*;
+
+public class AppJwtUtil {
+
+    private static final int TOKEN_TIME_OUT = 3_600;
+    private static final String TOKEN_ENCRY_KEY = "MzE1NDdjMjJjOTNkNDRjZjRlODg3ZGRhNjIwMGFmNWU";
+    private static final int REFRESH_TIME = 300;
+
+    public static String getToken(Long id){
+        Map<String, Object> claimMaps = new HashMap<>();
+        claimMaps.put("id",id);
+        long currentTime = System.currentTimeMillis();
+        return Jwts.builder()
+                .setId(UUID.randomUUID().toString())
+                .setIssuedAt(new Date(currentTime))
+                .setSubject("system")
+                .setIssuer("wanjl")
+                .setAudience("admin")
+                .compressWith(CompressionCodecs.GZIP)
+                .signWith(SignatureAlgorithm.HS512, generalKey())
+                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))
+                .addClaims(claimMaps)
+                .compact();
+    }
+
+    private static Jws<Claims> getJws(String token) {
+        return Jwts.parser()
+                .setSigningKey(generalKey())
+                .parseClaimsJws(token);
+    }
+
+    public static Claims getClaimsBody(String token) {
+        try {
+            return getJws(token).getBody();
+        }catch (ExpiredJwtException e){
+            return null;
+        }
+    }
+
+    public static int verifyToken(Claims claims) {
+        if(claims==null){
+            return 1;
+        }
+        try {
+            claims.getExpiration().before(new Date());
+            if((claims.getExpiration().getTime()-System.currentTimeMillis())>REFRESH_TIME*1000){
+                return -1;
+            }else {
+                return 0;
+            }
+        } catch (ExpiredJwtException ex) {
+            return 1;
+        }catch (Exception e){
+            return 2;
+        }
+    }
+
+    public static SecretKey generalKey() {
+        byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
+        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
+        return key;
+    }
+}

+ 10 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/resources/application.yml

@@ -0,0 +1,10 @@
+spring:
+  cloud:
+    gateway:
+      routes:
+        - id: admin-local
+          uri: lb://zhaozhaonews-admin
+          predicates:
+            - Path=/service_6001/**
+          filters:
+            - StripPrefix=1

+ 12 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/resources/bootstrap.yml

@@ -0,0 +1,12 @@
+server:
+  port: 6001
+spring:
+  application:
+    name: zhaozhaonews-admin-gateway
+  cloud:
+    nacos:
+      discovery:
+        server-addr: 192.168.200.129:8848
+      config:
+        server-addr: 192.168.200.129:8848
+        file-extension: yaml

+ 52 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-admin-gateway/src/main/resources/logback-spring.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="false" scan="true" scanPeriod="30 seconds">
+    <springProperty name="APP_NAME" source="spring.application.name" defaultValue="zhaozhaonews" />
+    <property name="LOG_HOME" value="${user.dir}/logs"/>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}) - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_HOME}/${APP_NAME}.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <FileNamePattern>${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+            <MaxFileSize>10MB</MaxFileSize>
+            <MaxHistory>30</MaxHistory>
+            <totalSizeCap>1GB</totalSizeCap>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_HOME}/error.log</file>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>ERROR</level>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <FileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</FileNamePattern>
+            <MaxHistory>30</MaxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <logger name="org.springframework" level="INFO"/>
+    <logger name="org.springframework.web" level="INFO"/>
+    <logger name="springfox" level="WARN"/>
+    <logger name="io.swagger" level="WARN"/>
+
+    <root level="INFO">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="FILE"/>
+        <appender-ref ref="ERROR_FILE"/>
+    </root>
+</configuration>

+ 20 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/pom.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.zhaozhaonews</groupId>
+        <artifactId>zhaozhaonews-gateway</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>zhaozhaonews-app-gateway</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+</project>

+ 13 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/java/com/zhaozhaonews/app/gateway/AppGateWayApplication.java

@@ -0,0 +1,13 @@
+package com.zhaozhaonews.app.gateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+@SpringBootApplication
+@EnableDiscoveryClient
+public class AppGateWayApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(AppGateWayApplication.class, args);
+    }
+}

+ 50 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/java/com/zhaozhaonews/app/gateway/config/CorsConfig.java

@@ -0,0 +1,50 @@
+package com.zhaozhaonews.app.gateway.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.reactive.CorsWebFilter;
+import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
+
+/**
+ * 跨域配置类(CORS Configuration)
+ *
+ * 本类用于配置APP端网关的跨域资源共享(CORS)策略,
+ * 解决浏览器同源策略限制下的跨域访问问题。
+ * 允许所有来源(Origin)、所有请求方法(Method)和所有请求头(Header)的跨域请求,
+ * 适用于开发环境或需要开放跨域访问的场景。
+ *
+ * @author WanJl
+ * @version 1.0
+ * @title CorsConfig
+ * @description APP网关跨域配置
+ */
+@Configuration
+public class CorsConfig {
+
+    /**
+     * 创建跨域过滤器实例
+     *
+     * 配置跨域规则:允许任意来源、任意请求方法、任意请求头,
+     * 并对所有路径(/**)生效。
+     *
+     * @return CorsWebFilter 响应式跨域过滤器
+     */
+    @Bean
+    public CorsWebFilter corsWebFilter() {
+        // 创建跨域配置对象
+        CorsConfiguration config = new CorsConfiguration();
+        // 允许所有来源的请求
+        config.addAllowedOrigin("*");
+        // 允许所有HTTP方法(GET、POST、PUT、DELETE等)
+        config.addAllowedMethod("*");
+        // 允许所有请求头
+        config.addAllowedHeader("*");
+
+        // 创建基于URL的跨域配置源
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        // 为所有路径注册跨域配置
+        source.registerCorsConfiguration("/**", config);
+        return new CorsWebFilter(source);
+    }
+}

+ 90 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/java/com/zhaozhaonews/app/gateway/filter/AuthorizeFilter.java

@@ -0,0 +1,90 @@
+package com.zhaozhaonews.app.gateway.filter;
+
+
+import com.zhaozhaonews.app.gateway.util.AppJwtUtil;
+import io.jsonwebtoken.Claims;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+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.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title AuthorizeFilter
+ * @description
+ * @create 2026/4/12
+ */
+@Component  //注册到spring容器
+@Slf4j
+public class AuthorizeFilter implements Ordered, GlobalFilter {
+    /**
+     * 设置全局过滤
+     *
+     * @param exchange
+     * @param chain
+     * @return
+     */
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        //1、获取request请求和response响应对象
+        ServerHttpRequest request = exchange.getRequest();
+        ServerHttpResponse response = exchange.getResponse();
+
+        //2、判断是否登录
+        if (request.getURI().getPath().contains("/login")) {
+            return chain.filter(exchange);//放行
+        }
+
+        //3、获取token
+        String token = request.getHeaders().getFirst("token");
+
+        //4、判断token是否存在
+        if (StringUtils.isBlank(token)) {
+            //设置响应状态为401 未授权
+            response.setStatusCode(HttpStatus.UNAUTHORIZED);
+            return response.setComplete();
+        }
+
+        //5、校验 JWT 并提取 userId 转发到请求头(下游服务通过拦截器获取)
+        try {
+            Claims claimsBody = AppJwtUtil.getClaimsBody(token);
+            int result = AppJwtUtil.verifyToken(claimsBody);
+            if (result == 1 || result == 2) {
+                response.setStatusCode(HttpStatus.UNAUTHORIZED);
+                return response.setComplete();
+            }
+            Object userId = claimsBody.get("id");
+            if (userId != null) {
+                ServerHttpRequest mutatedRequest = request.mutate()
+                        .headers(httpHeaders -> httpHeaders.add("userId", userId.toString()))
+                        .build();
+                exchange = exchange.mutate().request(mutatedRequest).build();
+            }
+        } catch (Exception e) {
+            log.error("token解析失败", e);
+            response.setStatusCode(HttpStatus.UNAUTHORIZED);
+            return response.setComplete();
+        }
+
+        //6、放行
+        return chain.filter(exchange);
+    }
+
+    /**
+     * 优先级设置,值越小,优先级越高
+     *
+     * @return
+     */
+    @Override
+    public int getOrder() {
+        return 0;
+    }
+}

+ 120 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/java/com/zhaozhaonews/app/gateway/util/AppJwtUtil.java

@@ -0,0 +1,120 @@
+package com.zhaozhaonews.app.gateway.util;
+
+import io.jsonwebtoken.*;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.*;
+
+public class AppJwtUtil {
+
+    // TOKEN的有效期一天(S)
+    private static final int TOKEN_TIME_OUT = 3_600;
+    // 加密KEY
+    // 字符串“wanjl”通过MD5加密,加密后内容进行Base64加密生成43位字符串。作为通用加密KEY
+    private static final String TOKEN_ENCRY_KEY = "MzE1NDdjMjJjOTNkNDRjZjRlODg3ZGRhNjIwMGFmNWU";
+    // 最小刷新间隔(S)
+    private static final int REFRESH_TIME = 300;
+
+    // 根据用户ID生成token
+    public static String getToken(Long id){
+        Map<String, Object> claimMaps = new HashMap<>();
+        claimMaps.put("id",id);
+        long currentTime = System.currentTimeMillis();
+        return Jwts.builder()
+                .setId(UUID.randomUUID().toString())
+                .setIssuedAt(new Date(currentTime))  //签发时间
+                .setSubject("system")  //说明
+                .setIssuer("wanjl") //签发者信息
+                .setAudience("app")  //接收用户
+                .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
+                .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
+                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))  //过期时间戳
+                .addClaims(claimMaps) //cla信息
+                .compact();
+    }
+
+    /**
+     * 获取token中的claims信息
+     *
+     * @param token
+     * @return
+     */
+    private static Jws<Claims> getJws(String token) {
+            return Jwts.parser()
+                    .setSigningKey(generalKey())
+                    .parseClaimsJws(token);
+    }
+
+    /**
+     * 获取payload body信息
+     *
+     * @param token
+     * @return
+     */
+    public static Claims getClaimsBody(String token) {
+        try {
+            return getJws(token).getBody();
+        }catch (ExpiredJwtException e){
+            return null;
+        }
+    }
+
+    /**
+     * 获取hearder body信息
+     *
+     * @param token
+     * @return
+     */
+    public static JwsHeader getHeaderBody(String token) {
+        return getJws(token).getHeader();
+    }
+
+    /**
+     * 是否过期
+     *
+     * @param claims
+     * @return -1:有效,0:有效,1:过期,2:过期
+     */
+    public static int verifyToken(Claims claims) {
+        if(claims==null){
+            return 1;
+        }
+        try {
+            claims.getExpiration()
+                    .before(new Date());
+            // 需要自动刷新TOKEN
+            if((claims.getExpiration().getTime()-System.currentTimeMillis())>REFRESH_TIME*1000){
+                return -1;
+            }else {
+                return 0;
+            }
+        } catch (ExpiredJwtException ex) {
+            return 1;
+        }catch (Exception e){
+            return 2;
+        }
+    }
+
+    /**
+     * 由字符串生成加密key
+     *
+     * @return
+     */
+    public static SecretKey generalKey() {
+        byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
+        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
+        return key;
+    }
+
+    public static void main(String[] args) {
+       /* Map map = new HashMap();
+        map.put("id","11");*/
+        System.out.println(AppJwtUtil.getToken(1102L));
+        Jws<Claims> jws = AppJwtUtil.getJws("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAADWLQQrDIBAA_7LnCO6qq8lvTGLA0BZhDW0o_Xs2h95mGOYLe68wQeDIHi0axMUan5bNzG4jM8YSCYOj4hkGqLnDhDx6pkDWDSDHrLec0svz7iKq7_zaH2r5WNVya8rl0_5nSvdZtSFa-l1VJYAxgwAAAA.3OHb1hwmchluFXRIzMhOY7PDiFVb6MjTDfP-jAv40R9rJGKugvcGoFgubN3ZEzzgcxc0JXVlRAYOeEzIS19NrA");
+        Claims claims = jws.getBody();
+        System.out.println(claims.get("id"));
+
+    }
+
+}

+ 22 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/resources/application.yml

@@ -0,0 +1,22 @@
+spring:
+  cloud:
+    gateway:
+      routes:
+        - id: user-local
+          uri: lb://zhaozhaonews-user
+          predicates:
+            - Path=/user/**
+          filters:
+            - StripPrefix=1
+        - id: article-local
+          uri: lb://zhaozhaonews-article
+          predicates:
+            - Path=/article/**
+          filters:
+            - StripPrefix=1
+        - id: behavior-local
+          uri: lb://zhaozhaonews-behavior
+          predicates:
+            - Path=/behavior/**
+          filters:
+            - StripPrefix=1

+ 12 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/resources/bootstrap.yml

@@ -0,0 +1,12 @@
+server:
+  port: 51601
+spring:
+  application:
+    name: zhaozhaonews-app-gateway
+  cloud:
+    nacos:
+      discovery:
+        server-addr: 192.168.200.129:8848
+      config:
+        server-addr: 192.168.200.129:8848
+        file-extension: yaml

+ 52 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-app-gateway/src/main/resources/logback-spring.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="false" scan="true" scanPeriod="30 seconds">
+    <springProperty name="APP_NAME" source="spring.application.name" defaultValue="zhaozhaonews" />
+    <property name="LOG_HOME" value="${user.dir}/logs"/>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}) - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_HOME}/${APP_NAME}.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <FileNamePattern>${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+            <MaxFileSize>10MB</MaxFileSize>
+            <MaxHistory>30</MaxHistory>
+            <totalSizeCap>1GB</totalSizeCap>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_HOME}/error.log</file>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>ERROR</level>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <FileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</FileNamePattern>
+            <MaxHistory>30</MaxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <logger name="org.springframework" level="INFO"/>
+    <logger name="org.springframework.web" level="INFO"/>
+    <logger name="springfox" level="WARN"/>
+    <logger name="io.swagger" level="WARN"/>
+
+    <root level="INFO">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="FILE"/>
+        <appender-ref ref="ERROR_FILE"/>
+    </root>
+</configuration>

+ 20 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/pom.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.zhaozhaonews</groupId>
+        <artifactId>zhaozhaonews-gateway</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>zhaozhaonews-wemedia-gateway</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+</project>

+ 22 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/java/com/zhaozhaonews/wemedia/gateway/WemediaGatewayApplication.java

@@ -0,0 +1,22 @@
+package com.zhaozhaonews.wemedia.gateway;
+
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title WemediaGatewayApplication
+ * @description
+ * @create 2026/4/26
+ */
+
+@SpringBootApplication
+@EnableDiscoveryClient
+public class WemediaGatewayApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(WemediaGatewayApplication.class, args);
+    }
+}

+ 50 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/java/com/zhaozhaonews/wemedia/gateway/config/CorsConfig.java

@@ -0,0 +1,50 @@
+package com.zhaozhaonews.wemedia.gateway.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.reactive.CorsWebFilter;
+import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
+
+/**
+ * 跨域配置类(CORS Configuration)
+ *
+ * 本类用于配置自媒体端网关的跨域资源共享(CORS)策略,
+ * 解决浏览器同源策略限制下的跨域访问问题。
+ * 允许所有来源(Origin)、所有请求方法(Method)和所有请求头(Header)的跨域请求,
+ * 适用于自媒体管理后台与网关之间的跨域通信场景。
+ *
+ * @author WanJl
+ * @version 1.0
+ * @title CorsConfig
+ * @description 自媒体网关跨域配置
+ */
+@Configuration
+public class CorsConfig {
+
+    /**
+     * 创建跨域过滤器实例
+     *
+     * 配置跨域规则:允许任意来源、任意请求方法、任意请求头,
+     * 并对所有路径(/**)生效。
+     *
+     * @return CorsWebFilter 响应式跨域过滤器
+     */
+    @Bean
+    public CorsWebFilter corsWebFilter() {
+        // 创建跨域配置对象
+        CorsConfiguration config = new CorsConfiguration();
+        // 允许所有来源的请求
+        config.addAllowedOrigin("*");
+        // 允许所有HTTP方法(GET、POST、PUT、DELETE等)
+        config.addAllowedMethod("*");
+        // 允许所有请求头
+        config.addAllowedHeader("*");
+
+        // 创建基于URL的跨域配置源
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        // 为所有路径注册跨域配置
+        source.registerCorsConfiguration("/**", config);
+        return new CorsWebFilter(source);
+    }
+}

+ 90 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/java/com/zhaozhaonews/wemedia/gateway/filter/AuthorizeFilter.java

@@ -0,0 +1,90 @@
+package com.zhaozhaonews.wemedia.gateway.filter;
+
+import com.zhaozhaonews.wemedia.gateway.util.AppJwtUtil;
+import io.jsonwebtoken.Claims;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+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.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+@Component
+@Slf4j
+public class AuthorizeFilter implements Ordered, GlobalFilter {
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        log.info("网关...开始过滤...");
+        //1.获取request和response对象
+        ServerHttpRequest request = exchange.getRequest();
+        ServerHttpResponse response = exchange.getResponse();
+
+        String path = request.getURI().getPath();
+        log.info("请求路径: {}", path);
+
+        //2.判断是否是登录(修正)
+        if(path.contains("/login/") || path.endsWith("/login")) {
+            log.info("自媒体用户登录....放行.....");
+            return chain.filter(exchange);
+        }
+        //3.获取token
+        String token = request.getHeaders().getFirst("token");
+
+        //4.判断token是否存在
+        if(StringUtils.isBlank(token)){
+            response.setStatusCode(HttpStatus.UNAUTHORIZED);
+            return response.setComplete();
+        }
+
+        //5.判断token是否有效
+        try {
+            Claims claimsBody = AppJwtUtil.getClaimsBody(token);
+            //是否是过期
+            // 0- 有效
+            // 1- 过期
+            // 2- 无效
+            int result = AppJwtUtil.verifyToken(claimsBody);
+            if(result == 1 || result  == 2){
+                response.setStatusCode(HttpStatus.UNAUTHORIZED);    //返回401
+                return response.setComplete();
+            }
+            //---新增代码--start--------------------
+            //从token的payload获取userId
+            //是用户登录的时候JWT的payload里面存了id的相关信息
+            Object userId = claimsBody.get("id");
+            //将userId放到请求头中
+            ServerHttpRequest serverHttpRequest = request.mutate()  //获取新的请求对象
+                    .headers(httpHeaders -> {   //获取请求头对象
+                        //设置请求头,把userId转换为字符串放进header中
+                        httpHeaders.add("userId", userId.toString());
+                    }).build();
+            //将新的请求对象放入到exchange中
+            exchange = exchange.mutate().request(serverHttpRequest).build();
+            //---新增代码--end-------------------------
+        } catch (Exception e) {
+            //-----修改异常处理代码--start-------
+            //异常的原因可能是:token过期、token无效、格式错误等等
+            log.error("token解析失败",e);
+            response.setStatusCode(HttpStatus.UNAUTHORIZED);    //返回401
+            return response.setComplete();
+            //-----修改异常处理代码--start--------------
+        }
+
+        //6.放行
+        return chain.filter(exchange);
+    }
+
+    /**
+     * 优先级设置  值越小  优先级越高
+     * @return
+     */
+    @Override
+    public int getOrder() {
+        return -10;
+    }
+}

+ 120 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/java/com/zhaozhaonews/wemedia/gateway/util/AppJwtUtil.java

@@ -0,0 +1,120 @@
+package com.zhaozhaonews.wemedia.gateway.util;
+
+import io.jsonwebtoken.*;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.*;
+
+public class AppJwtUtil {
+
+    // TOKEN的有效期一天(S)
+    private static final int TOKEN_TIME_OUT = 3_600;
+    // 加密KEY
+    // 字符串“wanjl”通过MD5加密,加密后内容进行Base64加密生成43位字符串。作为通用加密KEY
+    private static final String TOKEN_ENCRY_KEY = "MzE1NDdjMjJjOTNkNDRjZjRlODg3ZGRhNjIwMGFmNWU";
+    // 最小刷新间隔(S)
+    private static final int REFRESH_TIME = 300;
+
+    // 根据用户ID生成token
+    public static String getToken(Long id){
+        Map<String, Object> claimMaps = new HashMap<>();
+        claimMaps.put("id",id);
+        long currentTime = System.currentTimeMillis();
+        return Jwts.builder()
+                .setId(UUID.randomUUID().toString())
+                .setIssuedAt(new Date(currentTime))  //签发时间
+                .setSubject("system")  //说明
+                .setIssuer("wanjl") //签发者信息
+                .setAudience("wemedia")  //接收用户
+                .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
+                .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
+                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))  //过期时间戳
+                .addClaims(claimMaps) //cla信息
+                .compact();
+    }
+
+    /**
+     * 获取token中的claims信息
+     *
+     * @param token
+     * @return
+     */
+    private static Jws<Claims> getJws(String token) {
+            return Jwts.parser()
+                    .setSigningKey(generalKey())
+                    .parseClaimsJws(token);
+    }
+
+    /**
+     * 获取payload body信息
+     *
+     * @param token
+     * @return
+     */
+    public static Claims getClaimsBody(String token) {
+        try {
+            return getJws(token).getBody();
+        }catch (ExpiredJwtException e){
+            return null;
+        }
+    }
+
+    /**
+     * 获取hearder body信息
+     *
+     * @param token
+     * @return
+     */
+    public static JwsHeader getHeaderBody(String token) {
+        return getJws(token).getHeader();
+    }
+
+    /**
+     * 是否过期
+     *
+     * @param claims
+     * @return -1:有效,0:有效,1:过期,2:过期
+     */
+    public static int verifyToken(Claims claims) {
+        if(claims==null){
+            return 1;
+        }
+        try {
+            claims.getExpiration()
+                    .before(new Date());
+            // 需要自动刷新TOKEN
+            if((claims.getExpiration().getTime()-System.currentTimeMillis())>REFRESH_TIME*1000){
+                return -1;
+            }else {
+                return 0;
+            }
+        } catch (ExpiredJwtException ex) {
+            return 1;
+        }catch (Exception e){
+            return 2;
+        }
+    }
+
+    /**
+     * 由字符串生成加密key
+     *
+     * @return
+     */
+    public static SecretKey generalKey() {
+        byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
+        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
+        return key;
+    }
+
+    public static void main(String[] args) {
+       /* Map map = new HashMap();
+        map.put("id","11");*/
+        System.out.println(AppJwtUtil.getToken(1102L));
+        Jws<Claims> jws = AppJwtUtil.getJws("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAADWLQQrDIBAA_7LnCO6qq8lvTGLA0BZhDW0o_Xs2h95mGOYLe68wQeDIHi0axMUan5bNzG4jM8YSCYOj4hkGqLnDhDx6pkDWDSDHrLec0svz7iKq7_zaH2r5WNVya8rl0_5nSvdZtSFa-l1VJYAxgwAAAA.3OHb1hwmchluFXRIzMhOY7PDiFVb6MjTDfP-jAv40R9rJGKugvcGoFgubN3ZEzzgcxc0JXVlRAYOeEzIS19NrA");
+        Claims claims = jws.getBody();
+        System.out.println(claims.get("id"));
+
+    }
+
+}

+ 10 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/resources/application.yml

@@ -0,0 +1,10 @@
+spring:
+  cloud:
+    gateway:
+      routes:
+        - id: wemedia-local
+          uri: lb://zhaozhaonews-wemedia
+          predicates:
+            - Path=/wemedia/**
+          filters:
+            - StripPrefix=1

+ 13 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/resources/bootstrap.yml

@@ -0,0 +1,13 @@
+server:
+  port: 51602
+spring:
+  application:
+    name: zhaozhaonews-wemedia-gateway
+  cloud:
+    nacos:
+      discovery:
+        server-addr: 192.168.200.129:8848
+      config:
+        server-addr: 192.168.200.129:8848
+        file-extension: yaml
+

+ 52 - 0
zhaozhaonews/zhaozhaonews-gateway/zhaozhaonews-wemedia-gateway/src/main/resources/logback-spring.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="false" scan="true" scanPeriod="30 seconds">
+    <springProperty name="APP_NAME" source="spring.application.name" defaultValue="zhaozhaonews" />
+    <property name="LOG_HOME" value="${user.dir}/logs"/>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}) - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_HOME}/${APP_NAME}.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <FileNamePattern>${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+            <MaxFileSize>10MB</MaxFileSize>
+            <MaxHistory>30</MaxHistory>
+            <totalSizeCap>1GB</totalSizeCap>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${LOG_HOME}/error.log</file>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>ERROR</level>
+        </filter>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <FileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</FileNamePattern>
+            <MaxHistory>30</MaxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <logger name="org.springframework" level="INFO"/>
+    <logger name="org.springframework.web" level="INFO"/>
+    <logger name="springfox" level="WARN"/>
+    <logger name="io.swagger" level="WARN"/>
+
+    <root level="INFO">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="FILE"/>
+        <appender-ref ref="ERROR_FILE"/>
+    </root>
+</configuration>