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

禁用http.csrf()后控制器中Principal为null的问题排查

解决禁用CSRF后Controller中Principal为null的问题

看起来你遇到的问题是:为了测试REST接口临时禁用CSRF后,原本能正常获取的Principal突然变成了null。这大概率是因为禁用CSRF的同时,你的安全配置没确保认证机制依然生效,或者测试请求没正确携带认证信息。下面给你一步步的解决方案:

1. 检查并修正Security配置

首先,确保你的WebSecurityConfigurerAdapter配置中,禁用CSRF后依然开启了认证规则和合适的认证方式(比如HTTP Basic,适合REST测试)。这里给你一个正确的配置示例:

@Configuration
@EnableWebSecurity
public class TestSecurityConfig extends WebSecurityConfigurerAdapter {

    // 配置测试用的内存用户
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("test_user")
            .password("{noop}test_pass") // {noop}表示不使用密码加密,仅用于测试
            .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable() // 禁用CSRF,适配REST接口
            .authorizeRequests()
                .anyRequest().authenticated() // 强制所有请求都需要认证
            .and()
            .httpBasic(); // 启用HTTP Basic认证,方便测试时传递用户名密码
    }
}

这里的关键是:禁用CSRF后,一定要保留authorizeRequests().anyRequest().authenticated()来强制认证,同时通过httpBasic()开启简单的认证方式——REST接口通常用无状态的HTTP Basic比Session认证更合适。

2. 确保测试请求携带认证信息

如果用MockMvc做单元测试,一定要在请求中添加HTTP Basic认证信息,否则Spring Security无法识别用户,Principal自然为null。示例代码如下:

@Autowired
private MockMvc mockMvc;

@Test
public void testPostEndpoint() throws Exception {
    mockMvc.perform(post("/your-rest-endpoint")
            .contentType(MediaType.APPLICATION_JSON)
            .content("{\"field\":\"value\"}")
            // 携带测试用户的认证信息
            .with(httpBasic("test_user", "test_pass")))
            .andExpect(status().isOk())
            // 可以额外验证返回内容中是否包含用户名
            .andExpect(jsonPath("$.username").value("test_user"));
}

如果用Postman、curl等工具测试,需要在请求头中添加Authorization字段,值为Basic [base64编码的用户名:密码]。比如test_user:test_pass编码后是dGVzdF91c2VyOnRlc3RfcGFzcw==,所以请求头就是:

Authorization: Basic dGVzdF91c2VyOnRlc3RfcGFzcw==

3. 确认Controller中Principal的获取方式

确保你在Controller中正确获取Principal,推荐使用@AuthenticationPrincipal注解直接拿到用户详情,比原生Principal更方便:

@PostMapping("/your-rest-endpoint")
public ResponseEntity<YourResponse> handlePostRequest(
        @RequestBody YourPojo requestBody,
        @AuthenticationPrincipal UserDetails userDetails) {
    // 这里可以直接拿到用户名
    String username = userDetails.getUsername();
    // ... 业务逻辑
    return ResponseEntity.ok(new YourResponse(username));
}

当然,如果你坚持用Principal参数,只要请求已认证,它也会被正确填充:

@PostMapping("/your-rest-endpoint")
public ResponseEntity<YourResponse> handlePostRequest(
        @RequestBody YourPojo requestBody,
        Principal principal) {
    if (principal != null) {
        String username = principal.getName();
        // ...
    }
    // ...
}

问题根源解释

为什么禁用CSRF会导致Principal为null?其实两者本身没有直接关联,但很可能是你在修改配置时,不小心移除了认证规则(比如anyRequest().authenticated()),或者原本依赖Session的认证在CSRF禁用后出现了状态丢失。而REST接口本身适合无状态的认证方式(比如HTTP Basic),所以调整配置为HTTP Basic后,就能同时满足禁用CSRF和正常获取用户信息的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:35:26