Jquery 如何使用Flask、Basic Auth和Ajax在浏览器中存储令牌

Jquery 如何使用Flask、Basic Auth和Ajax在浏览器中存储令牌,jquery,python,ajax,flask,flask-login,Jquery,Python,Ajax,Flask,Flask Login,我只是想提醒一下,我还不知道这其中的大部分是如何工作的,所以如果这是一个简单的问题,请提前道歉 我正在尝试使用basicauth为Flask登录设置一个简单的RESTful站点。我有一个需要登录的页面(管理面板)和一个登录页面(登录)。如果我进入管理面板,浏览器会弹出一个窗口,允许我输入用户名和密码。一旦验证,我相信它存储了一个令牌,这样当我转到其他页面时,我就不需要再次登录。这一切都有效 在登录页面上,我不想出现弹出窗口,所以我添加了ajax,以使用身份验证头来完成文章。这可以工作并成功地让我

我只是想提醒一下,我还不知道这其中的大部分是如何工作的,所以如果这是一个简单的问题,请提前道歉

我正在尝试使用basicauth为Flask登录设置一个简单的RESTful站点。我有一个需要登录的页面(管理面板)和一个登录页面(登录)。如果我进入管理面板,浏览器会弹出一个窗口,允许我输入用户名和密码。一旦验证,我相信它存储了一个令牌,这样当我转到其他页面时,我就不需要再次登录。这一切都有效

在登录页面上,我不想出现弹出窗口,所以我添加了ajax,以使用身份验证头来完成文章。这可以工作并成功地让我登录

一旦我使用登录页面登录,然后转到管理面板页面,浏览器会弹出另一个登录框。我相信这是因为我在写ajax文章时没有存储令牌

如何将令牌存储在与弹出窗口类似但完全通过ajax的庄园中

以下是登录验证javascript:

$(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的幕后工作是什么呢?我开始认为浏览器只是缓存用户名和密码,然后重用它们,而无需再次向用户请求。这是怎么回事?