Ajax POST不能使用spring security _csrf,但GET方法有效

时间:2018-05-07 08:45:50

标签: ajax spring-mvc post spring-security csrf

我尝试使用jquery ajax post方法提交数据,但到目前为止我没有这样做。我使用的是spring security 4.0.4.RELEASE和Spring framework 4.2.5.RELEASE。经过这么多吨谷歌搜索为什么GET工作和POST没有。我发现_csrf令牌有一些事情要做,为什么不允许使用POST方法。我尝试在URL处添加令牌,然后它现在允许POST方法,但它为传递给data参数的所有数据检索空值。这是我的示例代码。

控制器:

@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方法。我是春天安全的新人。

1 个答案:

答案 0 :(得分:0)

CSRF令牌用于防止远程第三方伪造请求。基本上,攻击者将完全按原样复制表单,然后强制在攻击者控制的网站上的毫无戒心的用户,代表用户发送请求,该请求将不可避免地包括用户的cookie,通常是合法的网站。

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

假设您没有尝试在此处执行任何恶意操作并完全控制您的机器人,您可以轻松地向网站发送初始GET请求,建立会话,并从HTML中获取您的会话唯一的CSRF令牌(将作为ctrlKey标记内的值嵌入。)

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