update
This commit is contained in:
@@ -7,19 +7,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
@Configuration
|
||||
public class MyMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
/*
|
||||
* addResourceHandlers
|
||||
* void
|
||||
*/
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
|
||||
registry.addResourceHandler("swagger-ui.html")
|
||||
.addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/");
|
||||
// SpringDoc OpenAPI serves its own UI resources — no manual mapping needed.
|
||||
// Static application assets:
|
||||
registry.addResourceHandler("/static/**")
|
||||
.addResourceLocations("classpath:/static/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
package com.yaoyuan.jiscuss.config;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
/**
|
||||
* SpringDoc OpenAPI 2.x configuration.
|
||||
*
|
||||
* <p>Replaces discontinued springfox-swagger2 (incompatible with Spring Boot 3).
|
||||
* UI is available at /swagger-ui/index.html
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class SwaggerConfiguration {
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
|
||||
.apis(RequestHandlerSelectors.basePackage("com.yaoyuan.jiscuss.controller")).paths(PathSelectors.any())
|
||||
.build();
|
||||
public OpenAPI jiscussOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("Jiscuss REST APIs")
|
||||
.description("Jiscuss forum system API documentation")
|
||||
.version("1.0")
|
||||
.contact(new Contact()
|
||||
.name("Jiscuss Team")
|
||||
.email("developer@mail.com")));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder().title("swagger-ui RESTful APIs").description("Jiscuss")
|
||||
.termsOfServiceUrl("http://localhost/").contact("developer@mail.com").version("1.0").build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +1,157 @@
|
||||
package com.yaoyuan.jiscuss.config;
|
||||
|
||||
import com.yaoyuan.jiscuss.handler.CustomAccessDeniedHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Configurable;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
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.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
|
||||
|
||||
/**
|
||||
* prePostEnabled :决定Spring Security的前注解是否可用 [@PreAuthorize,@PostAuthorize,..]
|
||||
* secureEnabled : 决定是否Spring Security的保障注解 [@Secured] 是否可用
|
||||
* jsr250Enabled :决定 JSR-250 annotations 注解[@RolesAllowed..] 是否可用.
|
||||
* 开启 Spring Security 方法级安全注解 @EnableGlobalMethodSecurity
|
||||
* Spring Security 6 configuration (Spring Boot 3 compatible).
|
||||
*
|
||||
* <p>Breaking changes from Spring Boot 2.x:
|
||||
* <ul>
|
||||
* <li>{@code WebSecurityConfigurerAdapter} removed → use {@code SecurityFilterChain} bean</li>
|
||||
* <li>{@code antMatchers} → {@code requestMatchers}</li>
|
||||
* <li>{@code @EnableGlobalMethodSecurity} → {@code @EnableMethodSecurity}</li>
|
||||
* <li>{@code @Configurable} (AspectJ) corrected to {@code @Configuration}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Configurable
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Autowired
|
||||
private CustomAccessDeniedHandler customAccessDeniedHandler;
|
||||
|
||||
@Qualifier("userDetailServiceImpl")
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
private final CustomAccessDeniedHandler customAccessDeniedHandler;
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
/** Controlled by spring.h2.console.enabled — only true in dev (h2) profile. */
|
||||
@Value("${spring.h2.console.enabled:false}")
|
||||
private boolean h2ConsoleEnabled;
|
||||
|
||||
public WebSecurityConfig(
|
||||
CustomAccessDeniedHandler customAccessDeniedHandler,
|
||||
@Qualifier("userDetailServiceImpl") UserDetailsService userDetailsService,
|
||||
ApplicationContext applicationContext) {
|
||||
this.customAccessDeniedHandler = customAccessDeniedHandler;
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态资源设置
|
||||
* Exclude truly static assets from the security filter chain entirely.
|
||||
* H2 console and Druid monitoring are NOT excluded here — they are secured via filterChain.
|
||||
*/
|
||||
@Override
|
||||
public void configure(WebSecurity webSecurity) {
|
||||
//不拦截静态资源,所有用户均可访问的资源
|
||||
webSecurity.ignoring().antMatchers(
|
||||
"/",
|
||||
"/css/**",
|
||||
"/js/**",
|
||||
"/images/**",
|
||||
"/static/**",
|
||||
"/h2-console/**",
|
||||
"/test/**",
|
||||
"/druid/**"
|
||||
@Bean
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return web -> web.ignoring().requestMatchers(
|
||||
"/css/**", "/js/**", "/images/**", "/static/**", "/favicon.ico"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* http请求设置
|
||||
* Main security filter chain.
|
||||
*
|
||||
* <p>Security decisions:
|
||||
* <ul>
|
||||
* <li>H2 console: only accessible when enabled (dev profile) and requires ROLE_ADMIN</li>
|
||||
* <li>Druid monitoring: requires ROLE_ADMIN (Druid also has its own login page)</li>
|
||||
* <li>Admin/Actuator paths: requires ROLE_ADMIN</li>
|
||||
* <li>SpringDoc UI paths: public (API docs are for developers)</li>
|
||||
* <li>All other authenticated requests: evaluated by {@code RbacPermission.hasPermission}</li>
|
||||
* <li>X-Frame-Options: SAMEORIGIN (prevents clickjacking while allowing H2 console iframe)</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
//http.csrf().disable(); //注释就是使用 csrf 功能
|
||||
http.headers().frameOptions().disable();//解决 in a frame because it set 'X-Frame-Options' to 'DENY' 问题
|
||||
//http.anonymous().disable();
|
||||
http.authorizeRequests()
|
||||
.antMatchers("/login/**", "/initUserData")//不拦截登录相关方法
|
||||
.permitAll()
|
||||
//.antMatchers("/user").hasRole("ADMIN") // user接口只有ADMIN角色的可以访问
|
||||
//.anyRequest()
|
||||
//.authenticated()// 任何尚未匹配的URL只需要验证用户即可访问
|
||||
.anyRequest()
|
||||
.access("@rbacPermission.hasPermission(request, authentication)")//根据账号权限访问
|
||||
.and()
|
||||
.formLogin()
|
||||
//.loginPage("/")
|
||||
.loginPage("/login") //登录请求页
|
||||
.loginProcessingUrl("/login") //登录POST请求路径
|
||||
.usernameParameter("username") //登录用户名参数
|
||||
.passwordParameter("password") //登录密码参数
|
||||
.defaultSuccessUrl("/index") //默认登录成功页面
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.accessDeniedHandler(customAccessDeniedHandler) //无权限处理器
|
||||
.and()
|
||||
.logout()
|
||||
.logoutSuccessUrl("/login?logout"); //退出登录成功URL
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// Fix: SAMEORIGIN instead of disable() — prevents clickjacking, allows H2 iframe in same origin
|
||||
http.headers(headers -> headers
|
||||
.frameOptions(frame -> frame.sameOrigin())
|
||||
);
|
||||
|
||||
// CSRF: disable for H2 console path only (H2 web console does not send CSRF tokens)
|
||||
if (h2ConsoleEnabled) {
|
||||
http.csrf(csrf -> csrf.ignoringRequestMatchers("/h2-console/**"));
|
||||
}
|
||||
|
||||
// Build the RBAC expression manager with ApplicationContext so @bean references work
|
||||
DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
|
||||
expressionHandler.setApplicationContext(applicationContext);
|
||||
WebExpressionAuthorizationManager rbacManager = new WebExpressionAuthorizationManager(
|
||||
"@rbacPermission.hasPermission(request, authentication)");
|
||||
rbacManager.setExpressionHandler(expressionHandler);
|
||||
|
||||
http.authorizeHttpRequests(auth -> {
|
||||
// Publicly accessible
|
||||
auth.requestMatchers(
|
||||
"/login/**", "/initUserData",
|
||||
// SpringDoc OpenAPI UI and spec endpoint (developer tools, no sensitive data)
|
||||
"/swagger-ui/**", "/swagger-ui.html", "/v3/api-docs/**"
|
||||
).permitAll();
|
||||
|
||||
// H2 console: admin-only, dev profile only
|
||||
if (h2ConsoleEnabled) {
|
||||
auth.requestMatchers("/h2-console/**").hasRole("ADMIN");
|
||||
}
|
||||
|
||||
// Druid monitoring, Actuator, Admin UI: restricted to ROLE_ADMIN
|
||||
auth.requestMatchers("/druid/**").hasRole("ADMIN");
|
||||
auth.requestMatchers("/actuator/**").hasRole("ADMIN");
|
||||
auth.requestMatchers("/admin/**").hasRole("ADMIN");
|
||||
|
||||
// All remaining requests evaluated by RBAC permission bean
|
||||
auth.anyRequest().access(rbacManager);
|
||||
});
|
||||
|
||||
http.formLogin(form -> form
|
||||
.loginPage("/login")
|
||||
.loginProcessingUrl("/login")
|
||||
.usernameParameter("username")
|
||||
.passwordParameter("password")
|
||||
.defaultSuccessUrl("/index")
|
||||
);
|
||||
|
||||
http.logout(logout -> logout
|
||||
.logoutSuccessUrl("/login?logout")
|
||||
);
|
||||
|
||||
http.exceptionHandling(ex -> ex
|
||||
.accessDeniedHandler(customAccessDeniedHandler)
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义获取用户信息接口
|
||||
* Authentication manager wired with BCrypt — replaces the old
|
||||
* {@code configure(AuthenticationManagerBuilder)} override.
|
||||
*/
|
||||
@Override
|
||||
public void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(BCryptPasswordEncoder passwordEncoder) {
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
return new ProviderManager(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码加密算法
|
||||
* @return
|
||||
*/
|
||||
/** BCrypt password encoder bean. */
|
||||
@Bean
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user