我正在将我的项目迁移到 Spring boot 3。它配置了 jsp 页面。问题是当使用 Spring security 6 运行时,页面不再加载,浏览器上出现错误 ERR_TOO_MANY_REDIRECTS
我得到了很多调试日志,但都是一样的:
2024-06-27T10:24:47.982+02:00 DEBUG 55771 --- [jsp] [0.1-8080-exec-2] o.s.w.servlet.view.InternalResourceView : View name 'login', model {}
2024-06-27T10:24:47.983+02:00 DEBUG 55771 --- [jsp] [0.1-8080-exec-2] o.s.w.servlet.view.InternalResourceView : Forwarding to [/WEB-INF/jsp/login.jsp]
2024-06-27T10:24:47.984+02:00 DEBUG 55771 --- [jsp] [0.1-8080-exec-4] o.s.w.servlet.view.InternalResourceView : View name 'login', model {}
2024-06-27T10:24:47.984+02:00 DEBUG 55771 --- [jsp] [0.1-8080-exec-4] o.s.w.servlet.view.InternalResourceView : Forwarding to [/WEB-INF/jsp/login.jsp]
我的安全配置文件 src/main/java/org/example/jsp/WebSecurityConfig.java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form.loginPage("/login")
.loginProcessingUrl("/login")
.permitAll()
)
.logout(logout -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login")
.permitAll()
);
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
src/main/webapp/WEB-INF/jsp/login.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="${pageContext.request.contextPath}/login" method="post">
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username"/>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password"/>
</div>
<div>
<button type="submit">Login</button>
</div>
<c:if test="${param.error != null}">
<div>Invalid username or password.</div>
</c:if>
</form>
</body>
</html>
src/main/java/org/example/jsp/JspController.java
@Controller
public class JspController {
@GetMapping("/login")
public String getLogin() {
return "login";
}
@GetMapping("/home")
public String home() {
return "home";
}
}
以及 pox.xml 中的依赖项
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<!-- JSP dependencies -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet.jsp</groupId>
<artifactId>jakarta.servlet.jsp-api</artifactId>
<version>3.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
<!-- <version>2.0.0</version>-->
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Server dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
我使用过Spring boot 3.2.7
应用程序属性(src/main/resources/application.properties)文件包含
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
还尝试添加
src/main/java/org/example/jsp/WebMvcConfig.java:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
}
我不明白我做错了什么。请指导我解决这个问题,非常感谢。 :)
更新:
以下是日志:
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.security.web.FilterChainProxy : Securing GET /login
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.security.web.FilterChainProxy : Secured GET /login
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.web.servlet.DispatcherServlet : GET "/login", parameters={}
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.example.jsp.JspController#getLogin()
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8, application/signed-exchange;v=b3;q=0.7]
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.w.servlet.view.InternalResourceView : View name 'login', model {}
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.w.servlet.view.InternalResourceView : Forwarding to [/WEB-INF/jsp/login.jsp]
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.security.web.FilterChainProxy : Securing GET /WEB-INF/jsp/login.jsp
2024-06-28T14:02:41.048+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-06-28T14:02:41.049+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.s.w.s.HttpSessionRequestCache : Saved request http://localhost:8080/WEB-INF/jsp/login.jsp?continue to session
2024-06-28T14:02:41.049+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://localhost:8080/login
2024-06-28T14:02:41.049+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.a.c.c.C.[.[localhost].[/].[jsp] : The response class [class org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterResponse] could not be unwraped to the Catalina response class and will be closed immediately after the forward
2024-06-28T14:02:41.049+02:00 DEBUG 77220 --- [jsp] [.1-8080-exec-10] o.s.web.servlet.DispatcherServlet : Completed 302 FOUND
然后为了解决重定向问题,我在安全配置的 PermitAll 中添加了
WEB-INF
.requestMatchers("/login", "/error", "/webjars/**", "/css/**", "/WEB-INF/**").permitAll()
现在的新错误是
2024-06-28T14:04:54.841+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.security.web.FilterChainProxy : Securing GET /login
2024-06-28T14:04:54.848+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.security.web.FilterChainProxy : Secured GET /login
2024-06-28T14:04:54.849+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/login", parameters={}
2024-06-28T14:04:54.850+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.example.jsp.JspController#getLogin()
2024-06-28T14:04:54.857+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8, application/signed-exchange;v=b3;q=0.7]
2024-06-28T14:04:54.857+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.w.servlet.view.InternalResourceView : View name 'login', model {}
2024-06-28T14:04:54.858+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.w.servlet.view.InternalResourceView : Forwarding to [/WEB-INF/jsp/login.jsp]
2024-06-28T14:04:54.860+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.security.web.FilterChainProxy : Securing GET /WEB-INF/jsp/login.jsp
2024-06-28T14:04:54.862+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.security.web.FilterChainProxy : Secured GET /WEB-INF/jsp/login.jsp
2024-06-28T14:04:54.906+02:00 ERROR 77243 --- [jsp] [0.1-8080-exec-1] o.a.c.c.C.[.[localhost].[/].[jsp] : Servlet.service() for servlet [jsp] threw exception
java.lang.ClassNotFoundException: jakarta.servlet.jsp.jstl.core.ConditionalTagSupport
.
.
.
2024-06-28T14:04:54.908+02:00 DEBUG 77243 --- [jsp] [0.1-8080-exec-1] o.s.web.servlet.DispatcherServlet : Error rendering view [org.springframework.web.servlet.view.InternalResourceView: name 'login'; URL [/WEB-INF/jsp/login.jsp]]
我的JSP页面也经过了身份验证,所以我添加了permitAll,帮助我解决了问题,同时还暂时禁用了csrf
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers( "/webjars/**", "/WEB-INF/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(formLogin -> formLogin
.loginPage("/login")
.failureUrl("/login?error=true")
.permitAll()
)
.logout(logout -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login")
.permitAll()
)
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
此外,我使用了错误的依赖关系,
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
相反应该使用
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.0</version>
</dependency>