Flask中如何实现用户登陆认证?

本文和大家分享的主要是使用Flask实现用户登陆认证的相关知识,希望对大家学习Flask有所帮助。

用户认证的原理

在了解使用Flask来实现用户认证之前,我们首先要明白用户认证的原理。假设现在我们要自己去实现用户认证,需要做哪些事情呢?

1. 首先,用户要能够输入用户名和密码,所以需要网页和表单,用以实现用户输入和提交的过程。

2. 用户提交了用户名和密码,我们就需要比对用户名,密码是否正确,而要想比对,首先我们的系统中就要有存储用户名,密码的地方,大多数后台系统会通过数据库来存储,但是实际上我们也可以简单的存储到文件当中。(为简明起见,本文将用户信息存储到json文件当中)

3. 登录之后,我们需要维持用户登录状态,以便用户在访问特定网页的时候来判断用户是否已经登录,以及是否有权限访问改网页。这就需要有维护一个会话来保存用户的登录状态和用户信息。

4. 从第三步我们也可以看出,如果我们的网页需要权限保护,那么当请求到来的时候,我们就首先要检查用户的信息,比如是否已经登录,是否有权限等,如果检查通过,那么在response的时候就会将相应网页回复给请求的用户,但是如果检查不通过,那么就需要返回错误信息。

5. 在第二步,我们知道要将用户名和密码存储起来,但是如果只是简单的用明文存储用户名和密码,很容易被“有心人”盗取,从而造成用户信息泄露,那么我们实际上应当将用户信息尤其是密码做加密处理之后再存储比较安全。

6. 用户登出

通过Flask以及相应的插件来实现登录过程

接下来讲述如何通过Flask框架以及相应的插件来实现整个登录过程,需要用到的插件如下:

· flask-wtf

· wtf

· werkzeug

· flask_login

使用flask-wtf和wtf来实现表单功能

flask-wtf对wtf做了一些封装,不过有些东西还是要直接用wtf,比如StringField等。flask-wtf和wtf主要是用于建立html中的元素和Python中的类的对应关系,通过在Python代码中操作对应的类,对象等从而控制html中的元素。我们需要在python代码中使用flask-wtf和wtf来定义前端页面的表单(实际是定义一个表单类),再将对应的表单对象作为render_template函数的参数,传递给相应的template,之后Jinja模板引擎会将相应的template渲染成html文本,再作为http response返回给用户。

定义表单类示例代码:

# forms.pyfrom flask_wtf import FlaskFormfrom wtforms import StringField, BooleanField, PasswordFieldfrom wtforms.validators import DataRequired

# 定义的表单都需要继承自FlaskFormclass LoginForm(FlaskForm):

# 域初始化时,第一个参数是设置label属性的

username = StringField(’User Name’, validators=[DataRequired()])

password = PasswordField(’Password’, validators=[DataRequired()])

remember_me = BooleanField(’remember me’, default=False)

在wtf当中,每个域代表就是html中的元素,比如StringField代表的是file:///C:\Users\wlc\AppData\Local\Temp\ksohtml\wps912D.tmp.png元素,当然wtf的域还定义了一些特定功能,比如validators,可以通过validators来对这个域的数据做检查,详细请参考wtf教程。

对应的html模板可能如下login.html:

{% extends "layout.html" %}<html>

<head>

<title>Login Pagetitle>

head>

<body>

<form action="{{ url_for("login") }}" method="POST">

<p>

User Name:<br>

<input type="text" name="username" /><br>

p>

<p>

Password:br>

<input type="password" name="password" /><br>

p>

<p>

<input type="checkbox" name="remember_me"/>Remember Me

p>

{{ form.csrf_token }}

form>

body>html>

这里{{ form.csrf_token }}也可以使用{{ form.hidden_tag() }}来替换

同时我们也可以使用form去定义模板,跟直接用html标签去定义效果是相同的,Jinja模板引擎会将对象、属性转化为对应的html标签,

相对应的template,如下login.html:

{% extends "base.html" %}

{% block content %}

<h1>Sign Inh1>

<form action="{{ url_for("login") }}" method="post" name="login">

{{ form.csrf_token }}

<p>

{{ form.username.label }}<br>

{{ form.username(size=80) }}<br>

p>

<p>

{{ form.password.label }}<br>

{{ form.password(size=80) }}<br>

p>

<p>{{ form.remember_me }} Remember Mep>

<p><input type="submit" value="Sign In">p>

form>{% endblock %}

现在我们需要在view中定义相应的路由,并将相应的登录界面展示给用户。

简单起见,将view的相关路由定义放在主程序当中

# [email protected](’/login’)def login():

form = LoginForm()

return render_template(’login.html’, title="Sign In", form=form)

这里简单起见,当用户请求’/login’路由时,直接返回login.html网页,注意这里的html网页是经过Jinja模板引擎将相应的模板转换后的html网页。

至此,如果我们把以上代码整合到flask当中,就应该能够看到相应的登录界面了,那么当用户提交之后,我们应当怎样存储呢?这里我们暂时先不用数据库这样复杂的工具存储,先简单地存为文件。接下来就看下如何去存储。

加密和存储

我们可以首先定义一个User类,用于处理与用户相关的操作,包括存储和验证等。

# models.py

from werkzeug.security import generate_password_hashfrom werkzeug.security import check_password_hashfrom flask_login import UserMixinimport jsonimport uuid

# define profile.json constant, the file is used to# save user name and password_hash

PROFILE_FILE = "profiles.json"

class User(UserMixin):

def __init__(self, username):

self.username = username

self.password_hash = self.get_password_hash()

self.id = self.get_id()

@property

def password(self):

raise AttributeError(’password is not a readable attribute’)

@password.setter

def password(self, password):

"""save user name, id and password hash to json file"""

self.password_hash = generate_password_hash(password)

with open(PROFILE_FILE, ’w+’) as f:

try:

profiles = json.load(f)

except ValueError:

profiles = {}

profiles[self.username] = [self.password_hash,

self.id]

f.write(json.dumps(profiles))

def verify_password(self, password):

if self.password_hash is None:

return False

return check_password_hash(self.password_hash, password)

def get_password_hash(self):

"""try to get password hash from file.

:return password_hash: if the there is corresponding user in

the file, return password hash.

None: if there is no corresponding user, return None.

"""

try:

with open(PROFILE_FILE) as f:

user_profiles = json.load(f)

user_info = user_profiles.get(self.username, None)

if user_info is not None:

return user_info[0]

except IOError:

return None

except ValueError:

return None

return None

def get_id(self):

"""get user id from profile file, if not exist, it will

generate a uuid for the user.

"""

if self.username is not None:

try:

with open(PROFILE_FILE) as f:

user_profiles = json.load(f)

if self.username in user_profiles:

return user_profiles[self.username][1]

except IOError:

pass

except ValueError:

pass

return unicode(uuid.uuid4())

@staticmethod

def get(user_id):

"""try to return user_id corresponding User object.

This method is used by load_user callback function

"""

if not user_id:

return None

try:

with open(PROFILE_FILE) as f:

user_profiles = json.load(f)

for user_name, profile in user_profiles.iteritems():

if profile[1] == user_id:

return User(user_name)

except:

return None

return None

· User类需要继承flask-login中的UserMixin类,用于实现相应的用户会话管理。

· 这里我们是直接存储用户信息到一个json文件"profiles.json"

· 我们并不直接存储密码,而是存储加密后的hash值,在这里我们使用了werkzeug.security包中的generate_password_hash函数来进行加密,由于此函数默认使用了sha1算法,并添加了长度为8的盐值,所以还是相当安全的。一般用途的话也就够用了。

· 验证password的时候,我们需要使用werkzeug.security包中的check_password_hash函数来验证密码

· get_id是UserMixin类中就有的method,在这我们需要overwrite这个method。在json文件中没有对应的user id时,可以使用uuid.uuid4()生成一个用户唯一id

至此,我们就实现了第二步和第五步,接下来要看第三步,如何去维护一个session

维护用户session

先看下代码,这里把相应代码也放入到app.py当中

from forms import LoginFormfrom flask_wtf.csrf import CsrfProtectfrom model import Userfrom flask_login import login_user, login_requiredfrom flask_login import LoginManager, current_userfrom flask_login import logout_user

app = Flask(__name__)

app.secret_key = os.urandom(24)

# use login manager to manage session

login_manager = LoginManager()

login_manager.session_protection = ’strong’

login_manager.login_view = ’login’

login_manager.init_app(app=app)

# 这个callback函数用于reload User object,根据session中存储的user [email protected]_manager.user_loaderdef load_user(user_id):

return User.get(user_id)

# csrf protection

csrf = CsrfProtect()

csrf.init_app(app)

@app.route(’/login’)def login():

form = LoginForm()

if form.validate_on_submit():

user_name = request.form.get(’username’, None)

password = request.form.get(’password’, None)

remember_me = request.form.get(’remember_me’, False)

user = User(user_name, password)

if user.verify_password(password):

login_user(user)

return redirect(request.args.get(’next’) or url_for(’main’))

return render_template(’login.html’, title="Sign In", form=form)

· 维护用户的会话,关键就在这个LoginManager对象。

· 必须实现这个load_user callback函数,用以reload user object

· 当密码验证通过后,使用login_user()函数来登录用户,这时用户在会话中的状态就是登录状态了

受保护网页

保护特定网页,只需要对特定路由加一个装饰器就可以,如下

# app.py

# [email protected](’/’)@app.route(’/main’)@login_requireddef main():

return render_template(

’main.html’, username=current_user.username)# ...

· current_user保存的就是当前用户的信息,实质上是一个User对象,所以我们直接调用其属性, 例如这里我们要给模板传一个username的参数,就可以直接用current_user.username

· 使用@login_required来标识改路由需要登录用户,非登录用户会被重定向到’/login’路由(这个就是由login_manager.login_view = ’login’ 语句来指定的)

用户登出

# app.py

# [email protected](’/logout’)@login_requireddef logout():

logout_user()

return redirect(url_for(’login’))# ...

至此,我们就实现了一个完整的登陆和登出的过程。

另外我们可能还需要其它辅助的功能,诸如发送确认邮件,密码重置,权限分级管理等,这些功能都可以通过flask及其插件来完成,这个大家可以自己探索下啦!

来源:简书

时间: 2024-10-05 04:43:36

Flask中如何实现用户登陆认证?的相关文章

从 &amp;quot;org.apache.hadoop.security.AccessControlException:Permission denied: user=...&amp;quot; 看Hadoop 的用户登陆认证

假设远程提交任务给Hadoop 可能会遇到?"org.apache.hadoop.security.AccessControlException:Permission denied: user=..." , 当然,假设是spark over YARN, 也相同会遇到相似的问题,比如: ?An error occurred while calling None.org.apache.spark.api.java.JavaSparkContext. : org.apache.hadoop.

用户登陆认证

  1)输入用户名密码   2)认证成功后显示欢迎信息   3)输错三次后锁定 #!/usr/local/bin/python3# Author: Jonas Mao import osimport sysimport getpass os.system('clear') #清理屏幕(可选)user_limit = 0while user_limit < 3: username = input('请输入您的用户名:') lock_file = open('lock_list','r+') loc

REST_FRAMEWORK加深记忆-加了用户登陆认证,自定义权限的API接口

哈哈,终于快结束了.. urls.py from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^', include('snippets.urls')), ] urlpatterns += [ url(r'^api-auth/', include('rest_framewo

IOS开发之记录用户登陆状态

今天要说的是如何记录我们用户的登陆状态.例如微信,QQ等,在用户登陆后,关闭应用在打开就直接登陆了.那么我们在App开发中如何记录用户的登陆状态呢?之前在用PHP或者Java写B/S结构的东西的时候,我们用Session来存储用户的登陆信息,Session是存在服务器上仅在一次回话中有效,如果要记录用户的登陆状态,那么会用到一个叫Cookie的东西.Cookie和Session不同,Cookie是存在用户本地的一个文件,Cookie中存的就是用户的登陆信息,当用户在此登陆时,自动从Cookie中

用户登陆状态,ios开发用户登陆

IOS开发之记录用户登陆状态,ios开发用户登陆 上一篇博客中提到了用CoreData来进行数据的持久 化,CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreData还是蛮好用的.今天要说的是如何记录我们用户 的登陆状态.例如微信,QQ等,在用户登陆后,关闭应用在打开就直接登陆了.那么我们在App开发中如何记录用户的登陆状态呢?之前在用PHP或者 Java写B/S结构的东西的时候,我们用Session来存储用户的登陆信息,Session是存在服务器上仅在一

IOS开发之记录用户登陆状态,ios开发用户登陆

IOS开发之记录用户登陆状态,ios开发用户登陆 上一篇博客中提到了用CoreData来进行数据的持久化,CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreData还是蛮好用的.今天要说的是如何记录我们用户的登陆状态.例如微信,QQ等,在用户登陆后,关闭应用在打开就直接登陆了.那么我们在App开发中如何记录用户的登陆状态呢?之前在用PHP或者Java写B/S结构的东西的时候,我们用Session来存储用户的登陆信息,Session是存在服务器上仅在一次回话

关于django用户登录认证中的cookie和session

最近弄django的时候在用户登录这一块遇到了困难,网上的资料也都不完整或者存在缺陷. 写这篇文章的主要目的是对一些刚学django的新手朋友提供一些帮助.前提是你对django中的session和cookie已经有了一定的了解. 我最基本的设想是当用户登陆了网站后,用户访问本网站的其他网页时依旧能识别其身份. 很多教程的方法是在用户的cookie中存储用户名,这种方法当然是非常不安全的. 其实只要我们使用了django中的session,django就会自动在用户的cookie中存储 sess

django 自定义auth中user登陆认证以及自写认证

第一种: 重写自定义auth中user登陆认证模块, 引入MobelBackend from django.contrib.auth.backends import ModelBackend 重写验证模块 class CustomBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): try: user = Hbuser.objects.get(username

flask中的endpoint、自定义转化器、与djnago中session区别、利用装饰器实现登录认证

flask路由中的endpoint 与自定义转化器 ''' endpoint主要用于 反向解析, 例如:login函数中配的路由是/login,其中endpoint='lg' 则在其他函数,可以用 url=url_for('lg'),redirect(url)直接访问login函数 ''' ''' 自定义转化器,可以用来动态更新url_for 的跳转路由 其中 to_python主要是给后端的,可以对路由参数做修改 to_url是给前端url的,可以更新指定的url ''' flask与djan