feat: 配置Swagger UI在开发/测试环境可访问
- SecurityConfig: 添加Environment注入和环境检测逻辑 - SecurityConfig: 在dev/test环境放行Swagger相关路径 - SecurityConfig: 移除重复的PasswordEncoder Bean定义 - SecurityConfigTest: 修改测试以适应新的构造函数 - OpenApiConfig: 修正开发环境服务器URL从8080改为8084 修改的文件: - novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/SecurityConfig.java - novalon-manage-api/manage-sys/src/test/java/cn/novalon/manage/sys/config/SecurityConfigTest.java - novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/OpenApiConfig.java 功能说明: - Swagger UI在dev/test环境可通过http://localhost:8084/swagger-ui.html访问 - 生产环境自动禁用Swagger访问,确保安全性 - 解决了Bean冲突问题(PasswordEncoder重复定义) - 修正了服务器端口配置
This commit is contained in:
+34
-34
@@ -22,39 +22,39 @@ import java.util.List;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class OpenApiConfig {
|
public class OpenApiConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public OpenAPI customOpenAPI() {
|
public OpenAPI customOpenAPI() {
|
||||||
return new OpenAPI()
|
return new OpenAPI()
|
||||||
.info(new Info()
|
.info(new Info()
|
||||||
.title("Novalon Manage System API")
|
.title("Novalon Manage System API")
|
||||||
.version("1.0.0")
|
.version("1.0.0")
|
||||||
.description("Novalon 管理系统 RESTful API 文档")
|
.description("Novalon 管理系统 RESTful API 文档")
|
||||||
.contact(new Contact()
|
.contact(new Contact()
|
||||||
.name("Novalon Team")
|
.name("Novalon Team")
|
||||||
.email("support@novalon.cn"))
|
.email("support@novalon.cn"))
|
||||||
.license(new License()
|
.license(new License()
|
||||||
.name("Apache 2.0")
|
.name("Apache 2.0")
|
||||||
.url("https://www.apache.org/licenses/LICENSE-2.0")))
|
.url("https://www.apache.org/licenses/LICENSE-2.0")))
|
||||||
.servers(List.of(
|
.servers(List.of(
|
||||||
new Server().url("http://localhost:8080").description("开发环境"),
|
new Server().url("http://localhost:8084").description("开发环境"),
|
||||||
new Server().url("https://api.novalon.cn").description("生产环境")))
|
new Server().url("https://api.novalon.cn").description("生产环境")))
|
||||||
.tags(Arrays.asList(
|
.tags(Arrays.asList(
|
||||||
new Tag().name("用户管理").description("用户相关操作"),
|
new Tag().name("用户管理").description("用户相关操作"),
|
||||||
new Tag().name("角色管理").description("角色相关操作"),
|
new Tag().name("角色管理").description("角色相关操作"),
|
||||||
new Tag().name("配置管理").description("系统配置相关操作"),
|
new Tag().name("配置管理").description("系统配置相关操作"),
|
||||||
new Tag().name("字典管理").description("字典数据相关操作"),
|
new Tag().name("字典管理").description("字典数据相关操作"),
|
||||||
new Tag().name("通知管理").description("系统通知相关操作"),
|
new Tag().name("通知管理").description("系统通知相关操作"),
|
||||||
new Tag().name("文件管理").description("文件上传下载相关操作"),
|
new Tag().name("文件管理").description("文件上传下载相关操作"),
|
||||||
new Tag().name("日志管理").description("操作日志相关操作"),
|
new Tag().name("日志管理").description("操作日志相关操作"),
|
||||||
new Tag().name("认证管理").description("登录认证相关操作"),
|
new Tag().name("认证管理").description("登录认证相关操作"),
|
||||||
new Tag().name("统计信息").description("系统统计相关操作")));
|
new Tag().name("统计信息").description("系统统计相关操作")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public GroupedOpenApi allApi() {
|
public GroupedOpenApi allApi() {
|
||||||
return GroupedOpenApi.builder()
|
return GroupedOpenApi.builder()
|
||||||
.group("all")
|
.group("all")
|
||||||
.pathsToMatch("/api/**")
|
.pathsToMatch("/api/**")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-16
@@ -1,13 +1,14 @@
|
|||||||
package cn.novalon.manage.sys.config;
|
package cn.novalon.manage.sys.config;
|
||||||
|
|
||||||
import cn.novalon.manage.sys.security.JwtAuthenticationFilter;
|
import cn.novalon.manage.sys.security.JwtAuthenticationFilter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||||
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
|
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,30 +21,54 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
|
|||||||
@EnableWebFluxSecurity
|
@EnableWebFluxSecurity
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
|
||||||
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
|
private final Environment environment;
|
||||||
|
|
||||||
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
|
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter, Environment environment) {
|
||||||
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
|
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
|
||||||
}
|
this.environment = environment;
|
||||||
|
|
||||||
@Bean
|
|
||||||
public PasswordEncoder passwordEncoder() {
|
|
||||||
return new BCryptPasswordEncoder();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
|
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
|
||||||
return http
|
String[] activeProfiles = environment.getActiveProfiles();
|
||||||
|
boolean isDevOrTest = false;
|
||||||
|
|
||||||
|
for (String profile : activeProfiles) {
|
||||||
|
if ("dev".equals(profile) || "test".equals(profile)) {
|
||||||
|
isDevOrTest = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("SecurityConfig初始化: 当前环境={}, Swagger启用状态={}",
|
||||||
|
activeProfiles.length > 0 ? String.join(",", activeProfiles) : "default", isDevOrTest);
|
||||||
|
|
||||||
|
ServerHttpSecurity.AuthorizeExchangeSpec exchanges = http
|
||||||
.csrf(ServerHttpSecurity.CsrfSpec::disable)
|
.csrf(ServerHttpSecurity.CsrfSpec::disable)
|
||||||
.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
|
.httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
|
||||||
.formLogin(ServerHttpSecurity.FormLoginSpec::disable)
|
.formLogin(ServerHttpSecurity.FormLoginSpec::disable)
|
||||||
.addFilterBefore(jwtAuthenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION)
|
.addFilterBefore(jwtAuthenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION)
|
||||||
.authorizeExchange(exchanges -> exchanges
|
.authorizeExchange();
|
||||||
.pathMatchers("/api/auth/**").permitAll()
|
|
||||||
.pathMatchers("/api/public/**").permitAll()
|
exchanges.pathMatchers("/api/auth/**").permitAll()
|
||||||
.pathMatchers("/ws/**").permitAll()
|
.pathMatchers("/api/public/**").permitAll()
|
||||||
.pathMatchers("/actuator/**").permitAll()
|
.pathMatchers("/ws/**").permitAll()
|
||||||
.anyExchange().authenticated())
|
.pathMatchers("/actuator/**").permitAll();
|
||||||
.build();
|
|
||||||
|
if (isDevOrTest) {
|
||||||
|
exchanges.pathMatchers("/swagger-ui.html").permitAll()
|
||||||
|
.pathMatchers("/swagger-ui/**").permitAll()
|
||||||
|
.pathMatchers("/api-docs/**").permitAll()
|
||||||
|
.pathMatchers("/v3/api-docs/**").permitAll()
|
||||||
|
.pathMatchers("/swagger-resources/**").permitAll()
|
||||||
|
.pathMatchers("/webjars/**").permitAll();
|
||||||
|
logger.info("SecurityConfig: Swagger路径已放行");
|
||||||
|
}
|
||||||
|
|
||||||
|
exchanges.anyExchange().authenticated();
|
||||||
|
|
||||||
|
return http.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-10
@@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -16,19 +17,22 @@ class SecurityConfigTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Environment environment;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
private SecurityConfig securityConfig;
|
private SecurityConfig securityConfig;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
securityConfig = new SecurityConfig(jwtAuthenticationFilter);
|
securityConfig = new SecurityConfig(jwtAuthenticationFilter, environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPasswordEncoder() {
|
void testPasswordEncoder() {
|
||||||
PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
|
|
||||||
|
|
||||||
assertThat(passwordEncoder).isNotNull();
|
assertThat(passwordEncoder).isNotNull();
|
||||||
assertThat(passwordEncoder).isInstanceOf(org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.class);
|
|
||||||
|
|
||||||
String rawPassword = "testPassword123";
|
String rawPassword = "testPassword123";
|
||||||
String encodedPassword = passwordEncoder.encode(rawPassword);
|
String encodedPassword = passwordEncoder.encode(rawPassword);
|
||||||
@@ -41,8 +45,6 @@ class SecurityConfigTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPasswordEncoder_SamePasswordDifferentHashes() {
|
void testPasswordEncoder_SamePasswordDifferentHashes() {
|
||||||
PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
|
|
||||||
|
|
||||||
String rawPassword = "testPassword123";
|
String rawPassword = "testPassword123";
|
||||||
String hash1 = passwordEncoder.encode(rawPassword);
|
String hash1 = passwordEncoder.encode(rawPassword);
|
||||||
String hash2 = passwordEncoder.encode(rawPassword);
|
String hash2 = passwordEncoder.encode(rawPassword);
|
||||||
@@ -54,8 +56,6 @@ class SecurityConfigTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPasswordEncoder_EmptyPassword() {
|
void testPasswordEncoder_EmptyPassword() {
|
||||||
PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
|
|
||||||
|
|
||||||
String encodedPassword = passwordEncoder.encode("");
|
String encodedPassword = passwordEncoder.encode("");
|
||||||
|
|
||||||
assertThat(encodedPassword).isNotNull();
|
assertThat(encodedPassword).isNotNull();
|
||||||
@@ -64,8 +64,6 @@ class SecurityConfigTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPasswordEncoder_Strength() {
|
void testPasswordEncoder_Strength() {
|
||||||
PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
|
|
||||||
|
|
||||||
String rawPassword = "testPassword123";
|
String rawPassword = "testPassword123";
|
||||||
String encodedPassword = passwordEncoder.encode(rawPassword);
|
String encodedPassword = passwordEncoder.encode(rawPassword);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user