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

Spring Boot部署Kubernetes时Thymeleaf模板解析失败问题排查

解决Spring Boot部署Kubernetes后Thymeleaf模板找不到的问题

问题描述

Spring Boot应用本地运行正常,部署到Kubernetes后出现错误:Error resolving template [login],提示模板不存在或无法被配置的模板解析器访问,无法加载登录页面。

排查与解决步骤

1. 确认打包后的Jar包含模板文件

首先检查应用Jar包是否正确包含了src/main/resources/templates目录下的文件:

  • 本地执行命令查看:
    jar tf your-application.jar | grep templates
    
  • 在Kubernetes中进入Pod检查:
    kubectl exec <your-pod-name> -- jar tf /app/your-application.jar | grep templates
    # 或者直接查看目录结构(如果Jar已解压)
    kubectl exec <your-pod-name> -- ls /app/BOOT-INF/classes/templates
    

如果没有找到模板文件,说明打包时未将资源文件包含进去,需调整构建配置:

  • Maven:确保pom.xml的resources配置包含所有资源文件:
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
        </resources>
    </build>
    
  • Gradle:确保build.gradle中正确配置资源目录:
    sourceSets {
        main {
            resources {
                srcDirs = ['src/main/resources']
            }
        }
    }
    

2. 修正Thymeleaf前缀配置的斜杠

你的application.properties中Thymeleaf前缀缺少末尾斜杠,导致拼接视图路径错误:

# 原配置(错误)
spring.thymeleaf.prefix=classpath:/templates
# 修改为(正确)
spring.thymeleaf.prefix=classpath:/templates/

原配置会将视图名user/test拼接成classpath:/templatesuser/test.html,显然无法找到模板。Kubernetes环境对路径解析更严格,必须添加末尾斜杠。

3. 验证Spring Security的登录页配置

WebSecurityConfig中配置了.loginPage("/login"),该配置指定处理登录页的接口路径,而非直接指定模板名。需确保/login的Controller能正常被访问:

  • 临时测试:将.loginPage("/login")改为.loginPage("user/test"),直接指定模板路径,若能正常加载,说明Security拦截了/login请求,需检查Controller的@RequestMapping是否正确,或调整Security的放行规则。

4. 检查容器文件权限

部分情况下,容器运行用户缺乏读取Jar内文件的权限,导致模板无法访问:

  • 进入Pod查看Jar文件权限:
    kubectl exec <your-pod-name> -- ls -l /app/your-application.jar
    
  • 若权限不足,可在Dockerfile中添加权限配置:
    RUN chmod 644 /app/your-application.jar
    

或临时使用root用户运行Pod(仅用于排查,生产环境不推荐)。

5. 临时关闭模板位置检查(排查用)

若确认模板存在但仍报错,可临时关闭Thymeleaf的模板位置检查:

spring.thymeleaf.check-template-location=false

此操作仅用于排查,生产环境建议保持true以确保配置正确性。


附用户提供的相关代码与配置

Controller代码

@RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login(HttpServletRequest request) {
    String referrer = request.getHeader("Referer");
    ModelAndView modelAndView = new ModelAndView();
    request.getSession().setAttribute("Referer", referrer);
    modelAndView.setViewName("user/test");
    return modelAndView;
}

test.html代码

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
    <h2>Test</h2>
</body>
</html>

WebSecurityConfig代码

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/api/**").permitAll()
            .antMatchers("/login").permitAll()
            .antMatchers("/reminder").permitAll()
            .antMatchers("/reset").permitAll()
            .antMatchers("/user/update-password").hasAnyAuthority("ADMIN", "MODERATOR", "USER")

            .anyRequest().authenticated()
            .and()
            .formLogin().successHandler(customizeAuthenticationSuccessHandler)
            .loginPage("/login").failureUrl("/login?error")
            .usernameParameter("username")
            .passwordParameter("password")
            .and()
            .logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/login?logout")
            .and()
            .exceptionHandling()
            //                .accessDeniedHandler(accessDeniedHandler)
            .and()
            .csrf().ignoringAntMatchers(new String[]{"/api/**"}).and();
}

/**
 *
 * @param web
 * @throws Exception
 */
@Override
public void configure(WebSecurity web) {
    web.expressionHandler(new DefaultWebSecurityExpressionHandler() {
        @Override
        protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
            WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi);
            root.setDefaultRolePrefix(""); //remove the prefix 
            return root;
        }
    }).ignoring().antMatchers(
            "/resources/**",
            "/static/**",
            "/assets/**",
            "/global_assets/**",
            "/webjars/**",
            "/css/**",
            "/js/**",
            "/img/**"
    ).antMatchers("/webjars/**", "/css/**", "/js/**");
}

application.properties配置

spring.thymeleaf.check-template-location=true
spring.thymeleaf.prefix=classpath:/templates
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 21:40:21