commit 4fe7ba5d0d781c47b3a2c293f223810036568981
Author: panxuejie <15855548138@163.com>
Date: Tue Dec 30 11:14:25 2025 +0800
Initial commit
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..69fcbcd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,150 @@
+
+
+ 4.0.0
+
+ com.tacit
+ tacit-parent
+ 1.0.0-SNAPSHOT
+ pom
+
+ Tacit Parent
+ Tacit App Microservice Architecture Parent Project
+
+
+ 21
+ 3.2.0
+ 2023.0.0
+ 2023.0.1.0
+ 3.5.5
+ 1.18.30
+ 0.11.5
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ ${mybatis-plus.version}
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jjwt.version}
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${jjwt.version}
+ runtime
+
+
+
+
+
+ tacit-gateway
+ tacit-admin
+ tacit-app-api
+ tacit-common
+
+
+
+
+ central
+ Maven Central Repository
+ https://repo1.maven.org/maven2/
+ default
+
+ true
+
+
+ false
+
+
+
+
+
+
+ central
+ Maven Central Repository
+ https://repo1.maven.org/maven2/
+ default
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ ${java.version}
+ ${java.version}
+ UTF-8
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tacit-admin/pom.xml b/tacit-admin/pom.xml
new file mode 100644
index 0000000..86dee2b
--- /dev/null
+++ b/tacit-admin/pom.xml
@@ -0,0 +1,101 @@
+
+
+
+ tacit-parent
+ com.tacit
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+
+ tacit-admin
+ Tacit Admin
+ Admin Service for Tacit Microservices
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+
+
+ com.oceanbase
+ oceanbase-client
+ 2.4.0
+
+
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.3.0
+
+
+
+
+ com.tacit
+ tacit-common
+ ${project.parent.version}
+
+
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+ false
+
+
+
+
+
\ No newline at end of file
diff --git a/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java b/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java
new file mode 100644
index 0000000..cf6e603
--- /dev/null
+++ b/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java
@@ -0,0 +1,17 @@
+package com.tacit.admin;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@SpringBootApplication
+@EnableDiscoveryClient
+@EnableFeignClients
+@MapperScan("com.tacit.admin.mapper")
+public class AdminApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(AdminApplication.class, args);
+ }
+}
diff --git a/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java b/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java
new file mode 100644
index 0000000..55841d0
--- /dev/null
+++ b/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java
@@ -0,0 +1,114 @@
+package com.tacit.admin.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+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;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Collections;
+
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity(prePostEnabled = true)
+public class SecurityConfig {
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http
+ // 禁用CSRF保护
+ .csrf(csrf -> csrf.disable())
+ // 允许跨域请求
+ .cors(cors -> cors.disable())
+ // 设置会话管理为无状态
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ // 添加JWT认证过滤器
+ .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
+ // 设置请求授权规则
+ .authorizeHttpRequests(auth -> auth
+ // 允许访问的路径
+ .requestMatchers("/test/**", "/user/login", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
+ // 其他请求需要认证
+ .anyRequest().authenticated()
+ );
+
+ return http.build();
+ }
+
+ @Bean
+ public OncePerRequestFilter jwtAuthenticationFilter() {
+ return new OncePerRequestFilter() {
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ // 从请求头中获取用户信息(由网关添加)
+ String userId = request.getHeader("X-User-Id");
+ String username = request.getHeader("X-Username");
+ String role = request.getHeader("X-Role");
+
+ // 如果有用户信息,则创建Authentication对象并设置到SecurityContext
+ if (userId != null && username != null && role != null) {
+ // 创建用户对象
+ org.springframework.security.core.userdetails.User user = new User(
+ username,
+ "",
+ Collections.singletonList(() -> "ROLE_" + role.toUpperCase())
+ );
+
+ // 创建认证对象
+ org.springframework.security.authentication.UsernamePasswordAuthenticationToken authentication =
+ new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(
+ user,
+ null,
+ user.getAuthorities()
+ );
+
+ // 设置认证信息到SecurityContext
+ org.springframework.security.core.context.SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+
+ // 继续过滤链
+ filterChain.doFilter(request, response);
+ }
+ };
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
+ DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+ provider.setUserDetailsService(userDetailsService);
+ provider.setPasswordEncoder(passwordEncoder);
+ return new ProviderManager(Collections.singletonList(provider));
+ }
+
+ @Bean
+ public UserDetailsService userDetailsService() {
+ // 这里可以根据实际情况实现UserDetailsService,从数据库中获取用户信息
+ return username -> {
+ // 由于我们使用网关进行认证,这里可以返回一个空实现
+ // 实际项目中应该根据用户名从数据库中获取用户信息
+ throw new UsernameNotFoundException("User not found: " + username);
+ };
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+}
diff --git a/tacit-admin/src/main/java/com/tacit/admin/config/SwaggerConfig.java b/tacit-admin/src/main/java/com/tacit/admin/config/SwaggerConfig.java
new file mode 100644
index 0000000..c233c2a
--- /dev/null
+++ b/tacit-admin/src/main/java/com/tacit/admin/config/SwaggerConfig.java
@@ -0,0 +1,23 @@
+package com.tacit.admin.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Swagger配置类
+ * 使用SpringDoc OpenAPI配置API文档
+ */
+@Configuration
+public class SwaggerConfig {
+
+ @Bean
+ public OpenAPI adminOpenAPI() {
+ return new OpenAPI()
+ .info(new Info()
+ .title("Tacit Admin API")
+ .description("管理台服务API文档")
+ .version("1.0.0"));
+ }
+}
diff --git a/tacit-admin/src/main/java/com/tacit/admin/controller/TestController.java b/tacit-admin/src/main/java/com/tacit/admin/controller/TestController.java
new file mode 100644
index 0000000..d885e0d
--- /dev/null
+++ b/tacit-admin/src/main/java/com/tacit/admin/controller/TestController.java
@@ -0,0 +1,32 @@
+package com.tacit.admin.controller;
+
+import com.tacit.common.entity.ResponseResult;
+import com.tacit.common.feign.AppApiFeignClient;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/test")
+@Tag(name = "测试接口", description = "用于测试的接口")
+public class TestController {
+
+ @Autowired
+ private AppApiFeignClient appApiFeignClient;
+
+ @Operation(summary = "测试接口", description = "这是一个测试接口")
+ @GetMapping("/hello")
+ public ResponseResult hello() {
+ return ResponseResult.success("Hello from admin service!");
+ }
+
+ @Operation(summary = "测试Feign调用", description = "测试通过Feign调用APP接口服务")
+ @GetMapping("/feign/{userId}")
+ public ResponseResult