如何整合Spring Security+Thymeleaf+HTMX避免403错误?
问题描述
我是Spring Security的新手,尤其不熟悉它与Thymeleaf、HTMX的整合。我希望保留Spring Security的功能同时使用HTMX,但目前遇到了403错误。若在配置中禁用CSRF,应用会丢失所有认证视图,表现得像未集成Spring Security一样。
相关代码
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="https://thymeleaf.org"> <head> <meta charset = "ISO-8859-1" /> <link href = "css/styles.css" rel = "stylesheet" /> <title>Logowanie</title> <script th:src="@{/js/scripts.js}" defer></script> <script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"></script> <meta name="_csrf" content="${_csrf.token}"/> <meta name="_csrf_header" content="${_csrf.headerName}"/> </head> <body hx-boost="true"> <div class="center"> <h4>Witamy w panelu klienta!</h4> <button id="myButton" th:onclick="hello()">Hello Button</button> <button id="button2" th:onClick="info()">info in console</button> <p id="sayHello" hx-post="/hello" hx-swap="outerHTML">ppp</p> <!-- <div sec:authorize="hasRole('USER')">Text visible to user.</div> <div sec:authorize="hasRole('ADMIN')">Text visible to admin.</div> <div sec:authorize="isAuthenticated()"> Text visible only to authenticated users. </div> Authenticated username: <div sec:authentication="name"></div> Authenticated user roles: <div sec:authentication="principal.authorities"></div> --> </div> </body> </html>
Controller
@Controller public class MainView { @GetMapping("/") public String mainView(Model model) { model.addAttribute("password", "dupka"); return "index"; } @GetMapping("/panel") public String panelView(Model model) { model.addAttribute("password", "dupeczka"); return "test"; } @PostMapping("/hello") // @PostMapping(headers = "HX-Request") public String furtherInfo() { return "fragments/invoices"; // return "test"; } }
原配置文件
@Configuration @EnableWebSecurity public class MyGlobalSecurityConfig { @Autowired protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user") .password("{noop}password") .roles("USER") .and() .withUser("admin") .password("{noop}password") .roles("ADMIN"); } }
尝试禁用CSRF的修改
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable(); return http.build(); }
解决方案
1. 修复Spring Security配置,保留CSRF并配置完整认证规则
禁用CSRF后丢失认证视图的核心原因是:你只配置了csrf().disable(),没有添加任何授权规则,Spring Security默认会允许所有请求匿名访问,因此不会触发登录页面,看起来像未集成Security。
修改配置文件,添加完整的SecurityFilterChain,包含授权规则、登录/登出配置:
@Configuration @EnableWebSecurity public class MyGlobalSecurityConfig { @Autowired protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user") .password("{noop}password") .roles("USER") .and() .withUser("admin") .password("{noop}password") .roles("ADMIN"); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // 配置授权规则 .authorizeHttpRequests(auth -> auth .requestMatchers("/", "/js/**", "/css/**").permitAll() // 允许首页和静态资源匿名访问 .anyRequest().authenticated() // 其他所有请求需要认证 ) // 启用默认登录页面,允许匿名访问登录页 .formLogin(form -> form .permitAll() ) // 配置登出,允许匿名访问登出接口 .logout(logout -> logout .permitAll() ) // 保留CSRF保护,不需要禁用 .csrf(csrf -> csrf // 可选:如果某些HTMX请求不需要CSRF(不推荐),可以忽略 // .ignoringRequestMatchers("/hello") ); return http.build(); } }
2. 让HTMX自动携带CSRF令牌
你已经在index.html中添加了CSRF的meta标签,但HTMX不会自动读取并携带这些令牌,需要添加一段脚本让HTMX在每个请求中带上CSRF头:
在index.html的<head>或<body>末尾添加:
<script> document.addEventListener('htmx:configRequest', function(evt) { const csrfToken = document.querySelector('meta[name="_csrf"]').content; const csrfHeader = document.querySelector('meta[name="_csrf_header"]').content; evt.headers[csrfHeader] = csrfToken; }); </script>
3. 验证Thymeleaf的Spring Security集成(可选)
如果你要启用页面中的sec:标签,需要确保引入了Thymeleaf的Spring Security依赖,并在HTML中添加命名空间:
<html lang="en" xmlns:th="https://thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/extras/spring-security">
这样配置后,HTMX的POST请求会正确携带CSRF令牌,不会触发403错误,同时Spring Security的认证功能(登录页、权限控制)也能正常工作。
内容的提问来源于stack exchange,提问作者Karol Wilczyński
相关产品推荐
相关产品推荐

