You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
]

解决方法

  1. 确认文件路径匹配
    检查login.jsp是否存在于/WEB-INF/view/目录下,文件名大小写与控制器返回的"login"完全一致(Linux/macOS区分大小写)。

  2. 完善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();
    }
    
  3. 添加静态资源处理
    在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();
        }
    }
    
  4. 排查视图解析问题
    添加Spring MVC调试日志,查看视图解析过程:

    logging.level.org.springframework.web=DEBUG
    

    从日志中确认Spring是否尝试加载/WEB-INF/view/login.jsp,以及是否存在文件访问权限问题。


内容的提问来源于stack exchange,提问作者Rohit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 21:57:01