【问题标题】:Spring RestController requires CSRFSpring RestController 需要 CSRF
【发布时间】:2015-10-28 17:05:27
【问题描述】:

我已经构建了一个 RestController,它应该可以帮助管理 Web 应用程序中的标签。我将通过 AJAX(使用 JQuery)调用所有方法,但 Spring-Boot 的默认安全性会导致以下结果(从 Postman 调用时):

{ “时间戳”:1438800538949, “状态”:403, “错误”:“禁止”, "message": "在请求参数 '_csrf' 或标头 'X-CSRF-TOKEN' 上发现无效的 CSRF Token 'null'。", “路径”:“/标签/添加” }

获取 CSRF 令牌以执行 JQuery 调用的最佳方法是什么?我认为这比禁用它更好。在 PostMan 中使用时会怎样?

这是控制器的代码:

@RestController
public class TagController {

@Autowired
private TagService tagService;

@RequestMapping(name = "/tag/list", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<TagList> getTagList() {
    TagList result = new TagList(tagService.list());
    return new ResponseEntity<TagList>(result, HttpStatus.OK);
}

@RequestMapping(name = "/tag/add", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> addTag(@RequestBody AlterTagForm form) {
    try {
        tagService.addTag(form.getArticleId(), form.getTagName());
        return new ResponseEntity<>(HttpStatus.ACCEPTED);
    } catch (EntityNotFoundException ex) {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

@RequestMapping(name = "/tag/remove", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> removeTag(@RequestBody AlterTagForm form) {
    try {
        tagService.removeTag(form.getArticleId(), form.getTagName());
        return new ResponseEntity<>(HttpStatus.ACCEPTED);
    } catch (EntityNotFoundException ex) {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}
}

【问题讨论】:

  • 因为是Rest api,所以可以在配置中禁用csrf check。通常这是由httpSecurity.csrf().disable()WebSecurityConfigurerAdapter .configure() 方法中完成的。也可以通过httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 使其无状态。由于我没有看 Spring boot,不知道它是如何完成的。

标签: jquery spring spring-mvc spring-boot csrf


【解决方案1】:

首先,您需要在 html 中包含 csrf 令牌:

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

你也可以在表格中这样做,像这样:

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

但我更喜欢在元数据中这样做,因为那是在标题中,并且我为每个带有表单的模板都有这个(我使用百里香、片段等)

其次,你需要修改你的ajax调用:

var csrfToken = $('#_csrf').attr("content");
var csrfHeader = $('#_csrf_header').attr("content");
$.ajax({
    url : '/tag/remove',
    type: 'POST',
    data: ({ id: id }),
    beforeSend: function(xhr){
            xhr.setRequestHeader(csrfHeader, csrfToken);
    }
}).done(function(data, textStatus, jqXHR) {
        alert('finish');
});

设置requestHeader是这个最重要的。

PS:有时可能会发生csrf header 为空的情况,您可以轻松替换它:

var csrfHeader = $('#_csrf_header').attr("content")

这样:

var csrfHeader = 'X-CSRF-TOKEN';

【讨论】:

  • 所以这意味着我将无法将其称为 webservice-wise,也无法通过 Postman 之类的工具?
  • 我不确定,因为 spring security 会生成 csrf 令牌。我无法想象春天可以验证邮递员的令牌。我认为你需要一个定制的 csrf 实现(一个你在春天从邮递员那里验证令牌的服务)
猜你喜欢
  • 2019-12-30
  • 1970-01-01
  • 1970-01-01
  • 2018-04-23
  • 1970-01-01
  • 2017-04-10
  • 2019-04-08
  • 1970-01-01
  • 2015-07-03
相关资源
最近更新 更多