diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 0000000..31d5b2b
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,9 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(mvn clean compile:*)",
+ "Bash(mvn spring-javaformat:apply)",
+ "Bash(cat:*)"
+ ]
+ }
+}
diff --git a/.idea/CoolRequestCommonStatePersistent.xml b/.idea/CoolRequestCommonStatePersistent.xml
index 591a8f6..6435996 100644
--- a/.idea/CoolRequestCommonStatePersistent.xml
+++ b/.idea/CoolRequestCommonStatePersistent.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..d3dcd6e
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ mysql.8
+ true
+ true
+ com.mysql.cj.jdbc.Driver
+ jdbc:mysql://10.0.0.10/aioj_dev
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/db-forest-config.xml b/.idea/db-forest-config.xml
new file mode 100644
index 0000000..d0a62c0
--- /dev/null
+++ b/.idea/db-forest-config.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CLAUDE.md b/CLAUDE.md
index bec41f0..2fe2580 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -2,195 +2,122 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-## Project Overview
+## Codebase Overview
-This is a microservices-based Online Judge (OJ) system with AI integration called AIOJ (AI Online Judge). It's built with Spring Boot 3.5.7 and Spring Cloud 2025.0.0, following a modular Maven multi-module architecture.
+This is a microservices architecture for an Online Judge (OJ) system, built on **Spring Boot 3.5.7**. The project uses Maven as the build tool and follows a modular monorepo structure, with clearly separated core modules and service modules.
-## Common Development Commands
+### Core Modules
-### Building the Project
-```bash
-# Build entire project
-mvn clean install
+The `aioj-backend-common` directory contains shared components and utilities used across all service modules:
-# Build with specific environment profile
-mvn clean install -P dev # Development (default)
-mvn clean install -P test # Testing
-mvn clean install -P prod # Production
+1. **aioj-backend-common-bom**
+ Bill of materials for centralized dependency management. This ensures consistent versions of all external libraries across all modules.
-# Format code according to Spring standards
-mvn spring-javaformat:apply
+2. **aioj-backend-common-core**
+ Core utilities and Spring framework extensions:
+ - `BannerApplicationRunner`: Custom application banner display during startup
+ - `SpringContextHolder`: Spring context accessor for non-Spring-managed classes
+ - `JavaTimeModule`: Jackson module for Java 8+ time API support
+ - Common constants and enumerations
+ - Application-level configurations and auto-configuration classes
-# Build Docker images using Jib
-mvn clean package jib:build
+3. **aioj-backend-common-feign**
+ Feign client configurations for seamless inter-service communication:
+ - Auto-configuration for Feign clients with default settings
+ - `@EnableAIOJFeignClients` annotation for enabling Feign clients with predefined base packages
+ - Feign interceptors and error handling mechanisms
-# Or use Maven wrapper
-./mvnw clean install
-```
+4. **aioj-backend-common-log**
+ Aspect-oriented programming (AOP) based logging framework:
+ - `SysLogAspect`: Aspect for logging system operations (controller methods, service calls)
+ - `SysLogEvent` and `SysLogListener`: Event-driven logging mechanism
+ - `SysLogUtils`: Utility class for creating and managing log entries
+ - Configuration properties for logging behavior
-### Running Services
-Each service runs on different ports:
-- Gateway: 8085
-- Other services: configured via Nacos
+5. **aioj-backend-common-mybatis**
+ MyBatis ORM framework extensions:
+ - Auto-fill functionality for `createTime` and `updateTime` fields
+ - Pagination interceptor implementation
+ - MyBatis configuration auto-configuration classes
-Run individual services from their respective directories:
-```bash
-cd aioj-backend-gateway
-mvn spring-boot:run
+6. **aioj-backend-common-starter**
+ Auto-configuration starters for easily enabling common features in service modules.
-# Or with specific profile
-mvn spring-boot:run -Dspring.profiles.active=dev
-```
-### Database Setup
-1. Create databases using the provided script:
-```bash
-mysql -u root -p < db/create_db.sql
-```
-This creates three databases: `aioj_dev`, `aioj_test`, and `aioj_prod`
+### Service Modules
-## Architecture Overview
+The service modules represent the individual microservices that make up the system:
-### Microservices Architecture
-The system consists of seven main services:
+1. **aioj-backend-auth**
+ Authentication and authorization service:
+ - JWT-based authentication with `JwtAuthenticationFilter`
+ - Security configuration with Spring Security
+ - User login, token generation, and validation
+ - Permission verification and access control
-1. **aioj-backend-gateway** (Port 8085)
- - API Gateway using Spring Cloud Gateway
- - Routes requests to appropriate services
- - Built with WebFlux for reactive programming
+2. **aioj-backend-gateway**
+ API gateway for request routing and filtering:
+ - Request routing to appropriate service modules
+ - Authentication token validation before forwarding requests
+ - Rate limiting and request filtering mechanisms
-2. **aioj-backend-auth**
- - OAuth2 authentication and authorization service
- - Manages user credentials and tokens
+3. **aioj-backend-judge-service**
+ Code judge service (under development):
+ - Will handle code submission, compilation, and execution
+ - Support for multiple programming languages
+ - Test case validation and result return
-3. **aioj-backend-user-service**
- - User management and profiles
- - Handles registration, login, profile updates
- - Integrates with Redis for session management
+4. **aioj-backend-user-service**
+ User management service:
+ - User registration, profile management, and information retrieval
+ - User role and permission assignment
+ - Integration with the auth service for authentication
-4. **aioj-backend-question-service**
- - Problem/question management
- - Handles problem storage and retrieval
-
-5. **aioj-backend-judge-service**
- - Core OJ functionality for code execution
- - Supports multiple programming languages
+5. **aioj-backend-question-service**
+ Question bank service (under development):
+ - Will manage programming problems, test cases, and problem categories
+ - Support for problem difficulty levels and tags
+ - Integration with the judge service for problem submission
6. **aioj-backend-ai-service**
- - AI integration for enhanced features
- - Code analysis and automated feedback
+ AI-related functionality service (under development):
+ - Will provide AI-assisted features like problem recommendation, code analysis, etc.
-7. **aioj-backend-upms** (User Permission Management System)
- - Role-based access control
- - Permission management
+7. **aioj-backend-upms**
+ User, permission, and menu management service:
+ - Low-level user and permission management
+ - Menu and resource access control
+ - Integration with other services for authorization
-### Common Modules
-- **aioj-backend-common**: Shared utilities with sub-modules:
- - `core`: Core utilities and configurations
- - `log`: Custom logging implementation
- - `starter`: Auto-configuration starters
- - `mybatis`: Database access layer
- - `feign`: HTTP client for service communication
- - `bom`: Bill of Materials for dependency management
+## Commonly Used Commands
-## Technology Stack
-
-### Core Technologies
-- **Java 17**
-- **Spring Boot 3.5.7**
-- **Spring Cloud 2025.0.0**
-- **Spring Cloud Alibaba 2025.0.0.0**
-- **Maven** for build management
-
-### Database & Persistence
-- **MySQL 9.4.0** as primary database
-- **MyBatis-Plus 3.5.14** for ORM
-- **Redis** for caching and session management
-
-### Cloud & Infrastructure
-- **Nacos** for service discovery and configuration (server: 10.0.0.10:8848)
-- **Spring Cloud Gateway** for API routing
-- **Docker** with Jib plugin for containerization
-- **Sentinel** for circuit breaking
-
-### Security
-- **Spring Security 6.5.6** with OAuth2
-- JWT token-based authentication
-- Role-based access control
-
-### API Documentation
-- **Knife4j** (OpenAPI 3) integrated across services
-
-## Configuration Management
-
-### Environment-Specific Configuration
-Three environments are supported:
-- `dev` (development, default)
-- `test` (testing)
-- `prod` (production)
-
-Configuration files:
-- `bootstrap.yml` - Nacos service discovery configuration
-- `application.yml` - Main application configuration
-- `application-{env}.yml` - Environment-specific settings
-
-### Nacos Integration
-All services use Nacos for:
-- Service discovery
-- Configuration management
-- Centralized properties management
-
-Default Nacos configuration:
-```yaml
-spring:
- cloud:
- nacos:
- discovery:
- server-addr: 10.0.0.10:8848
- username: nacos
- password: nacos
-```
-
-## Database Schema
-
-### Environment Databases
-- Development: `aioj_dev`
-- Testing: `aioj_test`
-- Production: `aioj_prod`
-
-All databases use UTF-8 character set with `utf8mb4_general_ci` collation.
-
-## Development Guidelines
+### Build
+- **Build the entire project**: `mvn clean compile`
+- **Build with tests**: `mvn clean install`
+- **Build a single module**: `mvn clean compile -pl aioj-backend-auth`
### Code Formatting
-- Uses Spring JavaFormat plugin for consistent code style
-- IDE plugin available: https://repo1.maven.org/maven2/io/spring/javaformat/spring-javaformat-intellij-idea-plugin
-- Run `mvn spring-javaformat:apply` before commits
+- **Format code**: `mvn spring-javaformat:apply`
+- **Check code format**: `mvn spring-javaformat:check`
-### Docker Integration
-- Jib plugin configured for container builds
-- Target registry: `10.0.0.3/aioj/{service-name}:{version}`
-- JVM memory configured: -Xms512m -Xmx512m
+### Testing
+- **Run all tests**: `mvn test`
+- **Run tests for a single module**: `mvn test -pl aioj-backend-user-service`
-### Service Communication
-- Uses OpenFeign for inter-service communication
-- Load balancing with Spring Cloud LoadBalancer
-- Circuit breaking with Sentinel
+### Running Services
+- **Run a service locally**: Use Spring Boot's `Application` class directly in IDE or use `mvn spring-boot:run -pl `
+- **Example**: `mvn spring-boot:run -pl aioj-backend-auth`
-### Logging
-- Custom logging implementation in `aioj-backend-common-log`
-- Integrates with Spring Security for context logging
-- Uses Hutool utilities for enhanced logging
+## Architecture Highlights
-## Testing
-- Spring Boot Test framework included
-- Spring Security Test for authentication testing
-- Test structure is evolving - check individual modules for test coverage
+- **Authentication**: JWT-based authentication implemented in `aioj-backend-auth` with `JwtAuthenticationFilter`
+- **Logging**: Aspect-oriented logging with `SysLogAspect` in `aioj-backend-common-log`
+- **Database**: MyBatis with auto-fill for create/update times implemented in `common-mybatis`
+- **Inter-service communication**: Feign clients with auto-configuration from `common-feign`
+- **Banner**: Custom application banner system in `common-core`
-## Important Notes
+## Key Entry Points
-1. **Service Dependencies**: Services depend on Nacos for discovery - ensure Nacos is running before starting services
-2. **Database Setup**: Run the database creation script before first run
-3. **Port Configuration**: Gateway runs on 8085, other services are dynamically registered
-4. **Environment Profiles**: Default is `dev` - use appropriate profiles for different environments
-5. **Configuration Management**: Most configuration is externalized to Nacos - check Nacos for service-specific settings
\ No newline at end of file
+- **Gateway Application**: `aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/AIOJGatewayApplication.java`
+- **Auth Application**: `aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/AIOJAuthApplication.java`
+- **User Service Application**: `aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/UserServiceApplication.java`
\ No newline at end of file
diff --git a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/config/SecurityConfiguration.java b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/config/SecurityConfiguration.java
index f80b8ff..33aa51c 100644
--- a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/config/SecurityConfiguration.java
+++ b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/config/SecurityConfiguration.java
@@ -1,5 +1,7 @@
package cn.meowrain.aioj.backend.auth.config;
+import cn.meowrain.aioj.backend.auth.filter.JwtAuthenticationFilter;
+import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
@@ -10,11 +12,15 @@ import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
+@RequiredArgsConstructor
public class SecurityConfiguration {
+ private final JwtAuthenticationFilter jwtAuthenticationFilter;
+
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
@@ -24,7 +30,8 @@ public class SecurityConfiguration {
"/v3/api-docs/**", "/favicon.ico")
.permitAll()
.anyRequest()
- .authenticated());
+ .authenticated())
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
diff --git a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/filter/JwtAuthenticationFilter.java b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/filter/JwtAuthenticationFilter.java
index b2aa727..8cb6ca0 100644
--- a/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/filter/JwtAuthenticationFilter.java
+++ b/aioj-backend-auth/src/main/java/cn/meowrain/aioj/backend/auth/filter/JwtAuthenticationFilter.java
@@ -1,11 +1,106 @@
package cn.meowrain.aioj.backend.auth.filter;
+import cn.meowrain.aioj.backend.auth.service.AuthService;
+import cn.meowrain.aioj.backend.auth.utils.JwtUtil;
+import io.jsonwebtoken.Claims;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
/**
- * JWT拦截器
+ * JWT认证过滤器
+ * 拦截所有请求,验证JWT Token
*/
@Component
-public class JwtAuthenticationFilter {
+@RequiredArgsConstructor
+@Slf4j
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+ private final JwtUtil jwtUtil;
+ private final AuthService authService;
+
+ private static final String TOKEN_PREFIX = "Bearer ";
+ private static final String HEADER_NAME = "Authorization";
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+
+ try {
+ String token = extractTokenFromRequest(request);
+
+ if (StringUtils.hasText(token) && jwtUtil.isTokenValid(token)) {
+ Claims claims = jwtUtil.parseClaims(token);
+ Authentication authentication = createAuthentication(claims);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+
+ log.debug("JWT Authentication successful for user: {}", claims.getSubject());
+ } else {
+ log.debug("No valid JWT token found in request");
+ }
+ } catch (Exception e) {
+ log.error("JWT Authentication failed", e);
+ SecurityContextHolder.clearContext();
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ /**
+ * 从请求中提取JWT Token
+ */
+ private String extractTokenFromRequest(HttpServletRequest request) {
+ String bearerToken = request.getHeader(HEADER_NAME);
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
+ return bearerToken.substring(TOKEN_PREFIX.length());
+ }
+ return null;
+ }
+
+ /**
+ * 根据JWT Claims创建Authentication对象
+ */
+ private Authentication createAuthentication(Claims claims) {
+ String userId = claims.getSubject();
+ String userName = claims.get("userName", String.class);
+ String role = claims.get("role", String.class);
+
+ // 创建权限列表
+ List authorities = Collections.singletonList(
+ new SimpleGrantedAuthority("ROLE_" + (role != null ? role : "USER"))
+ );
+
+ // 创建认证对象
+ UsernamePasswordAuthenticationToken authentication =
+ new UsernamePasswordAuthenticationToken(userId, null, authorities);
+
+ return authentication;
+ }
+
+ @Override
+ protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
+ String path = request.getRequestURI();
+ // 跳过不需要JWT验证的路径
+ return path.startsWith("/v1/auth/") ||
+ path.startsWith("/doc.html") ||
+ path.startsWith("/swagger-ui/") ||
+ path.startsWith("/swagger-resources/") ||
+ path.startsWith("/webjars/") ||
+ path.startsWith("/v3/api-docs/") ||
+ path.equals("/favicon.ico");
+ }
}
diff --git a/aioj-backend-common/aioj-backend-common-core/pom.xml b/aioj-backend-common/aioj-backend-common-core/pom.xml
index e3d07b1..b9055e7 100644
--- a/aioj-backend-common/aioj-backend-common-core/pom.xml
+++ b/aioj-backend-common/aioj-backend-common-core/pom.xml
@@ -1,7 +1,7 @@
+ 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">
4.0.0
cn.meowrain
@@ -63,5 +63,9 @@
org.springframework.boot
spring-boot-starter-validation
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
\ No newline at end of file
diff --git a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/banner/BannerApplicationRunner.java b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/banner/BannerApplicationRunner.java
index 5444d01..5e01ca6 100644
--- a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/banner/BannerApplicationRunner.java
+++ b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/banner/BannerApplicationRunner.java
@@ -14,43 +14,47 @@ import java.time.format.DateTimeFormatter;
@Slf4j
@RequiredArgsConstructor
public class BannerApplicationRunner implements ApplicationRunner {
- private final Environment env;
- @Value("${spring.application.name:unknown}")
- private String appName;
- @Override
- public void run(ApplicationArguments args) throws Exception {
- // Active profiles
- String profiles = String.join(",", env.getActiveProfiles());
- if (profiles.isEmpty()) {
- profiles = "default";
- }
+ private final Environment env;
- // Port
- String port = env.getProperty("server.port", "unknown");
+ @Value("${spring.application.name:unknown}")
+ private String appName;
- // JVM info
- String jvm = System.getProperty("java.version") + " (" + System.getProperty("java.vendor") + ")";
+ @Override
+ public void run(ApplicationArguments args) throws Exception {
+ // Active profiles
+ String profiles = String.join(",", env.getActiveProfiles());
+ if (profiles.isEmpty()) {
+ profiles = "default";
+ }
- // PID
- String pid = ManagementFactory.getRuntimeMXBean().getPid() + "";
+ // Port
+ String port = env.getProperty("server.port", "unknown");
- // Time
- String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ // JVM info
+ String jvm = System.getProperty("java.version") + " (" + System.getProperty("java.vendor") + ")";
- // Git commit id (如果没有 git.properties,不会报错)
- String gitCommit = env.getProperty("git.commit.id.abbrev", "N/A");
+ // PID
+ String pid = ManagementFactory.getRuntimeMXBean().getPid() + "";
- printBanner(appName, profiles, port, jvm, pid, time, gitCommit);
- }
- private void printBanner(String appName, String profiles, String port, String jvm, String pid, String time,
- String gitCommit) {
+ // Time
+ String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+ // Git commit id (如果没有 git.properties,不会报错)
+ String gitCommit = env.getProperty("git.commit.id.abbrev", "N/A");
+
+ printBanner(appName, profiles, port, jvm, pid, time, gitCommit);
+ }
+
+ private void printBanner(String appName, String profiles, String port, String jvm, String pid, String time,
+ String gitCommit) {
+
+ String banner = "\n" + "------------------------------------------------------------\n"
+ + " ✨AI Online Judge✨ - " + appName + "\n" + " Environment : " + profiles + "\n" + " Port : "
+ + port + "\n" + " Git Commit : " + gitCommit + "\n" + " JVM : " + jvm + "\n"
+ + " PID : " + pid + "\n" + " Started At : " + time + "\n"
+ + "------------------------------------------------------------\n";
+ System.out.println(banner);
+ }
- String banner = "\n" + "------------------------------------------------------------\n"
- + " ✨AI Online Judge✨ - " + appName + "\n" + " Environment : " + profiles + "\n" + " Port : "
- + port + "\n" + " Git Commit : " + gitCommit + "\n" + " JVM : " + jvm + "\n"
- + " PID : " + pid + "\n" + " Started At : " + time + "\n"
- + "------------------------------------------------------------\n";
- System.out.println(banner);
- }
}
diff --git a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/banner/config/AIOJBannerAutoConfiguration.java b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/banner/config/AIOJBannerAutoConfiguration.java
index 7f1bc17..13b01ad 100644
--- a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/banner/config/AIOJBannerAutoConfiguration.java
+++ b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/banner/config/AIOJBannerAutoConfiguration.java
@@ -7,8 +7,10 @@ import org.springframework.core.env.Environment;
@AutoConfiguration
public class AIOJBannerAutoConfiguration {
- @Bean
- public BannerApplicationRunner bannerApplicationRunner(Environment env) {
- return new BannerApplicationRunner(env);
- }
+
+ @Bean
+ public BannerApplicationRunner bannerApplicationRunner(Environment env) {
+ return new BannerApplicationRunner(env);
+ }
+
}
diff --git a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/constants/ServiceNameConstants.java b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/constants/ServiceNameConstants.java
index d0b440f..a4e7ca1 100644
--- a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/constants/ServiceNameConstants.java
+++ b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/constants/ServiceNameConstants.java
@@ -4,18 +4,20 @@ package cn.meowrain.aioj.backend.framework.core.constants;
* 全局服务名称
*/
public class ServiceNameConstants {
- /**
- * 用户服务 SERVICE NAME
- */
- public static final String USER_SERVICE = "user-service";
- /**
- * 认证服务 SERVICE NAME
- */
- public static final String AUTH_SERVICE = "auth-service";
+ /**
+ * 用户服务 SERVICE NAME
+ */
+ public static final String USER_SERVICE = "user-service";
+
+ /**
+ * 认证服务 SERVICE NAME
+ */
+ public static final String AUTH_SERVICE = "auth-service";
+
+ /**
+ * UPMS模块
+ */
+ public static final String UPMS_SERVICE = "upms-service";
- /**
- * UPMS模块
- */
- public static final String UPMS_SERVICE = "upms-service";
}
diff --git a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/exception/handler/GlobalExceptionHandler.java b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/exception/handler/GlobalExceptionHandler.java
index f4b6232..b6e01f5 100644
--- a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/exception/handler/GlobalExceptionHandler.java
+++ b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/exception/handler/GlobalExceptionHandler.java
@@ -5,6 +5,8 @@ import cn.meowrain.aioj.backend.framework.core.web.Result;
import cn.meowrain.aioj.backend.framework.core.web.Results;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
@@ -12,6 +14,8 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.stereotype.Component;
+
import java.util.List;
/**
@@ -19,12 +23,18 @@ import java.util.List;
*/
@Slf4j
@RestControllerAdvice
+@Order(Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {
+ // 加这个构造器,启动看日志
+ public GlobalExceptionHandler() {
+ System.out.println("===== 自定义异常处理器已加载 =====");
+ }
/**
* 捕获所有参数错误,然后统一捕获并且抛出
+ *
* @param request {@link HttpServletRequest}
- * @param ex {@link org.springframework.validation.method.MethodValidationException}
+ * @param ex {@link org.springframework.validation.method.MethodValidationException}
* @return {@link Result}
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@@ -32,9 +42,9 @@ public class GlobalExceptionHandler {
BindingResult bindingResult = ex.getBindingResult();
// 收集所有错误字段
List errorMessages = bindingResult.getFieldErrors()
- .stream()
- .map(FieldError::getDefaultMessage)
- .toList();
+ .stream()
+ .map(FieldError::getDefaultMessage)
+ .toList();
String exceptionMessage = String.join(",", errorMessages);
log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionMessage);
return Results.paramsValidFailure();
@@ -42,8 +52,9 @@ public class GlobalExceptionHandler {
/**
* 抽象异常捕获其
+ *
* @param request {@link HttpServletRequest}
- * @param ex {@link AbstractException}
+ * @param ex {@link AbstractException}
* @return {@link Result}
*/
@ExceptionHandler(value = { AbstractException.class })
@@ -74,6 +85,7 @@ public class GlobalExceptionHandler {
/**
* 获取请求URL
+ *
* @param request {@link HttpServletRequest}
* @return String
*/
diff --git a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/jackson/JavaTimeModule.java b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/jackson/JavaTimeModule.java
index 92ca9b0..69b1f4b 100644
--- a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/jackson/JavaTimeModule.java
+++ b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/jackson/JavaTimeModule.java
@@ -10,7 +10,6 @@ import java.io.Serial;
import java.time.*;
import java.time.format.DateTimeFormatter;
-
public class JavaTimeModule extends SimpleModule {
@Serial
@@ -19,7 +18,7 @@ public class JavaTimeModule extends SimpleModule {
/**
* JavaTimeModule构造函数,用于初始化时间序列化和反序列化规则
*/
- public JavaTimeModule() {
+ public JavaTimeModule() {
super(PackageVersion.VERSION);
// ======================= 时间序列化规则 ===============================
diff --git a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/utils/SpringContextHolder.java b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/utils/SpringContextHolder.java
index 126b038..587d180 100644
--- a/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/utils/SpringContextHolder.java
+++ b/aioj-backend-common/aioj-backend-common-core/src/main/java/cn/meowrain/aioj/backend/framework/core/utils/SpringContextHolder.java
@@ -13,65 +13,69 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
-public class SpringContextHolder implements ApplicationContextAware, EnvironmentAware, DisposableBean {
- private static ApplicationContext applicationContext = null;
+public class SpringContextHolder implements ApplicationContextAware, EnvironmentAware, DisposableBean {
- private static Environment environment = null;
- /**
- * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
- */
- public static T getBean(String name) {
- return (T) applicationContext.getBean(name);
- }
+ private static ApplicationContext applicationContext = null;
- /**
- * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
- */
- public static T getBean(Class requiredType) {
- return applicationContext.getBean(requiredType);
- }
+ private static Environment environment = null;
- @Override
- @SneakyThrows
- public void destroy() throws Exception {
- clearHolder();
- }
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ public static T getBean(String name) {
+ return (T) applicationContext.getBean(name);
+ }
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- SpringContextHolder.applicationContext = applicationContext;
- }
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ public static T getBean(Class requiredType) {
+ return applicationContext.getBean(requiredType);
+ }
- @Override
- public void setEnvironment(Environment environment) {
- SpringContextHolder.environment = environment;
- }
+ @Override
+ @SneakyThrows
+ public void destroy() throws Exception {
+ clearHolder();
+ }
- /**
- * 清除SpringContextHolder中的ApplicationContext为Null.
- */
- public static void clearHolder() {
- if (log.isDebugEnabled()) {
- log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
- }
- applicationContext = null;
- }
- /**
- * 发布事件
- * @param event
- */
- public static void publishEvent(ApplicationEvent event) {
- if (applicationContext == null) {
- return;
- }
- applicationContext.publishEvent(event);
- }
- /**
- * 是否是微服务
- * @return boolean
- */
- public static boolean isMicro() {
- return environment.getProperty("spring.cloud.nacos.discovery.enabled", Boolean.class, true);
- }
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ SpringContextHolder.applicationContext = applicationContext;
+ }
+
+ @Override
+ public void setEnvironment(Environment environment) {
+ SpringContextHolder.environment = environment;
+ }
+
+ /**
+ * 清除SpringContextHolder中的ApplicationContext为Null.
+ */
+ public static void clearHolder() {
+ if (log.isDebugEnabled()) {
+ log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
+ }
+ applicationContext = null;
+ }
+
+ /**
+ * 发布事件
+ * @param event
+ */
+ public static void publishEvent(ApplicationEvent event) {
+ if (applicationContext == null) {
+ return;
+ }
+ applicationContext.publishEvent(event);
+ }
+
+ /**
+ * 是否是微服务
+ * @return boolean
+ */
+ public static boolean isMicro() {
+ return environment.getProperty("spring.cloud.nacos.discovery.enabled", Boolean.class, true);
+ }
}
diff --git a/aioj-backend-common/aioj-backend-common-feign/src/main/java/cn/meowrain/aioj/backend/framework/feign/FeignAutoConfiguration.java b/aioj-backend-common/aioj-backend-common-feign/src/main/java/cn/meowrain/aioj/backend/framework/feign/FeignAutoConfiguration.java
index 7f34be2..25f160f 100644
--- a/aioj-backend-common/aioj-backend-common-feign/src/main/java/cn/meowrain/aioj/backend/framework/feign/FeignAutoConfiguration.java
+++ b/aioj-backend-common/aioj-backend-common-feign/src/main/java/cn/meowrain/aioj/backend/framework/feign/FeignAutoConfiguration.java
@@ -4,4 +4,5 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
@AutoConfiguration
public class FeignAutoConfiguration {
+
}
diff --git a/aioj-backend-common/aioj-backend-common-feign/src/main/java/cn/meowrain/aioj/backend/framework/feign/annotation/EnableAIOJFeignClients.java b/aioj-backend-common/aioj-backend-common-feign/src/main/java/cn/meowrain/aioj/backend/framework/feign/annotation/EnableAIOJFeignClients.java
index 86290f6..10336f7 100644
--- a/aioj-backend-common/aioj-backend-common-feign/src/main/java/cn/meowrain/aioj/backend/framework/feign/annotation/EnableAIOJFeignClients.java
+++ b/aioj-backend-common/aioj-backend-common-feign/src/main/java/cn/meowrain/aioj/backend/framework/feign/annotation/EnableAIOJFeignClients.java
@@ -9,5 +9,7 @@ import java.lang.annotation.*;
@Documented
@EnableFeignClients
public @interface EnableAIOJFeignClients {
- String[] basePackages() default {};
+
+ String[] basePackages() default {};
+
}
diff --git a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/aspect/SysLogAspect.java b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/aspect/SysLogAspect.java
index 99b6917..cf18321 100644
--- a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/aspect/SysLogAspect.java
+++ b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/aspect/SysLogAspect.java
@@ -20,6 +20,7 @@ import org.springframework.expression.EvaluationContext;
@Slf4j
@RequiredArgsConstructor
public class SysLogAspect {
+
/**
* 环绕通知方法,用于处理系统日志记录
* @param point 连接点对象
@@ -29,7 +30,7 @@ public class SysLogAspect {
*/
@Around("@annotation(sysLog)")
@SneakyThrows
- public Object around(ProceedingJoinPoint point,SysLog sysLog) {
+ public Object around(ProceedingJoinPoint point, SysLog sysLog) {
String strClassName = point.getTarget().getClass().getName();
String strMethodName = point.getSignature().getName();
log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);
@@ -76,4 +77,5 @@ public class SysLogAspect {
return obj;
}
+
}
diff --git a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/config/AIOJLogPropertiesConfiguration.java b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/config/AIOJLogPropertiesConfiguration.java
index dd90fd4..940e5f5 100644
--- a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/config/AIOJLogPropertiesConfiguration.java
+++ b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/config/AIOJLogPropertiesConfiguration.java
@@ -24,11 +24,10 @@ public class AIOJLogPropertiesConfiguration {
*/
private Integer maxLength = 20000;
- /**
- * 放行字段,password,mobile,idcard,phone
- */
- @Value("${log.exclude-fields:password,mobile,idcard,phone}")
- private List excludeFields;
-
+ /**
+ * 放行字段,password,mobile,idcard,phone
+ */
+ @Value("${log.exclude-fields:password,mobile,idcard,phone}")
+ private List excludeFields;
}
diff --git a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/event/SysLogEvent.java b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/event/SysLogEvent.java
index 0934408..7770a36 100644
--- a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/event/SysLogEvent.java
+++ b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/event/SysLogEvent.java
@@ -1,6 +1,5 @@
package cn.meowrain.aioj.backend.framework.log.event;
-
import cn.meowrain.aioj.backend.upms.api.entity.SysLog;
import org.springframework.context.ApplicationEvent;
diff --git a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/event/SysLogListener.java b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/event/SysLogListener.java
index d0c4618..0d2a879 100644
--- a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/event/SysLogListener.java
+++ b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/event/SysLogListener.java
@@ -38,38 +38,40 @@ public class SysLogListener implements InitializingBean {
SysLog sysLog = new SysLog();
BeanUtils.copyProperties(source, sysLog);
- // json 格式刷参数放在异步中处理,提升性能
- if (Objects.nonNull(source.getBody())) {
- String params = objectMapper.writeValueAsString(source.getBody());
- sysLog.setParams(StrUtil.subPre(params, logProperties.getMaxLength()));
- }
+ // json 格式刷参数放在异步中处理,提升性能
+ if (Objects.nonNull(source.getBody())) {
+ String params = objectMapper.writeValueAsString(source.getBody());
+ sysLog.setParams(StrUtil.subPre(params, logProperties.getMaxLength()));
+ }
- remoteLogService.saveLog(sysLog);
+ remoteLogService.saveLog(sysLog);
}
- /**
- * 在 Bean 初始化后执行,用于初始化 ObjectMapper
- * @throws Exception
- */
+ /**
+ * 在 Bean 初始化后执行,用于初始化 ObjectMapper
+ * @throws Exception
+ */
@Override
public void afterPropertiesSet() throws Exception {
- //给 ObjectMapper 添加 MixIn(用于过滤字段)
- objectMapper.addMixIn(Object.class, PropertyFilterMixIn.class);
- String[] ignorableFieldNames = logProperties.getExcludeFields().toArray(new String[0]);
+ // 给 ObjectMapper 添加 MixIn(用于过滤字段)
+ objectMapper.addMixIn(Object.class, PropertyFilterMixIn.class);
+ String[] ignorableFieldNames = logProperties.getExcludeFields().toArray(new String[0]);
+
+ FilterProvider filters = new SimpleFilterProvider().addFilter("filter properties by name",
+ SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames));
+ objectMapper.setFilterProvider(filters);
+ objectMapper.registerModule(new JavaTimeModule());
+ }
+
+ /**
+ * 属性过滤混合类:用于通过名称过滤属性
+ *
+ * @author lengleng
+ * @date 2025/05/31
+ */
+ @JsonFilter("filter properties by name")
+ class PropertyFilterMixIn {
- FilterProvider filters = new SimpleFilterProvider().addFilter("filter properties by name",
- SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames));
- objectMapper.setFilterProvider(filters);
- objectMapper.registerModule(new JavaTimeModule());
}
- /**
- * 属性过滤混合类:用于通过名称过滤属性
- *
- * @author lengleng
- * @date 2025/05/31
- */
- @JsonFilter("filter properties by name")
- class PropertyFilterMixIn {
- }
}
diff --git a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/utils/SysLogUtils.java b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/utils/SysLogUtils.java
index 1e146f0..1754f37 100644
--- a/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/utils/SysLogUtils.java
+++ b/aioj-backend-common/aioj-backend-common-log/src/main/java/cn/meowrain/aioj/backend/framework/log/utils/SysLogUtils.java
@@ -29,14 +29,13 @@ import java.util.Objects;
public final class SysLogUtils {
-
/**
* 获取系统日志事件源
* @return 系统日志事件源对象
*/
- public static SysLogEventSource getSysLog() {
+ public static SysLogEventSource getSysLog() {
HttpServletRequest request = ((ServletRequestAttributes) Objects
- .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
+ .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
SysLogEventSource sysLog = new SysLogEventSource();
sysLog.setLogType(LogTypeEnum.NORMAL.getType());
sysLog.setRequestUri(URLUtil.getPath(request.getRequestURI()));
@@ -47,7 +46,8 @@ public final class SysLogUtils {
sysLog.setServiceId(SpringUtil.getProperty("spring.application.name"));
// get 参数脱敏
- AIOJLogPropertiesConfiguration logProperties = SpringContextHolder.getBean(AIOJLogPropertiesConfiguration.class);
+ AIOJLogPropertiesConfiguration logProperties = SpringContextHolder
+ .getBean(AIOJLogPropertiesConfiguration.class);
Map paramsMap = MapUtil.removeAny(new HashMap<>(request.getParameterMap()),
ArrayUtil.toArray(logProperties.getExcludeFields(), String.class));
sysLog.setParams(HttpUtil.toParams(paramsMap));
@@ -74,7 +74,7 @@ public final class SysLogUtils {
* @param 返回泛型
* @return 参数值
*/
- public static T getValue(EvaluationContext context, String key, Class clazz) {
+ public static T getValue(EvaluationContext context, String key, Class clazz) {
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
Expression expression = spelExpressionParser.parseExpression(key);
return expression.getValue(context, clazz);
diff --git a/aioj-backend-gateway/pom.xml b/aioj-backend-gateway/pom.xml
index 1506346..657e0a8 100644
--- a/aioj-backend-gateway/pom.xml
+++ b/aioj-backend-gateway/pom.xml
@@ -1,7 +1,7 @@
+ 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">
4.0.0
cn.meowrain
@@ -50,17 +50,14 @@
com.alibaba.csp
sentinel-datasource-extension
-
+
com.github.xiaoymin
knife4j-openapi3-jakarta-spring-boot-starter
+ 4.5.0
-
- org.springframework.cloud
- spring-cloud-starter-bootstrap
- 4.3.0
-
cn.meowrain
aioj-backend-common-core
diff --git a/aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/filter/AuthGlobalFilter.java b/aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/filter/AuthGlobalFilter.java
index 6f3f861..6de7761 100644
--- a/aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/filter/AuthGlobalFilter.java
+++ b/aioj-backend-gateway/src/main/java/cn/meowrain/aioj/backend/gateway/filter/AuthGlobalFilter.java
@@ -19,7 +19,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
-import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.reactive.function.client.WebClient;
@@ -27,7 +26,6 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
-import java.util.List;
import java.util.Objects;
@Slf4j
@@ -44,15 +42,6 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
private final ObjectMapper objectMapper = new ObjectMapper();
- /**
- * 不需要认证的路径
- */
- private static final String[] DEFAULT_WHITE_LIST = {
- "/api/v1/auth/login",
- "/api/v1/auth/register",
- "/api/v1/auth/refresh",
- "/api/v1/user/info"
- };
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
@@ -60,6 +49,7 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
String path = request.getURI().getPath();
log.info("Auth filter processing request: {}", path);
+ log.info("Loaded white list from config: {}", gatewayPropertiesConfiguration.getWhiteList());
// 检查是否在白名单中
if (isWhiteListPath(path)) {
@@ -97,13 +87,6 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
* 检查路径是否在白名单中
*/
private boolean isWhiteListPath(String path) {
- // 先检查默认白名单
- for (String whitePath : DEFAULT_WHITE_LIST) {
- if (antPathMatcher.match(whitePath, path)) {
- return true;
- }
- }
-
// 检查配置文件中的白名单
if (gatewayPropertiesConfiguration.getWhiteList() != null && !gatewayPropertiesConfiguration.getWhiteList().isEmpty()) {
for (String whitePath : gatewayPropertiesConfiguration.getWhiteList()) {
@@ -122,7 +105,7 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
private Mono validateToken(String token) {
return webClientBuilder.build()
.post()
- .uri("lb://auth-service/api/v1/auth/validate")
+ .uri("lb://auth-service/v1/auth/validate")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.retrieve()
diff --git a/aioj-backend-gateway/src/main/resources/application-dev.yml b/aioj-backend-gateway/src/main/resources/application-dev.yml
index f3af779..f6453a4 100644
--- a/aioj-backend-gateway/src/main/resources/application-dev.yml
+++ b/aioj-backend-gateway/src/main/resources/application-dev.yml
@@ -8,11 +8,59 @@ spring:
nacos:
discovery:
enabled: true
- register-enabled: true
+ register-enabled: false
server-addr: 10.0.0.10:8848
username: nacos
password: nacos
+ config:
+ enabled: false
+ import-check:
+ enabled: false
+ gateway:
+ # Gateway 发现定位器配置
+ server:
+ webflux:
+ discovery:
+ locator:
+ enabled: true
+ lower-case-service-id: true
+ loadbalancer:
+ nacos:
+ enabled: true
+ retry:
+ enabled: true
+ max-retries-on-same-service-instance: 1
+ max-retries-on-next-service-instance: 2
+ cache:
+ enabled: true
+ ttl: 35s
+ health-check:
+ initial-delay: 0ms
+ interval: 30s
+
+
+aioj-backend-gateway:
+ # 白名单配置
+ white-list:
+ - /api/v1/auth/login
+ - /api/v1/auth/register
+ - /api/v1/auth/refresh
+ - /api/v1/user/register
+ - /api/v1/user/info
+ - /api/v1/question/list
+ - /api/v1/question/detail/**
+ - /actuator/health
+ - /swagger-ui/**
logging:
level:
- root: INFO
\ No newline at end of file
+ root: INFO
+ # Nacos 相关日志
+ com.alibaba.nacos: INFO
+ com.alibaba.cloud.nacos: DEBUG
+ # LoadBalancer 日志
+ org.springframework.cloud.loadbalancer: DEBUG
+ # Gateway 日志
+ org.springframework.cloud.gateway: DEBUG
+ # 自定义过滤器日志
+ cn.meowrain.aioj.backend.gateway: DEBUG
diff --git a/aioj-backend-gateway/src/main/resources/application.yml b/aioj-backend-gateway/src/main/resources/application.yml
index 00539f5..76a2fd5 100644
--- a/aioj-backend-gateway/src/main/resources/application.yml
+++ b/aioj-backend-gateway/src/main/resources/application.yml
@@ -1,29 +1,50 @@
server:
port: 8085
+ error:
+ include-stacktrace: never
+
spring:
+ profiles:
+ active: @env@
cloud:
gateway:
server:
webflux:
routes:
- id: auth-service
- uri: lb://auth-service/api
+ uri: lb://auth-service
predicates:
- Path=/api/v1/auth/**
+ filters:
+ - name: Retry
+ args:
+ retries: 3
+ statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
+ backoff:
+ firstBackoff: 50ms
+ maxBackoff: 500ms
- id: user-service
- uri: lb://user-service/api
+ uri: lb://user-service
predicates:
- Path=/api/v1/user/**
+ filters:
+ - name: Retry
+ args:
+ retries: 3
+ statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
+ backoff:
+ firstBackoff: 50ms
+ maxBackoff: 500ms
+ # 设置应用启动后的就绪探针
+ lifecycle:
+ timeout-per-shutdown-phase: 30s
aioj-backend-gateway:
# 白名单配置
white-list:
- /api/v1/auth/login
- - /api/v1/auth/register
+ - /api/v1/user/register
- /api/v1/auth/refresh
- - /api/v1/user/info
- - /api/v1/question/list
- - /api/v1/question/detail/**
- /actuator/health
- /swagger-ui/**
- /v3/api-docs/**
diff --git a/aioj-backend-gateway/src/main/resources/bootstrap.yml b/aioj-backend-gateway/src/main/resources/bootstrap.yml
deleted file mode 100644
index 80e4c6f..0000000
--- a/aioj-backend-gateway/src/main/resources/bootstrap.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-spring:
- application:
- name: aioj-gateway
- cloud:
- nacos:
- discovery:
- server-addr: 10.0.0.10:8848
- username: nacos
- password: nacos
- config:
- server-addr: 10.0.0.10:8848
- username: nacos
- password: nacos
- file-extension: yaml
- group: DEFAULT_GROUP
\ No newline at end of file
diff --git a/aioj-backend-upms/aioj-backend-upms-api/src/main/java/cn/meowrain/aioj/backend/upms/api/entity/SysLog.java b/aioj-backend-upms/aioj-backend-upms-api/src/main/java/cn/meowrain/aioj/backend/upms/api/entity/SysLog.java
index 2a1ca44..d4c7cc3 100644
--- a/aioj-backend-upms/aioj-backend-upms-api/src/main/java/cn/meowrain/aioj/backend/upms/api/entity/SysLog.java
+++ b/aioj-backend-upms/aioj-backend-upms-api/src/main/java/cn/meowrain/aioj/backend/upms/api/entity/SysLog.java
@@ -19,7 +19,6 @@
package cn.meowrain.aioj.backend.upms.api.entity;
-
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
@@ -41,14 +40,14 @@ import java.time.LocalDateTime;
@Schema(description = "日志")
public class SysLog implements Serializable {
- @Serial
+ @Serial
private static final long serialVersionUID = 1L;
/**
* 编号
*/
@TableId(type = IdType.ASSIGN_ID)
-// @ExcelProperty("日志编号")
+ // @ExcelProperty("日志编号")
@Schema(description = "日志编号")
private Long id;
@@ -56,7 +55,7 @@ public class SysLog implements Serializable {
* 日志类型
*/
@NotBlank(message = "日志类型不能为空")
-// @ExcelProperty("日志类型(0-正常 9-错误)")
+ // @ExcelProperty("日志类型(0-正常 9-错误)")
@Schema(description = "日志类型")
private String logType;
@@ -64,14 +63,14 @@ public class SysLog implements Serializable {
* 日志标题
*/
@NotBlank(message = "日志标题不能为空")
-// @ExcelProperty("日志标题")
+ // @ExcelProperty("日志标题")
@Schema(description = "日志标题")
private String title;
/**
* 创建者
*/
-// @ExcelProperty("创建人")
+ // @ExcelProperty("创建人")
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
@@ -79,7 +78,7 @@ public class SysLog implements Serializable {
/**
* 创建时间
*/
-// @ExcelProperty("创建时间")
+ // @ExcelProperty("创建时间")
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建时间")
private LocalDateTime createTime;
@@ -87,7 +86,7 @@ public class SysLog implements Serializable {
/**
* 更新时间
*/
-// @ExcelIgnore
+ // @ExcelIgnore
@TableField(fill = FieldFill.UPDATE)
@Schema(description = "更新时间")
private LocalDateTime updateTime;
@@ -95,7 +94,7 @@ public class SysLog implements Serializable {
/**
* 操作IP地址
*/
-// @ExcelProperty("操作ip地址")
+ // @ExcelProperty("操作ip地址")
@Schema(description = "操作ip地址")
private String remoteAddr;
@@ -108,42 +107,42 @@ public class SysLog implements Serializable {
/**
* 请求URI
*/
-// @ExcelProperty("浏览器")
+ // @ExcelProperty("浏览器")
@Schema(description = "请求uri")
private String requestUri;
/**
* 操作方式
*/
-// @ExcelProperty("操作方式")
+ // @ExcelProperty("操作方式")
@Schema(description = "操作方式")
private String method;
/**
* 操作提交的数据
*/
-// @ExcelProperty("提交数据")
+ // @ExcelProperty("提交数据")
@Schema(description = "提交数据")
private String params;
/**
* 执行时间
*/
-// @ExcelProperty("执行时间")
+ // @ExcelProperty("执行时间")
@Schema(description = "方法执行时间")
private Long time;
/**
* 异常信息
*/
-// @ExcelProperty("异常信息")
+ // @ExcelProperty("异常信息")
@Schema(description = "异常信息")
private String exception;
/**
* 服务ID
*/
-// @ExcelProperty("应用标识")
+ // @ExcelProperty("应用标识")
@Schema(description = "应用标识")
private String serviceId;
@@ -151,7 +150,7 @@ public class SysLog implements Serializable {
* 删除标记
*/
@TableLogic
-// @ExcelIgnore
+ // @ExcelIgnore
@TableField(fill = FieldFill.INSERT)
@Schema(description = "删除标记,1:已删除,0:正常")
private String delFlag;
diff --git a/aioj-backend-upms/aioj-backend-upms-api/src/main/java/cn/meowrain/aioj/backend/upms/api/feign/RemoteLogService.java b/aioj-backend-upms/aioj-backend-upms-api/src/main/java/cn/meowrain/aioj/backend/upms/api/feign/RemoteLogService.java
index 41c5784..160bba7 100644
--- a/aioj-backend-upms/aioj-backend-upms-api/src/main/java/cn/meowrain/aioj/backend/upms/api/feign/RemoteLogService.java
+++ b/aioj-backend-upms/aioj-backend-upms-api/src/main/java/cn/meowrain/aioj/backend/upms/api/feign/RemoteLogService.java
@@ -10,12 +10,14 @@ import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.UPMS_SERVICE)
public interface RemoteLogService {
- /**
- * 保存日志 (异步多线程调用,无token)
- * @param sysLog 日志实体
- * @return succes、false
- */
- @NoToken
- @PostMapping("/log/save")
- Result saveLog(@RequestBody SysLog sysLog);
+
+ /**
+ * 保存日志 (异步多线程调用,无token)
+ * @param sysLog 日志实体
+ * @return succes、false
+ */
+ @NoToken
+ @PostMapping("/log/save")
+ Result saveLog(@RequestBody SysLog sysLog);
+
}
diff --git a/aioj-backend-user-service/pom.xml b/aioj-backend-user-service/pom.xml
index 936c3a0..cae9988 100644
--- a/aioj-backend-user-service/pom.xml
+++ b/aioj-backend-user-service/pom.xml
@@ -1,7 +1,7 @@
+ 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">
4.0.0
cn.meowrain
@@ -20,7 +20,12 @@
cn.meowrain
- aioj-backend-common-starter
+ aioj-backend-common-core
+ 1.0-SNAPSHOT
+
+
+ cn.meowrain
+ aioj-backend-common-log
1.0-SNAPSHOT
@@ -60,21 +65,15 @@
spring-boot-starter-test
test
-
- org.springframework.boot
- spring-boot-devtools
- runtime
- true
-
+
+
+
+
+
+
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
-
-
- cn.meowrain
- aioj-backend-common-core
- 1.0-SNAPSHOT
-
\ No newline at end of file
diff --git a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/FrameworkConfiguration.java b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/FrameworkConfiguration.java
index a65791b..a00a3e5 100644
--- a/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/FrameworkConfiguration.java
+++ b/aioj-backend-user-service/src/main/java/cn/meowrain/aioj/backend/userservice/config/FrameworkConfiguration.java
@@ -5,7 +5,8 @@ import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
@Configuration
-@ComponentScans({ @ComponentScan("cn.meowrain.aioj.backend.framework.core.banner") })
+// @ComponentScans({
+// @ComponentScan("cn.meowrain.aioj.backend.framework.core.banner") })
public class FrameworkConfiguration {
}
diff --git a/aioj-backend-user-service/src/main/resources/application.yml b/aioj-backend-user-service/src/main/resources/application.yml
index 068e481..4b017f9 100644
--- a/aioj-backend-user-service/src/main/resources/application.yml
+++ b/aioj-backend-user-service/src/main/resources/application.yml
@@ -7,6 +7,8 @@ server:
port: 10010
servlet:
context-path: /api
+ error:
+ include-stacktrace: never
springdoc:
api-docs:
enabled: true
diff --git a/docs/auth-api.md b/docs/auth-api.md
new file mode 100644
index 0000000..ff72f52
--- /dev/null
+++ b/docs/auth-api.md
@@ -0,0 +1,243 @@
+# AIOJ 认证服务 API 文档
+
+## 概述
+
+AIOJ认证服务提供JWT(JSON Web Token)为基础的用户认证和授权功能。该服务负责用户登录、令牌生成、令牌刷新和令牌验证。
+
+**基础信息:**
+- 服务名称: auth-service
+- 基础路径: `/api`
+- API版本: v1
+- 认证方式: JWT Bearer Token
+- 数据格式: JSON
+
+## JWT认证机制
+
+### 访问令牌 (Access Token)
+- 有效期: 15分钟
+- 用途: 访问受保护的API接口
+- 格式: Bearer Token
+
+### 刷新令牌 (Refresh Token)
+- 有效期: 7天
+- 用途: 获取新的Access Token
+- 存储位置: Redis
+
+### 请求头格式
+```
+Authorization: Bearer {access_token}
+```
+
+## API接口
+
+### 1. 用户登录
+用户通过用户名和密码进行登录认证。
+
+**接口信息:**
+- URL: `POST /api/v1/auth/login`
+- 描述: 用户登录获取访问令牌
+- 认证要求: 无需认证
+
+**请求参数:**
+```json
+{
+ "userAccount": "string", // 用户账号
+ "userPassword": "string" // 用户密码
+}
+```
+
+**请求示例:**
+```bash
+curl -X POST http://localhost:10011/api/v1/auth/login \
+ -H "Content-Type: application/json" \
+ -d '{
+ "userAccount": "admin",
+ "userPassword": "password123"
+ }'
+```
+
+**响应示例:**
+```json
+{
+ "success": true,
+ "message": "操作成功",
+ "data": {
+ "id": 1,
+ "userAccount": "admin",
+ "unionId": null,
+ "accessToken": "eyJhbGciOiJIUzI1NiJ9...",
+ "refreshToken": "eyJhbGciOiJIUzI1NiJ9...",
+ "expire": null
+ }
+}
+```
+
+### 2. 令牌刷新
+使用刷新令牌获取新的访问令牌。
+
+**接口信息:**
+- URL: `POST /api/v1/auth/refresh`
+- 描述: 刷新访问令牌
+- 认证要求: 无需认证
+
+**请求参数:**
+```
+refreshToken=string // 刷新令牌
+```
+
+**请求示例:**
+```bash
+curl -X POST "http://localhost:10011/api/v1/auth/refresh?refreshToken=eyJhbGciOiJIUzI1NiJ9..."
+```
+
+**响应示例:**
+```json
+{
+ "success": true,
+ "message": "操作成功",
+ "data": {
+ "id": null,
+ "userAccount": null,
+ "unionId": null,
+ "accessToken": "eyJhbGciOiJIUzI1NiJ9...",
+ "refreshToken": "eyJhbGciOiJIUzI1NiJ9...",
+ "expire": null
+ }
+}
+```
+
+### 3. 获取访问令牌
+简化版的登录接口,仅返回访问令牌。
+
+**接口信息:**
+- URL: `POST /api/v1/auth/auth`
+- 描述: 获取访问令牌(简化版登录)
+- 认证要求: 无需认证
+
+**请求参数:**
+```json
+{
+ "userAccount": "string", // 用户账号
+ "userPassword": "string" // 用户密码
+}
+```
+
+**请求示例:**
+```bash
+curl -X POST http://localhost:10011/api/v1/auth/auth \
+ -H "Content-Type: application/json" \
+ -d '{
+ "userAccount": "admin",
+ "userPassword": "password123"
+ }'
+```
+
+**响应示例:**
+```json
+{
+ "success": true,
+ "message": "操作成功",
+ "data": "eyJhbGciOiJIUzI1NiJ9..."
+}
+```
+
+### 4. 令牌验证
+验证访问令牌的有效性。
+
+**接口信息:**
+- URL: `POST /api/v1/auth/validate`
+- 描述: 验证访问令牌
+- 认证要求: 可选的Authorization头
+
+**请求头:**
+```
+Authorization: Bearer {access_token} // 可选
+```
+
+**请求示例:**
+```bash
+curl -X POST http://localhost:10011/api/v1/auth/validate \
+ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."
+```
+
+**响应示例:**
+```json
+{
+ "success": true,
+ "message": "操作成功",
+ "data": true
+}
+```
+
+## 状态码说明
+
+| 状态码 | 说明 | 描述 |
+|--------|------|------|
+| 200 | OK | 请求成功 |
+| 400 | Bad Request | 请求参数错误 |
+| 401 | Unauthorized | 未授权访问或令牌无效 |
+| 403 | Forbidden | 禁止访问 |
+| 500 | Internal Server Error | 服务器内部错误 |
+
+## 错误响应格式
+
+```json
+{
+ "success": false,
+ "message": "错误描述",
+ "data": null
+}
+```
+
+## 常见错误码
+
+| 错误信息 | 可能原因 | 解决方案 |
+|----------|----------|----------|
+| "用户不存在或密码错误" | 用户账号或密码不正确 | 检查用户名和密码 |
+| "Refresh Token 已过期" | 刷新令牌已过期 | 重新登录获取新令牌 |
+| "Refresh Token 已失效" | 刷新令牌在Redis中不存在或已失效 | 重新登录获取新令牌 |
+| "系统错误" | 服务间调用失败或系统异常 | 检查服务状态和网络连接 |
+
+## 配置信息
+
+### JWT配置
+```yaml
+jwt:
+ secret: "12345678901234567890123456789012" # 32字节密钥
+ access-expire: 900000 # 15分钟(毫秒)
+ refresh-expire: 604800000 # 7天(毫秒)
+```
+
+### 服务配置
+```yaml
+spring:
+ application:
+ name: auth-service
+server:
+ port: 10011
+ servlet:
+ context-path: /api
+```
+
+## 安全注意事项
+
+1. **令牌存储**: Access Token应存储在内存或HttpOnly Cookie中
+2. **HTTPS传输**: 生产环境必须使用HTTPS协议
+3. **密钥管理**: JWT密钥应定期轮换,使用强密钥
+4. **令牌过期**: 合理设置令牌过期时间,平衡安全性和用户体验
+5. **错误处理**: 不要在错误信息中泄露敏感信息
+
+## 使用流程
+
+1. **用户登录**: 调用`/login`接口获取Access Token和Refresh Token
+2. **API访问**: 在请求头中携带Access Token访问受保护接口
+3. **令牌刷新**: Access Token过期时使用Refresh Token获取新的Access Token
+4. **令牌验证**: 可使用`/validate`接口验证Token有效性
+
+## 联系信息
+
+如有问题或建议,请联系开发团队。
+
+---
+*文档版本: v1.0*
+*更新时间: 2025-12-14*
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 984ec16..6a26b97 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,13 +1,13 @@
+ 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">
4.0.0
org.springframework.boot
spring-boot-starter-parent
3.5.7
-
+
cn.meowrain
ai-oj
@@ -66,7 +66,8 @@
pom
import
-
+
org.springframework.boot
spring-boot-dependencies
@@ -75,7 +76,8 @@
import
-
+
org.springframework.cloud
spring-cloud-dependencies
@@ -96,10 +98,6 @@
src/main/resources
-
- application*.yml
- application*.properties
-
true
@@ -120,7 +118,8 @@
${spring-boot.version}
@@ -135,7 +134,8 @@
-
+
io.github.git-commit-id
git-commit-id-maven-plugin
@@ -168,13 +168,14 @@
true
- registry.cn-shanghai.aliyuncs.com/all_lib/eclipse-temurin:17.0.10_7-jdk-jammy
+
+ registry.cn-shanghai.aliyuncs.com/all_lib/eclipse-temurin:17.0.10_7-jdk-jammy
-
+
10.0.0.3/aioj/${project.artifactId}:${project.version}
-
+
${project.version}
@@ -183,7 +184,8 @@
- ${project.build.directory}/${project.artifactId}-${project.version}.tar
+
+ ${project.build.directory}/${project.artifactId}-${project.version}.tar