Ajax POST 无法与 spring security _csrf 一起使用,但 GET 方法有效

Ajax POST wont work with spring security _csrf but GET method works

我正在尝试使用 jquery ajax post 方法提交数据,但到目前为止我没有这样做。我正在使用 spring 安全 4.0.4.RELEASE 和 Spring 框架 4.2.5.RELEASE。在谷歌搜索了这么多关于为什么 GET 有效而 POST 无效之后。我发现 _csrf 令牌与为什么 POST 方法不被允许有关。我尝试在 URL 处添加令牌,然后它现在允许 POST 方法,但它会为传递给数据参数的所有数据检索空值。这是我的示例代码。

控制器:

@RequestMapping(value = "/ajaxAddDeliveryType", method = RequestMethod.GET, produces = "application/json")
    @ResponseBody
    public JsonResponse addDeliveryType(
            @ModelAttribute(value = "delivery") DeliveryType deliveryType,
            BindingResult result) {

        System.out.println("deliveryType "+deliveryType.toString());

        JsonResponse res = new JsonResponse();

        /* Validate all the input. it return "SUCCESS" or "FAIL" status */
        jsonResponse(res, result, deliveryType);

        if (res.getStatus().equalsIgnoreCase("success")) {
            /*
             * If result status is Success insert the data into database
             */
            deliveryTypeService.saveDeliveryType(deliveryType);
        }

        return res;
    }


/*
     * This method will validate all input field in form and returning response.
     * If result has error detected it will set the status to "FAIL". If no
     * error occured in result it will set the status to "SUCCESS".
     */
    public JsonResponse jsonResponse(JsonResponse res, BindingResult result,
            DeliveryType deliveryType) {

        /* Set error message if text field is empty */

        ValidationUtils.rejectIfEmptyOrWhitespace(result,
                "mainte_delivery_type", "Delivery type can not be empty");

        ValidationUtils.rejectIfEmptyOrWhitespace(result, "delivery_weight",
                "Delivery weight not be empty");

        ValidationUtils.rejectIfEmptyOrWhitespace(result, "delivery_price",
                "Delivery price can not be empty.");

        if (!deliveryTypeService.isDeliveryTypeUnique(deliveryType.getId(),
                deliveryType.getMainte_delivery_type())) {

            /* Set status to fail */
            res.setStatus("FAIL");

            /* Set error message if delivery type already exist */
            result.rejectValue("mainte_delivery_type",
                    "Delivery type already exists. Please fill in different value");

            res.setResult(result.getAllErrors());

        }

        if (result.hasErrors()) {

            /* Set status to fail */
            res.setStatus("FAIL");

            /*
             * Collect all error messages for text field that not properly
             * assign value
             */
            res.setResult(result.getAllErrors());

        }

        /* Validate if the weight or price is more than zero / 0 */
        else if (deliveryType.getDelivery_price().toString()
                .equalsIgnoreCase("0")
                || deliveryType.getDelivery_weight().toString()
                        .equalsIgnoreCase("0")) {

            /* Set status to fail */
            res.setStatus("FAIL");

            /* Set error message if delivery type already exist */
            result.rejectValue("delivery_price",
                    "Delivery weight/price value should be more than '0'");

            res.setResult(result.getAllErrors());

        }

        else {

            deliveryTypes.clear(); /* Clear array list */
            deliveryTypes.add(deliveryType);
            /*
             * Add employee model object into list
             */
            res.setStatus("SUCCESS"); /* Set status to success */
            res.setResult(deliveryTypes); /* Return object into list */

        }

        return res;
    }

提交 ajax POST 方法时的控制台输出:

> deliveryType DeliveryType [id=0, mainte_delivery_type=null, delivery_price=null, delivery_weight=null]

提交时的控制台输出ajax GET方法:

deliveryType DeliveryType [id=0, mainte_delivery_type=test, delivery_price=123, delivery_weight=12]

Javacript 代码:

/*
 * This function will validate all the input field inside the form and return a
     * response if result got an error. otherwise if no error in result is found it
     * will insert the data into database
     */
    function validateAndInsertUsingAjax(action, message) {

        /* Disable button to prevent redundant ajax request */
        $("#btnDeliveryType").prop('disabled', true);


        var datastring = $("#myform").serialize();

        $.ajax({

            type : "GET",
            url : myContext + '/' + action+ '?_csrf=' + $("#token").val(),
            data : datastring,
            contentType : "application/json; charset=utf-8",
            datatype : "json",
            crossDomain : "TRUE",
            success : function(response) {
                var stringResponse = JSON.stringify(response)
                // we have the response

                console.log("response " + stringResponse);

                var obj = JSON.parse(stringResponse);

                if (obj.status == "SUCCESS") {
                    /*
                     * Enable button to make ajax request again after response
                     * return
                     */
                    $("#btnDeliveryType").prop('disabled', false);

                    var userInfo = "<ol>";

                    for (i = 0; i < obj.result.length; i++) {

                        /* Create html elements */

                        userInfo += "<br><li><b>Delivery Type</b> : "
                                + obj.result[i].mainte_delivery_type;

                        userInfo += "<br><li><b>Delivery weight (kg)</b> : "
                                + obj.result[i].delivery_weight;

                        userInfo += "<br><li><b>Delivery Price</b> : "
                                + obj.result[i].delivery_price;

                    }

                    userInfo += "</ol>";

                    /* Draw message in #info div */
                    $('#info').html(message + userInfo);

                    /* Show and hide div */
                    $('#error').hide('slow');
                    $('#info').show('slow');

                    /* Populate DataTable */
                    populateDataTable();

                    /* Hide modal */
                    $('#modalAddDeliveryType').modal('hide');

                } else {
                    /*
                     * Enable button to make ajax request again after response
                     * return
                     */
                    $("#btnDeliveryType").prop('disabled', false);

                    var errorInfo = "";

                    for (i = 0; i < response.result.length; i++) {

                        errorInfo += "<br>" + (i + 1) + ". "
                                + response.result[i].code;

                    }

                    /* Show error message from response */
                    $('#error').html(
                            "Please correct following errors: " + errorInfo);

                    /* Show and hide div */
                    $('#info').hide('slow');
                    $('#error').show('slow');

                }

            },

            /* xhr.status shows server respond */
            error : function(xhr, desc, err) {
                /*
                 * Enable button to make ajax request again after response return
                 */

                $("#btnDeliveryType").prop('disabled', false);
                if (xhr.status == 500) {
                    alert('Error: ' + "Server not respond ");
                }
                if (xhr.status == 403) {
                    alert('Error: ' + "Access Denied");
                }

            }

        });

    }

JSP 页数:

<div class="modal fade" id="modalAddDeliveryType" tabindex="-1"
        role="dialog" aria-labelledby="exampleModalCenterTitle"
        aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h3 class="modal-title" id="exampleModalLongTitle"></h3>
                    <button type="button" class="close" data-dismiss="modal"
                        aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>

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

                <!-- Form Text field -->
                <form:form method="GET" modelAttribute="delivery" name="myform"
                    id="myform">
                    <form:input type="hidden" path="id" id="id" />
                    <div class="modal-body">

                        <!-- Input Delivery type -->
                        <div>
                            <label for="mainte_delivery_type">Delivery type: </label>
                            <form:input path="mainte_delivery_type" id="mainte_delivery_type"
                                class="form-control" placeholder="Delivery type" />
                            <form:errors path="mainte_delivery_type" cssClass="error" />
                        </div>

                        <!-- Input Delivery weight -->
                        <div>
                            <label for="delivery_weight">Delivery weight (kg): </label>
                            <form:input type="number" min="0" path="delivery_weight"
                                id="delivery_weight" class="form-control"
                                placeholder="Delivery weight(kg)" />
                            <form:errors path="delivery_weight" cssClass="error" />
                        </div>

                        <!-- Input Delivery price -->
                        <div>
                            <label for="delivery_price">Delivery price: </label>
                            <form:input type="number" min="0" path="delivery_price"
                                id="delivery_price" class="form-control"
                                placeholder="Delivery price" />
                            <form:errors path="delivery_price" cssClass="error" />
                        </div>




                        <div id="error" class="error"></div>

                    </div>

                    <div class="modal-footer">

                        <!-- Close button -->
                        <button type="button" class="btn btn-secondary"
                            data-dismiss="modal">Close</button>

                        <!-- Register button -->
                        <input type="submit" class="btn btn-primary" value="Save"
                            id="btnDeliveryType" onClick="insertOrUpdateDeliveryType()" />

                    </div>
                </form:form>
            </div>
        </div>
    </div>

我希望有人能给我提供一个更好的方法来实现这个 POST 方法。我是 spring 安全部门的新人。

CSRF 令牌用于防止远程第 3 方伪造请求。基本上,攻击者会按原样复制表格,然后强迫攻击者控制的网站上毫无戒心的用户代表用户向通常合法的网站发送请求,其中不可避免地会包含用户的 cookie。

因此,CSRF 令牌至少可以说是随机的,并且锁定到会话。这就像在您提交的每个表单中包含一个 cookie,并且 GET 请求也应该包含这些,只要一个操作正在完成。

假设您没有试图在这里做任何恶意的事情,并且可以完全控制您的机器人,您可以轻松地向网站发送初始 GET 请求,建立会话,并从您的会话中获取唯一的 CSRF 令牌HTML(将作为值嵌入到 <input> 标签中)。

编辑:顺便说一下,如果您控制了目标站点(生成此 CSRF 令牌的站点!),那么显然您可以将自己列入白名单,或者通过 [= 将该信息专门发送给用户20=]。如果是这样的话,有办法解决这个问题。