用户认证(一)

Flask的认证扩展

用户认证(一)_第1张图片

密码安全性

为了保证数据库用户密码的安全,数据库不储存密码本身,而要储存密码的散列值

使用Werkzeug实现密码散列

Werkzeug的security可以实现密码散列值的计算。
用户认证(一)_第2张图片
在User模型中加入密码散列的功能

from werkzeug.security import generate_password_hash, check_password_hash
###从Werkzeug的模块中导入两个函数
from . import db


class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User',backref='role',lazy='dynamic')

    def __repr__(self):
        return '' % self.name


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64),unique=True, index=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    password_hash = db.Column(db.String(128))
    ###加入password_hash属性

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')
    ###试图读取password属性,返回错误。

    @password.setter
    ###计算密码散列值的函数通过名为password的只写属性实现。
    def password(self,password):
        self.password_hash = generate_password_hash(password)
    ###设置password的属性时,赋值方法会调用Werkzeug提供的generate_password_hash()函数,并把结果赋值给password_hash字段。

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)
    ###verify_password方法接受一个参数(密码),将其传给Werkzeug提供的check_password_hash()函数,和储存在User模型中的密码散列值进行对比。密码正确返回True,密码错误返回False。

    def __repr__(self):
        return '' % self.username

在shell中测试密码散列功能:

C:\Users\Geek Lee\Geek-Lee.github.io>venv\Scripts\activate

(venv) C:\Users\Geek Lee\Geek-Lee.github.io>python manage.py shell
>>> u = User()
>>> u.password = 'cat'
>>> u.password_hash
'pbkdf2:sha1:1000$0TXuXSpo$c4de7d85b42d3078480305dd8ea7c55b4dfc01d6'
>>> u.password
Traceback (most recent call last):
  File "", line 1, in 
  File "C:\Users\Geek Lee\Geek-Lee.github.io\app\models.py", line 24, in passwor
d
    raise AttributeError('password is not a readable attribute')
AttributeError: password is not a readable attribute
>>> u.verify_password('cat')
True
>>> u.verify_password('dog')
False
>>> u2 = User()
>>> u2.password = 'cat'
>>> u2.password_hash
'pbkdf2:sha1:1000$LcSu5inU$df584e24bf63652db47306f7bfe2e1b1a555dae7'
###两个用户的密码一样,计算出的散列值也不一样
>>>

在test包中中实现密码散列化测试
test/test_user_model.py

import unittest
###引入unittest包
from app import create_app, db
from app.models import User


class UserModelTestCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app('testing')
        ###创建个实例
        self.app_context = self.app.app_context()
        self.app_context.push()
        ###激活上下文
        db.create_all()
        ###产生数据库实例

    def tearDown(self):
        db.session.remove()
        ###删除会话
        db.drop_all()
        ###删除数据库表
        self.app_context.pop()
        ###删除上下文

    def test_password_setter(self):
    ###测试密码的设置
        u = User(password='cat')
        self.assertTrue(u.password_hash is not None)
        ###??????????????????????

    def test_no_password_getter(self):
    ###测试密码是否能提取
        u = User(password='cat')
        with self.assertRaises(AttributeError):
            u.password
        ###?????????????????????? 

    def test_password_verification(self): 
    ###测试是否具有认证密码的功能
        u = User(password='cat')
        self.assertTrue(u.verify_password('cat'))
        self.assertFalse(u.verify_password('dog'))
        ###??????????????????????

    def test_password_salts_are_random(self):
    ###测试不同用户的相同密码的散列值是否相同
        u = User(password='cat')
        u2 = User(password='cat')
        self.assertTrue(u.password_hash != u2.password_hash)
        ###???????????????????????

创建认证蓝本

创建蓝本
auth蓝本保存在app程序包的auth文件夹下(必须同名)
flasky/app/auth/_init_.py

from flask import Blueprint
###导入Blueprint

auth = Blueprint('auth', __name__)

from . import views
###蓝本的包构造文件创建蓝本对象,再从views.py模块引入路由

蓝本中的路由和视图函数
flasky/app/auth/views.py
app/auth/views.py模块引入蓝本,然后使用auth蓝本的修饰器定义与认证相关的路由。

from flask import render_template
from . import auth

@auth.route('/login')
def login():
    return render_template('auth/login.html')

模板文件
为这个render_template()指定的模板文件必须保存在app/templates中auth文件夹中,因为Flask寻找模板的路径是在templates文件夹下面找(相对路径)
app/templates/auth/login.html

{% extends "base.html" %}

{% block title %}Flasky - Login{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Loginh1>
div>
{% endblock %}

激活蓝本
flasky/app/_init_.py
auth蓝本要在create_app()工厂函数中附加到程序上

from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from config import config

bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()


def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)

    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    from .auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint, url_prefix='/auth')
    ###注册蓝本时使用url_prefix是可选参数,注册后蓝本中定义的所有路由都会加上指定的前缀,即这个例子中的/auth。
    ###/login路由会注册成/auth/login。
    return app

你可能感兴趣的:(-----flask)