Запретить HTML только для чтения текстовые поля, которые будут изменены - PullRequest
0 голосов
/ 25 января 2020

Я разрабатываю веб-приложение Grails, модули которого, в основном, должны реализовывать интерфейс master-detail. В качестве подхода я хочу представить на рассмотрение следующий код:

Мастер-класс:

import org.codehaus.groovy.grails.web.json.JSONArray

class MyForm {
    String name
    String address
    String detail
    BigDecimal total

    static hasMany = [details: MyFormDetail]

    static constraints = {
        name()
        address()
        detail()
        total()
    }

    static mapping = {
        detail type: 'text'
    }

    def beforeInsert = {
        def detailJSON = new JSONArray(detail)
        detailJSON.each {
            def quantity = it.getAt("quantity").toString().toBigDecimal()
            def description = it.getAt("description").toString()
            def unitaryPrice = it.getAt("unitaryPrice").toString().toBigDecimal()
            def subtotal = it.getAt("subtotal").toString().toBigDecimal()
            def myFormDetail = new MyFormDetail(
                    quantity: quantity,
                    description: description,
                    unitaryPrice: unitaryPrice,
                    subtotal: subtotal
            )
            this.addToDetails(myFormDetail)
        }
    }
}

Подробный класс:

class MyFormDetail {
    Integer quantity
    String description
    BigDecimal unitaryPrice
    BigDecimal subtotal

    static belongsTo = [myForm: MyForm]

    static constraints = {
        quantity()
        description()
        unitaryPrice()
        subtotal()
    }
}

myFormUtilities. js вспомогательный файл:

$(document).ready(function() {
    $("#detailTable").jqGrid({
        datatype: "local",
        height: 100,
        colNames: ["QUANTITY","DESCRIPTION","UNIT. PRICE","SUBTOTAL"],
        colModel:[ 
            {name:'quantity',index:'quantity', width:100},
            {name:'description',index:'description', width:400},
            {name:'unitaryPrice',index:'unitaryPrice', width:100},
            {name:'subtotal',index:'subtotal', width:100}],
        caption: "DETAIL"
    });

    createTable();

    $("#addRow").bind("click",addRow);
    $("#removeRow").bind("click",removeRow);

    $("#quantity, #unitaryPrice").bind("keyup",calculateTotal);

    function calculateTotal(){
        let quantity = parseFloat($("#quantity").val());
        let unitaryPrice = parseFloat($("#unitaryPrice").val());
        let subtotal = quantity*unitaryPrice;
        $("#subtotal").val(subtotal);
    }

    function addRow(){
        let row = new Object();
        row.quantity = $("#quantity").val();
        row.description = $("#description").val();
        row.unitaryPrice = $("#unitaryPrice").val();
        row.subtotal = $("#subtotal").val();
        let detailJSON = ($("#detail").val()=="")?"[]":$("#detail").val();
        let mydata = $.parseJSON(detailJSON);
        mydata.push(row);
        $("#detail").val(JSON.stringify(mydata));
        createTable();
    }

    function removeRow(){
        let filaId = parseInt($('#detailTable').jqGrid('getGridParam','selrow')) - 1;
        let mydata = $.parseJSON($("#detail").val());
        let nuevoMydata = new Array();
        for(let i=0;i<mydata.length;i++){
            if(filaId!=i)
                nuevoMydata.push(mydata[i]);
        }
        $("#detail").val(JSON.stringify(nuevoMydata));
        createTable();
    }

    function createTable(){
        let total = 0;
        let aRow = new Object();
        let detailJSON = ($("#detail").val()=="")?"[]":$("#detail").val();
        let mydata = $.parseJSON(detailJSON);
        $("#detailTable").jqGrid("clearGridData", true);
        for(let i=0;i<mydata.length;i++){
            aRow = mydata[i];
            total += parseFloat(aRow.subtotal);
            $("#detailTable").jqGrid('addRowData',i+1,aRow);
        }
        $("#total").val(total);
    }
});

Это отображаемая форма (я знаю, что это автоматически сгенерированное представление, но считаю его очень основополагающим c GUI макет, пожалуйста): master-detail mockup

Итак, вот эти проблемы:

  • И Subtotal и Total поля - это вычисляемые поля, которые были установлены read-only для предотвращения изменения пользователем своего контента, но я обнаружил, что с помощью инспектора элементов браузера их содержимое и свойства (например, только для чтения) можно изменять.

  • То же самое происходит с detail. Если его содержимое изменяется, при сохранении экземпляра будет возникать ошибка на стороне сервера, поскольку блоку beforeInsert требуется действительная строка JSON для создания экземпляров detail. Это поле также используется для генерации детализации JqGrid. Это поле будет скрыто.

Все в этом примере работает должным образом, но любопытный пользователь может вызвать ошибку.

  • Есть ли способ чтобы предотвратить изменение текста этих полей даже с помощью инспектора?
  • Есть ли еще место , чтобы защитить эти данные от изменений?
  • Я использую это detail поле для сохранения строки JSON, и это мое решение для реализации шаблона master-detail. Есть ли другой способ улучшить эту реализацию, избегая необходимости определять поле для этой строки?

Ответы [ 3 ]

0 голосов
/ 25 января 2020

Извините, нет простого ответа для защиты данных, отправляемых из браузера. Любой пользовательский ввод должен рассматриваться как грязный на вашем сервере.

Данные, отправленные в POST или GET, всегда могут быть изменены на стороне клиента или даже созданы оптом (с помощью чего-то вроде CURL или Postman). По этой причине вы всегда должны проверять пользовательский ввод на стороне сервера и вычислять ваши итоговые значения там.

0 голосов
/ 27 января 2020

Если у вас есть свойства, которые вы не хотите участвовать в привязке данных массовых свойств, вам следует настроить их на bindable: false.

У вас есть следующее:

class MyFormDetail {
    Integer quantity
    String description
    BigDecimal unitaryPrice
    BigDecimal subtotal

    static belongsTo = [myForm: MyForm]

    static constraints = {
        quantity()
        description()
        unitaryPrice()
        subtotal()
    }
}

Вы может изменить это на что-то вроде этого:

class MyFormDetail {
    Integer quantity
    String description
    BigDecimal unitaryPrice
    BigDecimal subtotal

    static belongsTo = [myForm: MyForm]

    static constraints = {
        quantity()
        description()
        unitaryPrice bindable: false
        subtotal bindable: false
    }
}

Если у вас есть что-то вроде этого:

MyFormDetail mfd = new MyFormDetail()
// The body of the request will be read and
// bound to mfd but subtotal and unitaryPrice
// will NOT be updated...
mfd.properties = request

Если вы сделаете это, вы все равно сможете напрямую присвоить значения этим свойствам:

mfd.subtotal = // ... some value here

Другой вариант - отправить вашу форму в действие контроллера, которое принимает объект команды. Этот CO может выглядеть следующим образом ...

class FormCommand {
    Integer quantity
    String description
}

Если у вас есть действие, которое выглядит так:

class SomeController {
    def someAction(FormCommand co) {
        // it won't matter if subtotal
        // and unitaryPrice are in the
        // request, the co only has the
        // the properties you want so now
        // you can use those to update the
        // corresponding persistent entity
        // in a safe way
    }
}
0 голосов
/ 25 января 2020

Если ваши пользователи имеют доступ к инспектору / веб-консоли, то они имеют доступ ко всем данным на стороне клиента, которые вы делаете (они ограничены только своими знаниями о том, как использовать инструменты разработчика браузера). Они могут изменить текст любого элемента или значение ввода. Таким образом, единственное место, которое будет на 100% безопасным для хранения этих данных, находится на стороне сервера.

То, что вы можете попробовать, это, по крайней мере, заставит их гораздо больше работать для обновления чего-либо, и может предотвращение событий изменения, вызывающих сервер, заключается в том, чтобы скрыть каждый ввод (например, CSS) и заменить его в видимом HTML элементом <span> (или некоторым другим элементом, не являющимся формой). Затем убедитесь, что при изменении значения <input> вы обновляете текст соответствующего диапазона. Что-то вроде:

$("#subtotalSpan").html( $("#subtotal").val() );

добавлено в функцию calcTotal. Если вы хотите, чтобы они даже не могли редактировать HTML диапазона, вы можете go еще дальше и нарисовать значение в <canvas> вместо <span>. Они не смогут изменять текст, нарисованный в элементе canvas.

...