Flask-login 的文档讨论了如何处理“下一个”URL。这个想法似乎是:
/secret
/login
)
/secret
https://gist.github.com/bkdinoop/6698956 。这很有帮助,但由于它不包含 HTML 模板文件,我正在看看是否可以重新创建它们作为自我训练练习。
这是/secret
和
/login
部分的简化版本:
@app.route("/secret")
@fresh_login_required
def secret():
return render_template("secret.html")
@app.route("/login", methods=["GET", "POST"])
def login():
<...login-checking code omitted...>
if user_is_logged_in:
flash("Logged in!")
return redirect(request.args.get("next") or url_for("index"))
else:
flash("Sorry, but you could not log in.")
return render_template("login.html")
这是
login.html
:
<form name="loginform" action="{{ url_for('login') }}" method="POST">
Username: <input type="text" name="username" size="30" /><br />
Password: <input type="password" name="password" size="30" /><br />
<input type="submit" value="Login" /><br />
现在,当用户访问
/secret
时,他会被重定向到
/login?next=%2Fsecret
。到目前为止,一切顺利 - “下一个”参数位于查询字符串中。但是,当用户提交登录表单时,他会被重定向回索引页面,而不是返回到
/secret
URL。我猜测原因是因为传入 URL 上可用的“next”参数未合并到登录表单中,因此在处理表单时不会作为变量传递。但是正确的方法是什么解决这个问题?
一个解决方案似乎有效 - 将
<form>
标签从更改
<form name="loginform" action="{{ url_for('login') }}" method="POST">
至:
<form name="loginform" method="POST">
删除“action”属性后,浏览器(至少 Windows 上的 Firefox 45)会自动使用当前 URL,使其继承
?next=%2Fsecret
查询字符串,从而成功将其发送到表单处理处理程序。但是省略“action”表单属性并让浏览器将其填充到正确的解决方案中吗?它适用于所有浏览器和平台吗?
或者 Flask 或 Flask-login 是否打算以不同的方式处理这个问题?
action 属性,则不能使用 Flask-Login 提供的下一个参数。无论如何,我建议将端点而不是 url 放入 url 参数中,因为这样更容易验证。这是我正在开发的应用程序中的一些代码,也许这可以帮助您。
覆盖 Flask-Login 的未授权处理程序以使用端点而不是下一个参数中的 url:
@login_manager.unauthorized_handler
def handle_needs_login():
flash("You have to be logged in to access this page.")
return redirect(url_for('account.login', next=request.endpoint))
也在您自己的 URL 中使用
request.endpoint
:
{# login form #}
<form action="{{ url_for('account.login', next=request.endpoint) }}" method="post">
...
</form>
重定向到下一个参数中的端点(如果存在且有效),否则重定向到后备。
def redirect_dest(fallback):
dest = request.args.get('next')
try:
dest_url = url_for(dest)
except:
return redirect(fallback)
return redirect(dest_url)
@app.route("/login", methods=["GET", "POST"])
def login():
...
if user_is_logged_in:
flash("Logged in!")
return redirect_dest(fallback=url_for('general.index'))
else:
flash("Sorry, but you could not log in.")
return render_template("login.html")
在
login.html
中只需添加以下代码
<input
type="hidden"
name="next"
value="{{ request.args.get('next', '') }}"
/>
并且登录定义更改如下
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
next_url = request.form.get("next")
if username in users and users[username][1] == password:
session["username"] = username
if next_url:
return redirect(next_url)
return redirect(url_for("profile"))
return render_template("login.html")
这对我来说非常有效......谢谢你...
索引/然后使用 url_for(request.endpoint,**request.view_args) 代替,因为 request.endpoint 将不包含动态可变信息:
@login_manager.unauthorized_handler
def handle_needs_login():
flash("You have to be logged in to access this page.")
#instead of using request.path to prevent Open Redirect Vulnerability
next=url_for(request.endpoint,**request.view_args)
return redirect(url_for('account.login', next=next))
以下代码应改为:
def redirect_dest(home):
dest_url = request.args.get('next')
if not dest_url:
dest_url = url_for(home)
return redirect(dest_url)
@app.route("/login", methods=["GET", "POST"])
def login():
...
if user_is_logged_in:
flash("Logged in!")
return redirect_dest(home=anyViewFunctionYouWantToSendUser)
else:
flash("Sorry, but you could not log in.")
return render_template("login.html")
Flask_Restful 传递“next”URL,我一直在使用的解决方法是将“next”参数从 GET 方法传递到 POST方法。使用flask_restful,单击login.html中的登录按钮后,“next_page”参数设置为“None”
登录.html
...
<!-- next_page came from "render_template(next_page=request.args.get('next') ...)" in the get() function -->
<!-- And also from render_template('login.html', next_page=next_page) in the post() function -->
<form action="{{ url_for('login', next=next_page) }}" method="POST" >
<div class="field">
<div class="control">
<input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus="">
</div>
</div>
<div class="field">
<div class="control">
<input class="input is-large" type="password" name="password" placeholder="Your Password">
</div>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" name="remember_me">
Remember me
</label>
</div>
<button class="button is-block is-info is-large is-fullwidth">Login</button>
</form>
...
auth.py
class Login(Resource):
def get(self):
if current_user.is_authenticated:
return redirect(url_for('home'))
headers = {'Content-Type': 'text/html'}
#1 --> # Here I pass the "next_page" to login.html
return make_response(render_template('login.html', next_page=request.args.get('next')), 200, headers)
def post(self):
#2 --> # After the user clicks the login button, I retrieve the next_page saved in the GET method
next_page = request.args.get('next')
if current_user.is_authenticated:
return redirect(url_for('home'))
# Check if account exists in the db
existing_account = Account.objects(email=request.form.get('email')).first()
# Only redirects when the URL is relative, which ensures that the redirect
# stays within the same site as the application.
if existing_account:
if existing_account.check_password(request.form.get('password')):
login_user(existing_account, remember=request.form.get('remember_me'))
if not next_page or url_parse(next_page).netloc != '':
return redirect(url_for('home'))
#3 --> # Here I use the retrieved next_page argument
return redirect(url_for(next_page))
# Account not recognized
flash('Please check your login details and try again.')
headers = {'Content-Type': 'text/html'}
#4 --> # I also pass the "next_page" here in case the user-provided data is wrong
return make_response(render_template('login.html', next_page=next_page), 200, headers)
@login_manager.unauthorized_handler
def handle_needs_login():
return redirect(url_for('login', next_page=request.endpoint))
@app.route('/login/<next_page>', methods=['GET', 'POST'])
def login(next_page):
if current_user.is_authenticated:
return redirect(url_for(next_page))
if is_a_successfull_login():
redirect(url_for(next_page))
@app.route('/login', methods=['GET', 'POST'])
def login():
// do your stuff
next_page = request.form.get('next_page')
# next_page either be 'http://localhost:5000/None or 'http://localhost:5000/some_endpoints
return redirect(next_page) if 'None' not in next_page else redirect(url_for('default_action'))
注意Flask文档中提到的
不要忘记在 next_url
html
<input type="hidden" value="{{ request.args.get('next') }}" name="next_page" id="next_page"/>
使用 Flask-Login 在我的网站上设置 API 后,我的“下一个”参数就停止工作。这是我为解决问题所做的事情。
def setup_login_manager(app, login_manager):
login_manager.init_app(app)
login_manager.login_message_category = "warning"
@login_manager.unauthorized_handler
def setup_unauthorized():
if "api" in request.full_path:
abort(401)
else:
next_path = request.path
if next_path and next_path != "/":
return redirect(url_for("users.login", next=url_for(request.endpoint)))
else:
return redirect(url_for("users.login"))