Spring Boot3升级后JSP视图渲染遇Spring Security6权限问题
Spring Boot 3(Spring Security 6)升级后JSP视图跳转403问题
问题背景
正在将应用从Spring Boot 2.7升级到Spring Boot 3,同步升级Spring Security 6。现有配置如下:
MVC视图配置
spring.mvc.view.prefix=/WEB-INF/view/ spring.mvc.view.suffix=.jsp
控制器代码
@RequestMapping("/") public String home() { return "home"; }
该控制器返回视图名home,会渲染/WEB-INF/view/home.jsp页面。
安全配置(升级后初始版本)
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain config(HttpSecurity http) throws Exception { http.authorizeHttpRequests((auth) -> auth .requestMatchers("/").permitAll() .anyRequest().authenticated() ); }
问题现象
升级后访问localhost/时,浏览器会被重定向到localhost/WEB-INF/view/home.jsp,同时返回403错误——因为该路径未被授权。
临时解决方法是在安全配置中添加.requestMatchers("/", "/WEB-INF/**").permitAll(),此时功能恢复正常(停留在/并渲染JSP页面),但这种配置不合理且多余,毕竟/WEB-INF目录本就不应该允许直接访问。
开启调试日志后,Spring输出以下内容:
DEBUG [requestURL=/] o.s.security.web.FilterChainProxy : Securing GET / DEBUG [requestURL=/] o.s.security.web.FilterChainProxy : Secured GET / DEBUG [requestURL=/] o.s.security.web.FilterChainProxy : Securing GET /WEB-INF/view/home.jsp DEBUG [requestURL=/] o.s.security.web.FilterChainProxy : Secured GET /WEB-INF/view/home.jsp
极简复现示例
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>jsptest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>jsptest</name> <packaging>war</packaging> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> </dependencies> <build> <finalName>app</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Application.java
@SpringBootApplication @Controller public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public SecurityFilterChain config(HttpSecurity http) throws Exception { http .authorizeHttpRequests((auth) -> auth .requestMatchers("/", "/WEB-INF/view/**").permitAll() .anyRequest().authenticated() ); return http.build(); } @RequestMapping("/") public String home() { return "home"; } }
application.properties
spring.mvc.view.prefix=/WEB-INF/view/ spring.mvc.view.suffix=.jsp
home.jsp
hello
问题原因
Spring Security 6调整了默认拦截的DispatcherType范围:
- Spring Boot 2.x(Spring Security 5)默认只拦截
REQUEST、ERROR、ASYNC类型的请求,FORWARD请求会被跳过。当控制器返回视图名时,Spring MVC通过FORWARD转发到JSP路径,这个转发请求不会经过Security过滤器链,因此不会触发权限校验。 - Spring Boot 3(Spring Security 6)默认拦截所有DispatcherType,包括
FORWARD。此时控制器返回视图后的转发请求会被Security拦截,而/WEB-INF/**路径未被授权,因此返回403错误。
正确解决方案
不需要开放/WEB-INF/**的直接访问权限,只需配置Spring Security放行FORWARD类型的请求,或者恢复到2.x的拦截规则即可:
方案1:放行所有FORWARD类型请求
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain config(HttpSecurity http) throws Exception { http.authorizeHttpRequests((auth) -> auth .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll() .requestMatchers("/").permitAll() .anyRequest().authenticated() ); return http.build(); } }
方案2:仅拦截REQUEST类型请求(和2.x行为一致)
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import java.util.Collections; import java.util.Set; import org.springframework.web.servlet.DispatcherType; @Configuration public class SecurityConfig { @Bean public SecurityFilterChain config(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { MvcRequestMatcher mvcMatcher = new MvcRequestMatcher(introspector, "/**") { @Override protected Set<DispatcherType> getDispatcherTypes() { return Collections.singleton(DispatcherType.REQUEST); } }; http .securityMatcher(mvcMatcher) .authorizeHttpRequests((auth) -> auth .requestMatchers("/").permitAll() .anyRequest().authenticated() ); return http.build(); } }
以上两种方案都能解决问题,且不会暴露/WEB-INF目录的直接访问权限。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

