我有一个SpringBoot2.5.1应用程序,它具有SpringSecurity和thymleaf。
为了传递csrf令牌,我遵循了spring文档链接https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html#csrf-包括csrf令牌ajax
index.html包含上述链接中提到的标题
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
下面是在所有ajax请求中包含令牌的配置
$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
然而,在添加了上述更改之后,现有的junit测试用例开始失败
@AutoConfigureMockMvc
@WebMvcTest(MyUserInterfaceController.class)
class MyUserInterfaceControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldGetIndexPageWithNoUnknownUserName() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("index"))
.andExpect(model().attribute("username", "UNKNOWN_USER"));
}
}
在我做出与csrf相关的更改之前,上面的测试用例是通过的。然而,下面的测试用例通过了csrf更改或没有更改。
@SneakyThrows
@Test
@WithMockTestUser
void shouldSaveSettlementRemark() {
//var userDtos= ...
mockMvc.perform(post("/users")
.contentType(APPLICATION_JSON)
.content(userDtos))
.andDo(print())
.andExpect(status().isOk());
}
根据spring文档,如果SpringSecurityJAR在类路径中,csrf在默认情况下是启用的&因此post请求需要csrf令牌,否则请求被禁止。使用mockmvc编写的测试用例不遵循这种行为。
为什么在post请求传递时,get请求在stacktrace下失败?如何解决这个问题?
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "_csrf.token" (template: "index" - line 6, col 24)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
在运行应用程序时,ui正在呈现,但在后端,即使没有启动任何用户请求,thymeleaf也会尝试呈现index.html,这会引发以下错误。
":"org.thymeleaf.TemplateEngine","message":"[THYMELEAF][http-nio-8080-exec-1] Exception processing template \"index\": An error happened during template parsing (template: \"templates/index.html\")","stack_trace":"<#24d6b41b> o.s.e.s.SpelEvaluationException: EL1007E: Property or field 'token' cannot be found on null\n\tat o.s.e.s.a.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:213)
我可以通过添加空检查来修复上述两个错误,如下所示:
这是正确的方法还是我在损害应用程序的安全性?
<meta name="_csrf" th:content="${_csrf != null} ? ${_csrf.token} : '' "/>
<meta name="_csrf_header" th:content="${_csrf != null} ? ${_csrf.headerName} : '' "/>
Web安全配置如下所示:
@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomProperties customProperties;
@Override
protected void configure(HttpSecurity http) throws Exception {
if (customProperties.isEnabled()) {
log.info("authentication enabled.");
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.redirectionEndpoint()
.baseUri(customProperties.getAuthorizationResponseUri())
.and()
.userInfoEndpoint(userInfo -> userInfo.userService(new CustomOAuth2UserService()::loadUser));
} else {
log.info("authentication disabled.");
http.authorizeRequests()
.anyRequest()
.permitAll();
}
}
}
1条答案
按热度按时间vc6uscn91#
默认情况下,启用csrf时,spring security会在会话启动时生成csrf令牌。如果您没有会话,将不会有csrf令牌。您的第二个测试使用经过身份验证的用户,将有一个会话,并成功。
在禁用csrf时,您可以使用以下元标记来处理这种情况:
另一种方法是更改配置中的csrf令牌策略:
并使用以下元标记:
但我不知道你的帖子为什么有用。如果启用了csrf,我认为您必须使用.with(csrf()),如下所示。