Spring MVC + Spring Security + ThymeleafでCSRF対策を有効にしつつajax通信を行う。

開発

Spring Securityを有効にするとtokenを利用したCSRF対策を行うことができます。

フォームに対しては自動的にtype=”hidden”のinputが作成され、POST時にtokenが自動的に送信されます。

では、ajaxでRESTfulな通信をする場合はどうでしょう。

$.ajax({
type: "post",
url: '/test',
data: json,
success: function(data, dataType){
    $message.text('完了しました。');
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
    $message.text('失敗しました。: ' + textStatus + ' : ' + errorThrown);
}

みたいな処理を書いても「Request method ‘POST’ not supported」というエラーが返ってきてしまいます。

対策はここに書いてありますが、
まとめてみましょう。

メタデータにトークンを埋め込む

リクエストのパラメータにトークンを埋め込められない場合には、メタデータに埋め込む処理を加えます。
Thymeleafを使ってメタデータに埋め込むための記述は以下のとおりです。

(なんか表示がおかしくなるのでmetaをmet@と表記しています)



ajax通信時にリクエストヘッダにトークンを加える

メタデータに埋め込んだトークンをajax通信時にヘッダに加えます。
jQueryではajaxSendというメソッドでajax通信時のイベントを拾えるので、
下記のメソッドをonready時に呼び出しておきます。

    /**
     * Spring SecurityのCSRF対策によるトークンをajax通信で受け渡しできるようにします。
     */
    function setCsrfTokenToAjaxHeader() {
        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);
        });
    }

これでエラーなくコントローラーまで処理が飛んで行くはずです。

コントローラー側は@ModelAttribute(“xxx”) @RequestBodyを引数に取れば、dataに突っ込んだJSONがModelAttributeに指定した型に変換されますので、
そのまま利用しちゃってください。

もし@ResponseBodyを指定して戻り値にJacksonなどでString化したJSONオブジェクトを返す場合、
produces = “application/json; charset=utf-8″を指定してあげないと文字化けを起こしますので注意してください。

    @RequestMapping(method= RequestMethod.POST, produces = "application/json; charset=utf-8")
    @ResponseBody
    private String other(@ModelAttribute("user") @RequestBody User user) throws IOException {
        // 処理
    }

    @Autowired
    private User user;

    @ModelAttribute("user")
    private User getUser() {
        return user;
    }

コメント

タイトルとURLをコピーしました