Jquery 如何使用Flask、Basic Auth和Ajax在浏览器中存储令牌
我只是想提醒一下,我还不知道这其中的大部分是如何工作的,所以如果这是一个简单的问题,请提前道歉 我正在尝试使用basicauth为Flask登录设置一个简单的RESTful站点。我有一个需要登录的页面(管理面板)和一个登录页面(登录)。如果我进入管理面板,浏览器会弹出一个窗口,允许我输入用户名和密码。一旦验证,我相信它存储了一个令牌,这样当我转到其他页面时,我就不需要再次登录。这一切都有效 在登录页面上,我不想出现弹出窗口,所以我添加了ajax,以使用身份验证头来完成文章。这可以工作并成功地让我登录 一旦我使用登录页面登录,然后转到管理面板页面,浏览器会弹出另一个登录框。我相信这是因为我在写ajax文章时没有存储令牌 如何将令牌存储在与弹出窗口类似但完全通过ajax的庄园中 以下是登录验证javascript:Jquery 如何使用Flask、Basic Auth和Ajax在浏览器中存储令牌,jquery,python,ajax,flask,flask-login,Jquery,Python,Ajax,Flask,Flask Login,我只是想提醒一下,我还不知道这其中的大部分是如何工作的,所以如果这是一个简单的问题,请提前道歉 我正在尝试使用basicauth为Flask登录设置一个简单的RESTful站点。我有一个需要登录的页面(管理面板)和一个登录页面(登录)。如果我进入管理面板,浏览器会弹出一个窗口,允许我输入用户名和密码。一旦验证,我相信它存储了一个令牌,这样当我转到其他页面时,我就不需要再次登录。这一切都有效 在登录页面上,我不想出现弹出窗口,所以我添加了ajax,以使用身份验证头来完成文章。这可以工作并成功地让我
$(document).ready(function() {
// bind the form submit event to our function
$("#loginForm").bind('submit', function(e) {
// prevent page refresh
e.preventDefault();
// post the data
var username = $(this).find('input[name="username"]').val();
var password = $(this).find('input[name="password"]').val();
var ajax=$.ajax({
type: "POST",
dataType: 'json',
encode: true,
async: false,
headers: {
"Authorization": "Basic " + btoa(username + ":" + password)
},
data: { },
url: "http://127.0.0.1:5000/api/login"
}).done(function(data){
console.log('Login Success!')
location.reload();
});
ajax.fail(function(jqXHR, textStatus, errorThrown){
console.log('error! '+jqXHR+' - '+textStatus+' - '+errorThrown);
});
});
});
以下是几页:
#!/usr/bin/env python
import os
from flask import Flask, abort, request, jsonify, g, url_for, render_template
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.httpauth import HTTPBasicAuth
from passlib.apps import custom_app_context as pwd_context
from itsdangerous import (TimedJSONWebSignatureSerializer
as Serializer, BadSignature, SignatureExpired)
from functools import wraps
# initialization
app = Flask(__name__)
app.config['SECRET_KEY'] = 'super duper easy secret key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydb.db'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
# extensions
db = SQLAlchemy(app)
auth = HTTPBasicAuth()
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(32), index=True, unique=True)
password_hash = db.Column(db.String(64))
user_role = db.Column(db.Enum('admin', 'user', name='user_role'))
def hash_password(self, password):
self.password_hash = pwd_context.encrypt(password)
def verify_password(self, password):
return pwd_context.verify(password, self.password_hash)
def generate_auth_token(self, expiration=600):
s = Serializer(app.config['SECRET_KEY'], expires_in=expiration)
return s.dumps({'id': self.id})
@staticmethod
def verify_auth_token(token):
s = Serializer(app.config['SECRET_KEY'])
try:
data = s.loads(token)
except SignatureExpired:
return None # valid token, but expired
except BadSignature:
return None # invalid token
user = User.query.get(data['id'])
return user
@auth.verify_password
def verify_password(username_or_token, password):
# first try to authenticate by token
user = User.verify_auth_token(username_or_token)
if not user:
# try to authenticate with username/password
user = User.query.filter_by(username=username_or_token).first()
if not user or not user.verify_password(password):
return False
g.user = user
return True
def verify_role(role):
def decorator(func):
@wraps(func)
def decorated(*args, **kwargs):
if g.user.user_role == role:
return func(*args, **kwargs)
else:
abort(400) # incorrect role
return decorated
return decorator
@app.route('/api/users/<int:id>')
def get_user(id):
user = User.query.get(id)
if not user:
abort(400)
return jsonify({'username': user.username})
@app.route('/api/token')
@auth.login_required
def get_auth_token():
token = g.user.generate_auth_token(600)
return jsonify({'token': token.decode('ascii'), 'duration': 600})
@app.route('/login', methods=['GET', 'POST'])
def login():
print request.method
if request.method == 'POST':
print 'posted', request.form.get('username'), request.form.get('password')
else:
return render_template('login.html')
@app.route('/api/login', methods=['POST'])
@auth.login_required
def _login():
# HTTP Auth should do its thing
return jsonify({"login": "success", "user": g.user.username})
@app.route('/admin/')
@auth.login_required
@verify_role('admin')
def admin_panel():
return jsonify({'data': 'Hello, %s!' % g.user.username})
if __name__ == '__main__':
if not os.path.exists('mydb.db'):
db.create_all()
app.run(debug=True)
#/usr/bin/env python
导入操作系统
从flask导入flask、中止、请求、jsonify、g、url、呈现模板
从flask.ext.sqlalchemy导入sqlalchemy
从flask.ext.httpauth导入HTTPBasicAuth
从passlib.apps导入自定义的app\u上下文作为pwd\u上下文
从其危险进口(TimedJSONWebSignatureSerializer
作为序列化程序,BadSignature,SignatureExpired)
从functools导入包装
#初始化
app=烧瓶(名称)
app.config['SECRET_KEY']='super duper easy SECRET KEY'
app.config['SQLALCHEMY\u DATABASE\u URI']='sqlite:///mydb.db'
app.config['SQLALCHEMY\u COMMIT\u ON\u TEARDOWN']=True
#扩展
db=SQLAlchemy(应用程序)
auth=HTTPBasicAuth()
类用户(db.Model):
__tablename_uu='users'
id=db.Column(db.Integer,主键=True)
username=db.Column(db.String(32),index=True,unique=True)
密码\u hash=db.Column(db.String(64))
user\u role=db.Column(db.Enum('admin','user',name='user\u role'))
def哈希_密码(self,password):
self.password\u hash=pwd\u context.encrypt(密码)
def验证_密码(自我、密码):
返回pwd_context.verify(密码、self.password_散列)
def生成授权令牌(自身,过期=600):
s=序列化程序(app.config['SECRET\u KEY'],expires\u in=expire)
返回s.dumps({'id':self.id})
@静力学方法
def验证验证令牌(令牌):
s=序列化程序(app.config['SECRET\u KEY'])
尝试:
数据=s.loads(令牌)
除签名外:
返回无#有效令牌,但已过期
除签名外:
返回无#无效令牌
user=user.query.get(数据['id'])
返回用户
@验证密码
def验证密码(用户名或令牌、密码):
#首先尝试通过令牌进行身份验证
用户=用户。验证身份验证令牌(用户名或令牌)
如果不是用户:
#尝试使用用户名/密码进行身份验证
user=user.query.filter\u by(username=username\u或\u令牌)。first()
如果不是用户或非用户。请验证\u密码(密码):
返回错误
g、 用户=用户
返回真值
def验证_角色(角色):
def装饰器(func):
@包装(func)
def装饰(*args,**kwargs):
如果g.user.user_role==角色:
返回函数(*args,**kwargs)
其他:
中止(400)#角色不正确
回报
返回装饰器
@app.route(“/api/users/”)
def get_用户(id):
user=user.query.get(id)
如果不是用户:
中止(400)
返回jsonify({'username':user.username})
@app.route(“/api/token”)
@需要auth.login\u
def get_auth_令牌():
令牌=g.user.generate\u auth\u令牌(600)
返回jsonify({'token':token.decode('ascii'),'duration':600})
@app.route('/login',methods=['GET','POST'])
def login():
打印请求方法
如果request.method==“POST”:
打印'posted',request.form.get('username'),request.form.get('password'))
其他:
返回呈现模板('login.html')
@app.route('/api/login',methods=['POST'])
@需要auth.login\u
def_login():
#HTTP身份验证应该完成它的工作
返回jsonify({“login”:“success”,“user”:g.user.username})
@app.route(“/admin/”)
@需要auth.login\u
@验证\u角色(“管理员”)
def管理面板():
返回jsonify({'data':'Hello,%s!'%g.user.username})
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
如果不存在os.path.exists('mydb.db'):
db.create_all()
app.run(debug=True)
我甚至不确定我应该这样做。此外,这目前正在本地使用,但如果它移动到其他地方,我将最终使用ssl
提前谢谢 使用基本身份验证时,浏览器不会存储单独的令牌。在基本身份验证中,用户名和密码随每个请求一起发送。您的浏览器会自动为您执行此操作 这也是您的身份验证方案不起作用的原因:浏览器不知道您以前的请求,并且不存储凭据。因此,它会询问您何时收到来自服务器的下一个身份验证质询。由于出于安全原因,主要浏览器不会公开API来存储随机凭据,因此您无法更改此设置
解决这个问题的唯一方法是不依赖基本身份验证,而是旋转您自己的身份验证机制。您不能将成功的登录响应数据记录到控制台,选择令牌并使用localStorage.setItem(_令牌))将其存储在本地,然后根据需要进行访问吗?嗯,我想我可以这样做。我仍然有兴趣知道浏览器在哪里存储它。如果不是浏览器,那么HttpBasicAuth的幕后工作是什么呢?我开始认为浏览器只是缓存用户名和密码,然后重用它们,而无需再次向用户请求。这是怎么回事?