AjaxのPOSTでデータを送信しようとしてうまくいかないとき


大参考にさせていただきました
ajaxのPOST送信で403が返却される場合の対処方法

環境

spring boot
spring security
thymeleaf

SpringSecurityを使っているときはCSRFトークンが必要

SpringSecurityを使ってログイン機能を作った場合、そのアプリケーション全体でCSRF対策が有効になる

すごくシンプルに言うと、

POSTメソッドを使うときは、入力データだけじゃなくてCSRFトークンも送らないとアクセス禁止になる!

th:actionでトークンが自動で挿入される

参考

Ajaxじゃなくて普通にformで画面遷移する場合は、

<form th:action="@{hoge}" method="post">

</form>

のように書けば

<form action="/login" method="post">
    <!-- Spring MVCの機能と連携して出力されたCSRFトークン値のhidden項目 -->
    <input type="hidden"
        name="_csrf" value="63845086-6b57-4261-8440-97a3c6fa6b99" />
    <!-- omitted -->
</form>

超便利!!

でもAjaxのときは自動で生成されず、Forbiddenが返ってくる

chromeのデベロッパーツールでヘッダーを確認してみると、

403のステータスコード。アクセスしたらアカンって言われてる。

俺がええって言うてんのに!

SpringSecurityが勝手に「だってCSRFトークンないからあきまへん」って頑なになっとるんですな。

CSRFトークンを手動でAjaxのヘッダーにセットする


// htmlファイルの最後にCSRFをセット
// ちょっと変な場所
</body>
<meta th:name="_csrf" th:content="${_csrf.token}"/>
<meta th:name="_csrf_header" th:content="${_csrf.headerName}"/>
</html>
</body>の直前だとエラーになります。

ちょっと変な場所だけど、ここじゃないとダメでした

<head>の中に書いても読み込まれません

テンプレートエンジンを利用している場合、layoutファイルに次の様な書いてある場合は、個別のhtmlファイルにmetaを挿入しても読み込まれない

<head xmlns:th="http://www.thymeleaf.org"
    th:fragment="base_header(title, scripts, links)">


base_header(title, scripts, links)
この部分で、個別のファイルで<head>の中に書いてある
title
script
link
は読み込めるようになっている

metaはない → headにmetaを追加してもCSRFトークンはセットできない

postで403が返ってきたらCSRFトークンが送信できているか確認しよう!

これほんと初歩でしょうけど。

これで丸一日かかってしまいました。。。