• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

使用 LDAP 进行 Flask 身份验证

flask是一个基于 python 的微型 Web 框架,可让您快速高效地编写 Web 应用程序。“微”并不意味着 Flask 缺乏功能。它只是指 Flask 保持其核心小巧且高度可扩展的事实。

使用 LDAP 进行 Flask 身份验证  第1张

LDAP(轻量级目录访问协议)对不同的人有不同的含义,这取决于他们的使用。它是一种 Internet 协议,用于从以目录式结构存储数据的服务器中查找有关用户的联系信息、有关证书的信息、网络指针等。它最流行的用途是提供“单点登录”,用户可以通过登录一次访问多个应用程序,因为密码将在服务之间共享。

在本教程中,我将带您了解如何使用 LDAP 在 Flask 应用程序中实现用户身份验证。为了演示这一点,我将创建一个带有主页和登录页面的小型应用程序。用户需要在登录页面上输入登录详细信息。如果用户输入的凭据在提供的 LDAP 服务器上成功通过身份验证,则用户将登录。否则,将向用户显示相应的消息。

我假设您对 Flask、LDAP、Flask-Login扩展以及在开发 Python 应用程序时要遵循的使用virtualenv的环境设置最佳实践有基本的了解。

LDAP 服务器

要使用 LDAP,您需要一个服务器;OpenLDAP 是 LDAP 的开源实现。其他替代方案是 Microsoft Azure Active Directory,这是一项高级服务。由于我们只需要一个测试服务器,因此不需要设置本地服务器。我们将使用来自 Forum Systems的公开可用的 LDAP 测试服务器。

安装依赖

需要为我们将要开发的应用程序安装以下包。


$ pip install ldap3
$ pip install Flask-WTF flask-sqlalchemy Flask-Login

上述命令应安装此应用程序运行所需的所有必需包。

申请结构

首先,应用程序需要结构化,以便于理解。

flask_app/
    my_app/
        - __init__.py
        auth/
            - __init__.py
            - models.py
            - views.py
        static/
            - css/
            - js/
        templates/
            - base.html
            - home.html
            - login.html
    - run.py

所有文件将在下面讨论。该static文件夹包含标准的 Bootstrap CSS 和 JS 文件。

应用程序本身

首先需要编写配置文件:

flask_app/my_app/__init__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
 
app = Flask(__name__)
app.config['SQLALCHEMY_dataBASE_URI'] = 'sqlite:////tmp/test.db'
app.config['WTF_CSRF_SECRET_KEY'] = 'random key for form'
app.config['LDAP_PROVIDER_URL'] = 'ldap://ldap.forumsys.com:389/'
app.config['LDAP_PROTOCOL_VERSION'] = 3
 
db = SQLAlchemy(app)
 
app.secret_key = 'randon_key'
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'auth.login'
 
ctx = app.test_request_context()
ctx.push()
 
from my_app.auth.views import auth
app.register_blueprint(auth)
 
db.create_all()

在上面的文件中,应用程序已根据扩展和 LDAP 配置的需要配置了不同的选项。接下来是扩展的初始化,最后是数据库的创建。

SQLALCHEMY_DATABASE_URI如果数据库在该位置尚不存在,则最后一条语句会在提供的位置创建一个新数据库;否则,它会加载具有相同数据库的应用程序。

flask_app/my_app/auth/models.py

import ldap3
from flask_wtf import Form
from wtforms import StringField,PasswordField
from wtforms import validators
 
from my_app import db, app
 
def get_ldap_connection():
    conn = ldap3.initialize(app.config['LDAP_PROVIDER_URL'])
    return conn
 
class User(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    username = db.Column(db.String(100))
     
    def __init__(self, username, password):
        self.username = username
 
    @staticmethod
    def try_login(username, password):
        conn = get_ldap_connection()
        conn.simple_bind_s(
            'cn=%s,ou=mathematicians,dc=example,dc=com' % username,password
        )
    def is_authenticated(self):
        return True
  
    def is_active(self):
        return True
  
    def is_anonymous(self):
        return False
  
    def get_id(self):
        return self.id
 
class LoginForm(Form):
    username = StringField('Username',[validators.Datarequired()])
    password = PasswordField('Password',[validators.DataRequired()])

上面的文件从创建一个User模型开始,该模型仅包含一个username用于演示目的的字段。您可以根据应用程序的上下文根据需要添加任意数量的字段。

Flask-Login 扩展需要方法is_authenticated()、is_active()、is_anonymous()和。get_id()该try_login()方法通过首先创建与 LDAP 服务器的连接,然后使用用户名和密码通过创建简单绑定来登录来执行实际的身份验证过程。

该simple_bind_s()方法采用的第一个参数是 DN,它由 LDAP 服务器提供,并随 LDAP 记录配置而变化。表单处理由 负责LoginForm,它扩展了Flask-WTFormsForm提供的类。

flask_app/my_app/auth/views.py

import ldap3
from flask import (request,render_template,flash, redirect, url_for,
 Blueprint,g
)
from flask_login import (current_user,login_user,logout_user,login_required)
from my_app import login_manager,db
 
auth = Blueprint('auth', __name__)
 
from my_app.auth.models import User,LoginForm
 
@login_manager.user_loader
def load_user(id):
    return User.query.get(int(id))
 
@auth.before_request
def get_current_user():
    g.user = current_user
 
@auth.route('/')
@auth.route('/home')
def home():
    return render_template('home.html')
 
@auth.route('/login', methods = ['GET','post'])
def login():
    if current_user.is_authenticated:
        flash('You are already logged in.')
        return redirect(url_for('auth.home'))
    form = LoginForm(request.form)
 
    if request.method == 'POST' and form.validate():
        username = request.form['username']
        password = request.form['password']
         
        try:
            User.try_login(username,password)
        except:
            flash(
                'Invalid username or password. Please try again.',
                'danger')
            return render_template('login.html', form=form)
        user = User.query.filter_by(username = username)
 
        if not user:
            user = User(username = username, password = password)
            db.session.add(user)
            db.commit()
        login_user(user)
        flash('You have successfully logged in.', 'success')
        return redirect(url_for('auth.home'))
 
    if form.errors:
        flash(form.errors, 'danger')
  
    return render_template('login.html', form=form)
         
@auth.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('auth.home'))

在上面的文件中,方法load_user()和get_current_user()是 Flask-Login 扩展所需要的。接下来是我们应用程序的处理程序,它们由各自的路由装饰。

home()只是为用户呈现主页。主页的内容由 template 决定flask_app/my_app/templates/home.html,我们将在稍后讨论。

主要关注的处理程序是login()因为它处理完整的登录过程。如果登录用户尝试访问此页面,该页面将自动重定向到主页。否则,登录过程将从用户的 LDAP 用户名和密码作为表单输入的地方开始flask_app/my_app/templates/login.html。

使用这些凭据,应用程序会尝试从我们之前看到的配置中提供的 LDAP 服务器对用户进行身份验证。如果用户通过身份验证,如果是首次访问应用程序的用户,则应用程序会为用户创建一条新记录;否则,它只是让用户使用他们现有的记录登录。

需要时向用户显示 Flash 消息以保持用户与应用程序的互动。

logout()处理程序只是清除当前登录用户的会话,结果用户被注销。

flask_app/my_app/templates/base.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask Authentication with LDAP Tutorial</title>
    <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
    <link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet">
  </head>
  <body>
    <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div>
        <div>
          <a href="{{ url_for('auth.home') }}">Flask LDAP Demo</a>
        </div>
      </div>
    </div>
    <div>
    <br/>
    <div>
      {% for category, message in get_flashed_messages(with_categories=true) %}
        <div class="alert alert-{{category}} alert-dismissable">
          <button type="button" data-dismiss="alert" aria-hidden="true">&times;</button>
          {{ message }}
        </div>
      {% endfor %}
      </div>
    {% block container %}{% endblock %}
    </div>
  
    <!-- jquery (necessary for Bootstrap's javascript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
    <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
    {% block scripts %}
    {% endblock %}
  </body>
</html>

以上是包含页眉、页脚和其他在整个应用程序中通用的基本组件的基本文件。这有助于保持模板非常模块化且易于理解,因为每个模板仅包含与其处理程序和功能相关的代码。

尽管这里定义了所有公共组件,但我添加了一个空块scripts,可以在任何继承base.html. 请注意上面是如何闪烁消息的,以及如何动态地使用 Bootstrap CSS 类来使闪烁消息警报框具有适当的样式。

该container块将由其余模板扩展以添加它们各自的内容。

flask_app/my_app/templates/home.html

{% extends 'base.html' %}
  
{% block container %}
  <h1>Welcome to the Flask-LDAP Authentication Demo</h1>
  {% if current_user.is_authenticated %}
    <h3>Hey {{ current_user.username }}!!</h3>
    <a href="{{ url_for('auth.logout') }}">Click here to logout</a>
  {% else %}
  Click here to <a href="{{ url_for('auth.login') }}">login with LDAP</a>
  {% endif %}
{% endblock %}

请注意基本模板是如何扩展的,以及主页内容是如何添加到块容器中的。

如果用户已登录,他们会收到用户名并显示一条消息以注销。否则,将向用户显示一条登录消息,其中包含指向登录页面的链接。

flask_app/my_app/templates/login.html

{% extends 'home.html' %}
  
{% block container %}
  <div class="top-pad">
    <form
        method="POST"
        action="{{ url_for('auth.login') }}"
        role="form">
      {{ form.csrf_token }}
      <div class="form-group">{{ form.username.label }}: {{ form.username() }}</div>
      <div class="form-group">{{ form.password.label }}: {{ form.password() }}</div>
      <button type="submit" class="btn btn-default">Submit</button>
    </form>
  </div>
{% endblock %}

登录模板仅包含一个带有 和 字段的username表单password。

运行应用程序

要运行应用程序,请执行脚本run.py。这个脚本的内容是:

from my_app import app
app.run(debug=True)

现在只需从命令行执行:

python run.py

最后,在您喜欢的浏览器中打开http://127.0.0.1:5000/并查看您的应用程序的运行情况。一切都应该是不言自明的。为了登录,您可以使用以下用户:

  • riemann

  • gauss

  • euler

  • euclid

所有用户密码都是password。

结论

在本教程的过程中,我们在 Flask-Login 扩展的帮助下使用 Flask 构建了一个小而有效的 Web 应用程序。这个应用程序只需要一个用户名和密码,并根据提供的 LDAP 服务器对用户进行身份验证。发挥您的想象力,根据您的需要调整和扩展应用程序。

文章目录
  • LDAP 服务器
  • 安装依赖
  • 申请结构
  • 应用程序本身
      • flask_app/my_app/__init__.py
      • flask_app/my_app/auth/models.py
      • flask_app/my_app/auth/views.py
      • flask_app/my_app/templates/base.html
      • flask_app/my_app/templates/home.html
      • flask_app/my_app/templates/login.html
  • 运行应用程序
  • 结论
  • 发表评论