2017-02-25 55 views
0

问题陈述在Flask中,除非先访问另一条路由,否则我该如何防止路由被访问?

我正在研究一个Flask web应用程序,该应用程序显示表中的项目列表。用户可以选择一行并按下Delete按钮来删除该项目。但是,在从数据库中删除项目之前,用户首先会路由到确认屏幕,其中显示一些项目详细信息以及Confirm按钮。确认页面的网址遵循以下模式:dashboard/confirm-delete/<id>以及实际删除页面的网址遵循以下模式:dashboard/delete/<id>。请参阅下面的admin/views.py了解更多详情。

在系统正常工作时,我遇到的问题是用户可以通过在地址栏中输入dashboard/delete/<id>(其中<id>被替换为实际项目ID)来简单地跳过确认页面。

质询

有没有办法来防止用户访问dashboard/delete/<id>除非他们首先去dashboard/confirm-delete/<id>(确认屏幕)?或者,我的方法错了,是否有更好的方法?

当前代码:

功能在我dashboard.html页面时选择了行和删除按钮被按下称为:

$('#remove').click(function() { 
    var id = getId(); 
    window.location.href="/dashboard/confirm-delete" + $.trim(id); 
    }); 

确认按钮confirm-delete.html(删除确认页):

<a class="btn btn-default" href="{{ url_for('admin.delete_item', id=item.id) }}" role="button">Confirm Delete</a> 

我的admins/views.py

@admin_blueprint.route('dashboard/confirm-delete/<id>') 
@login_required 
@groups_required(['admin'}) 
def confirm_delete_item(id) 
    item = Item.query.get_or_404(id) 
    return render_template('admin/confirm-delete.html', item=item, title="Delete Item") 

@admin_blueprint.route('dashboard/delete/<id>', methods=['GET', 'POST']) 
@login_required 
@groups_required(['admin'}) 
def delete_item(id) 
    item = Item.query.get_or_404(id) 
    db.session.delete(item) 
    db.commit() 
    return redirect(url_for('home.homepage')) 

SOLUTION

基于标记的答案,因为接受我解决了这个问题,如下所示:

首先,我创建了一个新的形式,以处理confirm-delete.htmlSubmit按钮:

admin/forms.py

from flask_wtf import FlaskForm 
from wtforms import SubmitField 

class DeleteForm(FlaskForm): 
    submit = SubmitField('Confirm') 

我取代Confirm Button代码与下列到confirm-delete.html

<form method="post"> 
    {{ form.csrf_token }} 
    {{ form.submit }} 
</form> 

最后,我合并两者的功能app/views.py如下:

@admin_blueprint.route('dashboard/confirm-delete/<id>', methods=['GET', 'POST']) 
@login_required 
@groups_required(['admin'}) 
def confirm_delete_item(id) 
    form = DeleteForm() 
    item = Item.query.get_or_404(id) 
    if form.validate_on_submit(): 
    if form.submit.data: 
     db.session.delete(item) 
     db.commit() 
     return redirect(url_for('home.homepage')) 
    return render_template('admin/confirm-delete.html', item=item, form=form, title="Delete Item") 

通过这种方式,用户不能旁路通过在地址栏中输入特定链接来删除确认屏幕,并且可以简化代码。

+0

我想到的,我会在页面上设置一个cookie的第一件事,其中删除按钮,然后在确认页面上检查该cookie。 – coralvanda

回答

0

正如在评论中已经提到的,解决您的问题的一种方式是在用户发送请求时检查某个cookie。但个人而言,我不会推荐这种方法,因为这样的cookie很可能会受到影响,除非您想出某种散列算法来散列cookie值并以某种方式检查它们。
在我看来,最简单,最安全,最自然的方法是使用CSRF令牌保护/delete路由。您可以使用Flask_WTF扩展实现它。
总之,你必须创造这样DeleteForm,那么你就把{{form.csrf_token}}confirm-delete.htmlform.validate_on_submit()

退房验证它在delete_view()他们的文档:
http://flask-wtf.readthedocs.io/en/stable/form.html
http://flask-wtf.readthedocs.io/en/stable/csrf.html

+0

谢谢你的建议。我用CSRF实现表单验证的问题是,我仍然不得不以某种方式将'form'对象从一个视图传递到另一个视图。因此,我决定合并这两个视图(最终结果比我最初的方法更好)。我编辑了我的问题以包含我使用的解决方案。谢谢! –

0

我会做删除页面仅限POST。浏览器可能会跳过GET请求或尝试多次,但您无法控制它。抓取工具可以遵循匿名删除链接并删除您的所有wiki文章。浏览器预取程序可以预取一个注销链接。

REST纯粹主义者会坚持使用GET,POST,DELETE和PUT方法达到预期目的。

https://softwareengineering.stackexchange.com/questions/188860/why-shouldnt-a-get-request-change-data-on-the-server

所以,

在HTML

<form action='/dashboard/delete/{{id}}' method='post'> 

在瓶

@app.route('/dashboard/delete/<int:id>', methods=['POST']) 
def delete(id): 
+0

谢谢,这是非常丰富的。我结合了两个视图函数,因此我保留了'methods = ['GET','POST']'。 –