是否可以在 vue-router 中锁定除一条以外的所有路由?安全吗?或者我应该使用另一种方法?

Is it Possible to lock all routes except one, in vue-router ? Is it secure? Or maybe I should use another method?

我想做一个在线考试,这个考试有 5 页,有一个倒计时计时器(120 秒),每页有 4 个问题。 120 秒后用户将自动转到下一页,或者他们可以在此之前单击下一步按钮。

Laravel5.4 和 VueJs,用户回答的每个问题都有一个 Ajax 请求。我想要的是阻止用户看到其他页面。每个页面最多只能显示 120 秒。用户不应该能够点击后退按钮并查看之前的页面。这可能吗?

我想用 Vuejsvue-router 创建这个应用程序,但我不知道如何用 vue-router 实现它,我做了一些研究,但没有太多结果!

或者我不应该使用 vue-router,而是使用我自己的简单路由器,例如:

$("#page1").show();
$("#page2").hide();
$("#page3").hide();
.
.
// after 120 secs 
$("#page1").hide();
$("#page2").show();
$("#page3").hide();
.
.
 // i think this is not secure !

如有任何想法,我们将不胜感激。谢谢。

更新: 在此考试中,用户可以看到从 words table 中随机选择的 English words 的列表,除此之外什么都没有!用户点击他认为他知道其含义的每一个词!并且每次点击 ajax 请求保存 results table 中单词的 id。另外,除了actual words之外,还有一个fake_words table是随机选择50个词,如果用户点击假词超过3次,测试就会失败。最终结果将告诉用户他有多少词汇量。

更新 2: 我试着用 vue-router 来做这个,但在开始编码之前,我想也许它不应该用 vue-router 来实现因为所有问题都是在一个查询中随机从数据库中获取的,所以在考试开始之前,所有问题都被发送(ajax)到浏览器,现在我该怎么办?将它们分成不同的数组并将每个问题数组发送到我的一个页面?我必须这样做吗?我不能只为他们使用一个 v-for 吗?如果我决定更改问题数量怎么办?然后我想我每次都必须触摸我的代码并为 vue-router 创建新页面或删除其中一个页面。

如果这真的是像考试一样的高风险代码,您应该重新考虑您的方法:“Never trust the client”。我建议为后端编写代码来解决你的问题。

1) 使用中间件保护端点,即:

2) 在访问页面时创建时间戳

3) 120 秒后不接受用户的新post(回答)

注意:我希望他们也必须在线回答,否则无法确保安全:如果将问题放在浏览器中 window,他们可以始终缓存此问题,即使是最好的他们可以给屏幕拍照的代码。

编辑: 我会使用 pagination 一个一个地发送问题,但有时间限制。

edit 2: 当开发工具打开时,我也会向服务器发送通知。你可以试试https://github.com/sindresorhus/devtools-detect

也许这些片段会有所帮助:

const app = new Vue({
    el: '#app',
    data: {
        stepOne: 1,
    }
});

<step v-if="step==1"></step>

timeInterval = setInterval(function() {
    goToNextStep(2);
    this.$parent.stepOne = 0;
}.bind(this), 12000);

就安全性而言,选择JS框架影响不大。 Vue.js 是一个很好的选择。这是一个可以帮助您入门的示例代码:

<template>
  <div>
    <div id="question" v-if="question">
      {{question}}
      <button @click="nextQuestion();" v-if="hasNextQuestion()"></button>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        curQuestionNo: 0,
        maxQuestions: 4
        question: null,
        timer: null
      }
    },
    methods: {      
      fetchQuestion () {
        // return one question at a time from the server. Storing all questions on the client is not recommended.
        var url = 'https://myapi.com/question?curQuestionNo=' + this.curQuestionNo;

        axios.get(url) // or however you prefer to make ajax calls
          .then(res => {
            this.question = res.question;
            this.curQuestionNo++;
            this.stopTimer();
            if(this.hasNextQuestion()) {
              this.startTimer();
            }
          })
          .catch(() => {
            // do some error handling
          });
      },
      startTimer () {
        this.timer = setTimeout(() => {
          this.nextQuestion();
        }, 120 * 1000);
      },
      stopTimer () {
        clearTimeout(this.timer);
      },
      nextQuestion () {
        if(this.curQuestionNo > 0) {
          this.markAnswered(function() {  
            this.fetchQuestion();
          })
        } else {
          this.fetchQuestion();
        }
      },
      markAnswered (cb) {
        // record the answered question on server
        // server should only send the next question after this
        var url = 'https://myapi.com/mark-complete?curQuestionNo=' + this.curQuestionNo;

        axios.post(url) 
          .then(res => {
            this.fetchQuestion();
          })
          .catch(() => {
            // do some error handling
          });
      },
      hasNextQuestion () {
        return this.maxQuestions - (this.curQuestionNo + 1) > 0;
      }
    },
    created () {
      this.nextQuestion();      

    }
  }
</script>

在服务器端,我推荐类似下面的方法:

  1. 每个请求向客户发送一个问题
  2. 确保在 API returns 给客户的下一个问题之前,用户已经回答了上一个问题或允许回答问题的时间范围已经过期。这可以非常简单地实现,只需在您的数据存储中保留一个计数器来记录每个用户最后回答(或过期)的问题编号。不应允许用户回答等于或小于记录中数字的问题。服务器应该只发送比记录中的数字大一的问题编号。

示例代码(不确定 Laravel 5 语法,但您应该明白了):

Route::get('question', function(){
    $curQuestionNo = intVal(Input::get('curQuestionNo'));
    $user = User::find($userId); // get the logged in user, for example
    if($user->current_question_number === $curQuestionNo) {
      $question = Question::where('question_no', $curQuestionNo + 1);
      $question->sent_at = time(); // store the time when the question was sent
      return $question
    } else {
      // deny access
    }
});

Route::post('mark-complete', function(){
    $curQuestionNo = intVal(Input::get('curQuestionNo'));
    $user = User::find($userId); // get the logged in user, for example
    $question = Question::find($curQuestionNo);
    if($question->sent_at > 120 seconds) {
     // do not record answers
    } else {
      // record answers
    }
    $user->current_question_number = $curQuestionNo;
    $user->save();
});