使用Spring Security进行AJAX请求时出现403 Forbidden错误。

5

我有一个基于Spring Boot、Spring Security和Thymeleaf的网站,我在某些情况下还使用了Ajax。

问题:我在Spring Security中使用表单登录安全性。在浏览器中,登录后我可以访问REST API(GET),但是使用Ajax时,即使我的Ajax请求在Cookie中包含会话ID,它仍然返回HTTP 403错误。

安全配置:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
    .antMatchers("/admin/**").hasRole("ADMIN")
    .antMatchers("/rest/**").hasRole("ADMIN")
            .anyRequest().permitAll()
     .and()
     .formLogin().loginPage("/sign-in-up")
            .loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/");

}

REST API我已经正确测试过了。
@RestController
@RequestMapping("rest/categories")
public class CategoriesRest {
@Autowired
private CategoryService categoryService;

@GetMapping("/")
public ResponseEntity<List<Category>> findAll() {
    List<Category> all = categoryService.getAll();
    if (all.isEmpty()) {
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
    return new ResponseEntity<>(all, HttpStatus.OK);
}

@GetMapping("/{id}")
public ResponseEntity<Category> findById(@PathVariable int id) {
    Category obj = categoryService.get(id);
    if (obj == null) {
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
    return new ResponseEntity<>(obj, HttpStatus.OK);
}

@PostMapping("/")
public ResponseEntity<Category> createMainSlider(@RequestBody Category obj) {
    System.out.println("-------rest Post");

    return new ResponseEntity<>(categoryService.add(obj), HttpStatus.CREATED);
}

@PutMapping("/{id}")
public ResponseEntity<Category> update(@RequestBody Category obj, @PathVariable int id) {
    Category obj1 = categoryService.update(obj);

    System.out.println(obj);
    return new ResponseEntity<>(obj1, HttpStatus.OK);
}

@DeleteMapping("/{id}")
public ResponseEntity<Category> deleteEmp(@PathVariable int id) {
    categoryService.delete(id);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

}

我的Ajax代码:
$('.deleteBtn').bind('click',function(e){
        e.preventDefault();
        $.ajax({
            type:'DELETE',
            url : "/rest/categories/"+$(e.currentTarget).data('id'),
             xhrFields: {
                  withCredentials: true
               },
             success : function(result) {
                 location.reload();
                 console.log(result);
               },
              error : function(e) {
                alert("Error!")
                console.log("ERROR: ", e);
              }
        })
    })

我的ajax请求头像这样: ajax请求头 编辑:[GET]请求正常工作,但[PUT,POST,DELETE]不起作用。

你好,Hamod,你只针对 url:"/rest/categories/"+$(e.currentTarget).data('id') 遇到了 403 错误吗?其他 API 是否正常工作或者也出现了 403 错误? - Romil Patel
我的意思是,您能否获取除DELETE方法以外的响应或者所有方法和API都面临着403错误? - Romil Patel
如果您查看客户端发送到服务器的请求,将会发现 Authorization 头部被设置,并且它正在使用 Base64 编码的字符串发送基本身份验证。这很可能是由于您在 JavaScript 中设置了 withCredentials: true 导致的。我建议您尝试将其移除并观察结果。 - hooknc
是的,我尝试使用“withCredentials:true”来解决问题,但实际上它仍然存在。我将其删除后问题仍未得到解决。 - MuhammedH
@PatelRomil 我发现 [GET] 请求可以工作,但其他请求不行!! - MuhammedH
2个回答

9

.csrf().disable().cors()为什么起作用了?

CSRF是跨站请求伪造的缩写。

简单来说,它是一种令牌,与请求一起发送以防止攻击。为了使用Spring Security CSRF保护,我们首先需要确保对任何修改状态的操作使用正确的HTTP方法(PATCH, POST, PUT, 和 DELETE - 而不是GET)。

一些框架通过使无效的CSRF令牌无效化用户会话来处理无效的CSRF令牌,但这会引起自己的问题。相反,默认情况下,Spring Security的CSRF保护会产生一个HTTP 403访问被拒绝错误。

Ajax和JSON请求

如果您正在使用JSON,则不可能在HTTP参数中提交CSRF令牌。相反,您可以在HTTP头中提交该令牌。一个典型的模式是在您的meta标记中包含CSRF令牌。

<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>

//jQuery
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);
});

非常感谢,解释得非常好 :) - MuhammedH
@MuhammedHamod 欢迎,如果答案有帮助的话,您可以接受它。 - Romil Patel

4
感谢大家的帮助,我通过禁用CSRF来解决了这个问题,我添加了以下代码:

.csrf().disable().cors()

因此,在我的Spring安全配置中:
        http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/rest/**").hasRole("ADMIN")
            .anyRequest().permitAll().and().formLogin().loginPage("/sign-in-up")
            .loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
            .and()
            .csrf().disable().cors();

====== 编辑:
@Patel提供了有用的解释...感谢他

注:本段已有英文非中文,请确认是否需要翻译。


你好,我已经添加了相关细节。 - Romil Patel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接