1. JWT
Flask-HTTPAuthBasic AuthToken AuthidGET /api/users/
1.1 pyjwt
JWTpayloadidpyjwt
(venv) D:\python-code\flask-vuejs-madblog\back-end>pip install pyjwt (venv) D:\python-code\flask-vuejs-madblog\back-end>pip freeze > requirements.txt
back-end/app/models.pytokentoken_expiration
class User(PaginatedAPIMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128)) # 不保存原始密码
...
def get_jwt(self, expires_in=600):
now = datetime.utcnow()
payload = {
'user_id': self.id,
'name': self.name if self.name else self.username,
'exp': now + timedelta(seconds=expires_in),
'iat': now
}
return jwt.encode(
payload,
current_app.config['SECRET_KEY'],
algorithm='HS256').decode('utf-8')
@staticmethod
def verify_jwt(token):
try:
payload = jwt.decode(
token,
current_app.config['SECRET_KEY'],
algorithms=['HS256'])
except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidSignatureError) as e:
# Token过期,或被人修改,那么签名验证也会失败
return None
return User.query.get(payload.get('user_id'))
back-end/config.pySECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
back-end/app/api/tokens.py
from flask import jsonify, g
from app import db
from app.api import bp
from app.api.auth import basic_auth
@bp.route('/tokens', methods=['POST'])
@basic_auth.login_required
def get_token():
token = g.current_user.get_jwt()
db.session.commit()
return jsonify({'token': token})
JWTDELETE /tokens
1.2 Flask-Migrate 迁移
修改 User 数据模型后要进行数据库迁移:
(venv) D:\python-code\flask-vuejs-madblog\back-end>flask db migrate -m "users jwt" (venv) D:\python-code\flask-vuejs-madblog\back-end>flask db upgrade ... sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "DROP": syntax error [SQL: 'ALTER TABLE user DROP COLUMN token_expiration'] (Background on this error at: http://sqlalche.me/e/e3q8)
SQLite
batch_alter_tableback-end/migrations/env.py
def run_migrations_online():
...
context.configure(connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
render_as_batch=True, # 增加这个配置项
**current_app.extensions['migrate'].configure_args)
back-end/migrations/versions/8ba2e85acf5b_users_jwt.py
(venv) D:\python-code\flask-vuejs-madblog\back-end>flask db migrate -m "users jwt"
batch_alter_table
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_column('token')
batch_op.drop_column('token_expiration')
# ### end Alembic commands ###
最后,应用迁移脚本:
(venv) D:\python-code\flask-vuejs-madblog\back-end>flask db upgrade
1.3 JSON.parse
axiosPOST /tokensBasic Authget_tokenJWT
idJWT.
JSON.parse(atob(window.localStorage.getItem('madblog-token').split('.')[1])).user_id
front-end/src/store.js
export default {
debug: true,
state: {
is_authenticated: window.localStorage.getItem('madblog-token') ? true : false,
// 用户登录后,就算刷新页面也能再次计算出 user_id
user_id: window.localStorage.getItem('madblog-token') ? JSON.parse(atob(window.localStorage.getItem('madblog-token').split('.')[1])).user_id : 0
},
loginAction () {
if (this.debug) { console.log('loginAction triggered') }
this.state.is_authenticated = true
this.state.user_id = JSON.parse(atob(window.localStorage.getItem('madblog-token').split('.')[1])).user_id
},
logoutAction () {
if (this.debug) console.log('logoutAction triggered')
window.localStorage.removeItem('madblog-token')
this.