Spring Security无法渲染自定义登录页面问题求助
问题描述
刚开始学习不使用Spring Boot的Spring Security,尝试渲染自定义登录页面时发现:@GetMapping("/login")的控制器方法已被多次调用,但Spring无法渲染login.jsp页面,浏览器返回404错误。
相关配置代码
根配置类(AppConfig)
@Configuration @PropertySource("classpath:application.properties") @EnableJpaRepositories("com.example.repository") @Import(SecurityConfiguration.class) public class AppConfig { @Autowired private Environment environment; @Bean public DataSource getDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(environment.getProperty("jdbc.driverClassName")); dataSource.setUrl(environment.getProperty("jdbc.url")); dataSource.setUsername(environment.getProperty("jdbc.username")); dataSource.setPassword(environment.getProperty("jdbc.password")); return dataSource; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean(); entityManagerFactory.setDataSource(getDataSource()); entityManagerFactory.setPackagesToScan("com.example.model"); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); entityManagerFactory.setJpaVendorAdapter(vendorAdapter); entityManagerFactory.setJpaProperties(additionalProperties()); return entityManagerFactory; } @Bean public PlatformTransactionManager getTransactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); return transactionManager; } public Properties additionalProperties() { Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "update"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect"); properties.setProperty("hibernate.show_sql", "true"); return properties; } @Bean(name = "mvcHandlerMappingIntrospector") public HandlerMappingIntrospector mvcHandlerMappingIntrospector() { return new HandlerMappingIntrospector(); } }
Dispatcher上下文配置类(DispatcherServletConfig)
@Configuration @EnableWebMvc @ComponentScan(basePackages = { "com.example.controller"}) public class DispatcherServletConfig { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/view/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }
安全配置类(SecurityConfiguration)
@EnableWebSecurity @ComponentScan("com.example.service") public class SecurityConfiguration{ @Resource private UserDetailsService userDetailsService; @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsService); authenticationProvider.setPasswordEncoder(passwordEncoder()); return authenticationProvider; } @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder builder = http.getSharedObject(AuthenticationManagerBuilder.class); builder.authenticationProvider(authenticationProvider()); return builder.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests() .requestMatchers("/").authenticated() .and() .formLogin() .loginPage("/login") .permitAll(); return http.build(); } }
WebApplicationInitializer代码
public class MyWebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext container) throws ServletException { //Declaring root context AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(AppConfig.class); //Manage the life cycle of root application context container.addListener(new ContextLoaderListener(rootContext)); //Creating dispatcher servlet's application context AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext(); dispatcherContext.register(DispatcherServletConfig.class); //Register and Map dispatcher servlet ServletRegistration.Dynamic dispatcherServlet = container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext)); dispatcherServlet.addMapping("/"); dispatcherServlet.setLoadOnStartup(1); } }
控制器类代码(LoginController)
@Controller public class LoginController { @GetMapping("/") public String indexPage() { return "index"; } @GetMapping("/login") public String login() { System.out.println("have been called"); return "login"; } }
调试日志
cookie: JSESSIONID=9B485D0205797A978E6A25A54DBBAC88 Security filter chain: [ DisableEncodeUrlFilter WebAsyncManagerIntegrationFilter SecurityContextHolderFilter HeaderWriterFilter LogoutFilter UsernamePasswordAuthenticationFilter RequestCacheAwareFilter SecurityContextHolderAwareRequestFilter AnonymousAuthenticationFilter ExceptionTranslationFilter AuthorizationFilter ] ************************************************************ 2023-03-13T20:24:01.020+09:00 INFO 13976 --- [nio-8080-exec-5] Spring Security Debugger : ************************************************************ Request received for GET '/login': org.apache.catalina.connector.RequestFacade@4ff9b4c7 servletPath:/login pathInfo:null headers: host: localhost:8080 connection: keep-alive cache-control: max-age=0 upgrade-insecure-requests: 1 user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69 accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 sec-fetch-site: none sec-fetch-mode: navigate sec-fetch-user: ?1 sec-fetch-dest: document sec-ch-ua: "Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" accept-encoding: gzip, deflate, br accept-language: ja,en;q=0.9,en-GB;q=0.8,en-US;q=0.7 cookie: JSESSIONID=9B485D0205797A978E6A25A54DBBAC88 Security filter chain: [ DisableEncodeUrlFilter WebAsyncManagerIntegrationFilter SecurityContextHolderFilter HeaderWriterFilter LogoutFilter UsernamePasswordAuthenticationFilter RequestCacheAwareFilter SecurityContextHolderAwareRequestFilter AnonymousAuthenticationFilter ExceptionTranslationFilter AuthorizationFilter ] ************************************************************ 2023-03-13T20:24:01.029+09:00 INFO 13976 --- [nio-8080-exec-6] Spring Security Debugger : ************************************************************ Request received for GET '/login': org.apache.catalina.connector.RequestFacade@388d5e7e servletPath:/login pathInfo:null headers: host: localhost:8080 connection: keep-alive cache-control: max-age=0 upgrade-insecure-requests: 1 user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69 accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 sec-fetch-site: none sec-fetch-mode: navigate sec-fetch-user: ?1 sec-fetch-dest: document sec-ch-ua: "Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" accept-encoding: gzip, deflate, br accept-language: ja,en;q=0.9,en-GB;q=0.8,en-US;q=0.7 cookie: JSESSIONID=9B485D0205797A978E6A25A54DBBAC88 Security filter chain: [ DisableEncodeUrlFilter WebAsyncManagerIntegrationFilter SecurityContextHolderFilter HeaderWriterFilter LogoutFilter UsernamePasswordAuthenticationFilter RequestCacheAwareFilter SecurityContextHolderAwareRequestFilter AnonymousAuthenticationFilter ExceptionTranslationFilter AuthorizationFilter ]
解决方法
确认文件路径匹配
检查login.jsp是否存在于/WEB-INF/view/目录下,文件名大小写与控制器返回的"login"完全一致(Linux/macOS区分大小写)。完善Security放行规则
修改SecurityConfiguration中的securityFilterChain方法,确保登录页面及相关资源完全放行,避免循环重定向:@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests() .requestMatchers("/login", "/login/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll(); return http.build(); }添加静态资源处理
在DispatcherServletConfig中启用默认Servlet处理静态资源,避免Spring MVC拦截:@Configuration @EnableWebMvc @ComponentScan(basePackages = { "com.example.controller"}) public class DispatcherServletConfig implements WebMvcConfigurer { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/view/"); viewResolver.setSuffix(".jsp"); return viewResolver; } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }排查视图解析问题
添加Spring MVC调试日志,查看视图解析过程:logging.level.org.springframework.web=DEBUG从日志中确认Spring是否尝试加载
/WEB-INF/view/login.jsp,以及是否存在文件访问权限问题。
内容的提问来源于stack exchange,提问作者Rohit
相关产品推荐
相关产品推荐

