GuaGe
(不撞南墙)
2019 年3 月 7 日 09:15
1
安全重定向的代码:
def is_save_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc
def safe_redirect(default='index', **kwargs):
for target in request.args.get('next'), request.referrer:
if not target:
continue
if is_save_url(target):
return redirect(target)
return redirect(default, **kwargs)
视图函数:
@app.route('/login', methods=['POST', 'GET'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
password = form.password.data
remeber = form.remember.data
user = User.query.filter_by(username=username).first() or User.query.filter_by(email=username).first()
if user and user.validate_password(password):
login_user(user, remeber)
flash('欢迎回来{}!'.format(user.username), 'success')
return safe_redirect()
else:
flash('无效的用户或者密码错误!', 'danger')
return render_template('login.html', form=form)
@app.route('/register', methods=['POST', 'GET'])
def register():
if current_user.is_authenticated:
return safe_redirect()
form = RegisterForm()
if form.validate_on_submit():
user = User(
username=form.username.data,
email=form.email.data,
password=form.password.data
)
db.session.add(user)
db.session.commit()
flash('恭喜你,帐号注册成功!', 'success')
return safe_redirect('login')
return render_template('register.html', form=form)
问题:
我想了下,是不是因为 is_safe_url(’login’) 是 True, 所以 safe_redirect() 又会跳转到 login! 如果是这样的话,那这种情况应该如何处理,或者说只能使用 redirecit(url__for(‘index’)) 这种方式??
OQJ
(莫大毛)
2019 年3 月 7 日 11:45
2
redirect函数的第一个参数应该为目标url值,所以应该改成这样
def safe_redirect(default='index', **kwargs):
for target in request.args.get('next'), request.referrer:
if not target:
continue
if is_save_url(target):
return redirect(url_for(target))
return redirect( url_for(default) , **kwargs)
url_for()函数的导入如下
from flask import url_for
这个函数的作用是接受一个端点值,返回该端点所对应的url
GuaGe
(不撞南墙)
2019 年3 月 9 日 07:02
5
我试过了 OQJ 的方案,似乎还是有问题!
我这里通过点击主页的登录按钮登录之后,我 print request.args.get(‘next’) 或者 reqruest.referrer
或者 target
输出都是 ([log] here: 的内容)
[Log] here: http://localhost:5000/login
127.0.0.1 - - [09/Mar/2019 14:53:42] "GET /login HTTP/1.1" 302 -
127.0.0.1 - - [09/Mar/2019 14:53:42] "GET /login HTTP/1.1" 302 -
[Log] here: None http://localhost:5000/login
[Log] here: http://localhost:5000/login
127.0.0.1 - - [09/Mar/2019 14:53:42] "GET /login HTTP/1.1" 302 -
[Log] here: None http://localhost:5000/login
[Log] here: http://localhost:5000/login
127.0.0.1 - - [09/Mar/2019 14:53:42] "GET /login HTTP/1.1" 302 -
[Log] here: None http://localhost:5000/login
[Log] here: http://localhost:5000/login
[Log] here: None http://localhost:5000/login
也就是说,is_safe_url(target) 这里存在无限重定向!
如果
return redirecit(target)
改成
return redirect(url_for(target))
明显是错误的,因为 target 不是一个端点而是 url, 会报错!
OQJ
(莫大毛)
2019 年3 月 9 日 10:24
6
我那里的确错了 ,从request得到的next值和referrer值都是url,所以不用url_for函数处理,直接redirect即可
def safe_redirect(default='index', **kwargs):
for target in request.args.get('next'), request.referrer:
if not target:
continue
if is_save_url(target):
return redirect(target)
return redirect( url_for(default) , **kwargs)
GuaGe
(不撞南墙)
2019 年3 月 9 日 12:13
7
你仔细看下我的代码和输出!
书本上和源代码都是
redirect(targe)
但是会无限重定向到 login
具体的内容我打印出来了
如果 is_safe_url(target) 是 True, 那必然无限重定向到 login
我的问题就是这个!不知道怎么回事
OQJ
(莫大毛)
2019 年3 月 9 日 13:46
8
如果可以的话,请把前端的那部分代码也发上来,或者你也可以把项目上传到github上,把地址私信给我,有更多的信息,才能更有效的解决问题
GuaGe
(不撞南墙)
2019 年3 月 10 日 06:04
9
主页和登录页面
{% extends 'base.html' %}
{% block title %}
主页
{% endblock %}
{% block content %}
<div class="col-8 offset-2">
{{ super() }}
<h3 class="text-primary text-center">瓜哥留言板</h3>
<p class="text-info text-center">
<small>Version 1.0.0</small>
</p>
{% if current_user.is_authenticated: %}
<form method="post">
{{ form.csrf_token }}
{% for message in form.body.errors %}
<small class="text-danger">{{ message }}</small>
{% endfor %}
{{ form.body(class='form-control', placeholder='你想留下点什么内容呢?') }}
{{ form.submit(class='btn btn-info btn-block') }}
</form>
<hr>
<br>
{% endif %}
<h5>{{ messages|length }} 条留言
<small class="float-right">
<a href="#bottom" title="Go Bottom">↓</a>
</small>
</h5>
<div class="list-group">
{% for message in messages %}
<a class="list-group-item list-group-item-action flex-column">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1 text-success">{{ message.user.username }}
<small class="text-muted"> #{{ loop.revindex }}</small>
</h5>
{% if message.edit_time %}
<small>修改过{{ message.edit_time }}次</small>
{% endif %}
<small data-toggle="tooltip" data-placement="top"
data-timestamp="{{ message.timestamp.strftime('%Y-%m-%dT%H:%M:%SZ') }}"
data-delay="500">
{{ moment(message.timestamp).fromNow(refresh=True) }}
</small>
</div>
<p class="mb-1">{{ message.body }}</p>
</a>
{% endfor %}
</div>
</div>
{% endblock %}
{% extends 'base.html' %}
{% block title %}
登录
{% endblock %}
{% block content %}
<div class="col-4 offset-4">
{{ super() }}
<h3 class="text-center"><i class="text-info fa fa-user-circle-o fa-5x"></i></h3>
<p class="text-center">
<small><a href="{{ url_for('register') }}">没有帐号?</a></small>
</p>
<form method="post">
{{ form.csrf_token }}
{% for message in form.username.errors %}
<small class="text-danger">{{ message }}</small>
{% endfor %}
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">邮箱:</span>
</div>
{{ form.username(class='form-control', placeholder='也可以使用昵称登录') }}
</div>
{% for message in form.password.errors %}
<small class="text-danger">{{ message }}</small>
{% endfor %}
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">密码:</span>
</div>
{{ form.password(class='form-control', placeholder='输入你的密码') }}
</div>
<div class="text-right">
{{ form.remember(class='form-check-input') }}
{{ form.remember.label }}
</div>
{{ form.submit(class='btn btn-info btn-block') }}
</form>
</div>
{% endblock %}
视图
@app.route('/', methods=['POST', 'GET'])
def index():
messages = Message.query.order_by(Message.timestamp.desc()).all()
if current_user.is_authenticated:
form = MessageForm()
if form.validate_on_submit():
message = Message(
body=form.body.data,
user=current_user
)
db.session.add(message)
db.session.commit()
flash('你的留言发布成功!', 'success')
return safe_redirect()
return render_template('index.html', form=form, messages=messages)
else:
return render_template('index.html', messages=messages)
@app.route('/login', methods=['POST', 'GET'])
def login():
if current_user.is_authenticated:
return safe_redirect()
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
password = form.password.data
remeber = form.remember.data
user = User.query.filter_by(username=username).first() or User.query.filter_by(email=username).first()
if user and user.validate_password(password):
login_user(user, remeber)
flash('欢迎回来{}!'.format(user.username), 'success')
return safe_redirect()
else:
flash('无效的用户或者密码错误!', 'danger')
return render_template('login.html', form=form)
表单:
class LoginForm(FlaskForm):
username = StringField('昵称[邮箱]:', validators=[DataRequired('注册邮箱地址不能为空'), Length(1, 128)])
password = PasswordField('密码:', validators=[DataRequired('密码不能为空')])
remember = BooleanField('保持登录')
submit = SubmitField('登录')
OQJ
(莫大毛)
2019 年3 月 10 日 07:06
10
def login():
if current_user.is_authenticated:
return safe_redirect()
改为
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
GuaGe
(不撞南墙)
2019 年3 月 10 日 07:59
11
如果直接用 redirect(url_for())
那还又必要写个 is_safe_url 么?
我的问题是登录之后无限重定向。。。。。。
不是当前已经登录的情况下去请求 login
OQJ
(莫大毛)
2019 年3 月 10 日 11:44
12
我是这样想的,如果当前用户已经登录了,他又再一次请求登录视图,那么就把他重定向到主视图,正常情况下,已登录用户已经不再需要点击功能,页面上也不应该显示登录按钮给登录用户。你可以试试这样可以解决无限重定向没有,我还没想到其他办法,我也不太明白为什么不能重定向到正确的页面,求助
在程序埋点,看看有没有按预期打印,例如:
@app.route('/login', methods=['POST', 'GET'])
def login():
if current_user.is_authenticated:
print('1')
return safe_redirect()
form = LoginForm()
if form.validate_on_submit():
print('2')
username = form.username.data
password = form.password.data
remeber = form.remember.data
user = User.query.filter_by(username=username).first() or User.query.filter_by(email=username).first()
if user and user.validate_password(password):
print('3')
login_user(user, remeber)
flash('欢迎回来{}!'.format(user.username), 'success')
return safe_redirect()
else:
print('4')
flash('无效的用户或者密码错误!', 'danger')
print('5')
return render_template('login.html', form=form)
def safe_redirect
函数也尽量多打印出自己想看的信息,比如跳转的连接
1 个赞
大多数情况下都是粗心大意,或者条件分支没有处理好。
GuaGe
(不撞南墙)
2019 年3 月 11 日 04:51
15
我这里已经输出了 request.args.get(‘next’) 和 request.referrer的信息, 就是前面带 [log] here: 字样的内容,全部都是 login
[Log] here: http://localhost:5000/login 127.0.0.1 - - [09/Mar/2019 14:53:42] "GET /login HTTP/1.1" 302 - 127.0.0.1 - - [09/Mar/2019 14:53:42] "GET /login HTTP/1.1" 302 - [Log] here: None http://localhost:5000/login [Log] here: http://localhost:5000/login 127.0.0.1 - - [09/Mar/2019 14:53:42] "GET /login HTTP/1.1" 302 - [Log] here: None http://localhost:5000/login [Log] here: http://localhost:5000/login 127.0.0.1 - - [09/Mar/2019 14:53:42] "GET /login HTTP/1.1" 302 - [Log] here: None http://localhost:5000/login [Log] here: http://localhost:5000/login [Log] here: None http://localhost:5000/login
继续往前追溯。
按道理,正常登录会进入到这里吧
if user and user.validate_password(password):
login_user(user, remeber)
flash('欢迎回来{}!'.format(user.username), 'success')
print('test')
return safe_redirect()
然后进入到这里:
def safe_redirect(default='index', **kwargs):
for target in request.args.get('next'), request.referrer:
print(target)
if not target:
continue
if is_save_url(target):
return redirect(target)
print(default)
return redirect(default, **kwargs)
都print()
打印出来看看。
debug这事不能总是指望别人。
GuaGe
(不撞南墙)
2019 年3 月 11 日 05:53
17
这些地方都没有问题,我试过了!
主要是在
if current_user.is_authenticated:
# return redirect(url_for('index') 是正确的
return safe_redirect() # 就不行了!
我找到问题就是出在这里,改过来就正确了!
非常感谢各位的耐心帮助!
我是个新手,问题总是又多又可笑…
浪费大家时间,非常抱歉!
1 个赞