首页 > 其他分享 >关于`django-auth-ldap` 和 `ldap3`如何处理与 LDAP 服务器的连接和身份验证的问题

关于`django-auth-ldap` 和 `ldap3`如何处理与 LDAP 服务器的连接和身份验证的问题

时间:2024-09-22 12:22:12浏览次数:10  
标签:return 绑定 用户 auth 身份验证 user LDAP password

在使用 Django 进行 Active Directory (AD) 身份验证时,django-auth-ldapldap3 都涉及到与 LDAP 服务器的交互。一个关键的差异在于如何处理与 LDAP 服务器的连接和身份验证。具体来说,django-auth-ldap 通常使用一个 绑定用户 (Bind DN) 来执行搜索和验证操作,而使用 ldap3 时,你有更多的灵活性,可以选择不同的绑定方式。

为什么需要绑定用户 (Bind DN)?

在 LDAP 身份验证过程中,绑定用户用于执行以下操作:

  1. 搜索用户信息:在用户登录时,系统需要在 LDAP 目录中查找用户的条目。为了执行这些搜索操作,通常需要一个具有读取权限的绑定用户。
  2. 验证用户凭据
    :有两种主要方式来验证用户凭据:
  • 服务账户绑定:使用预先配置的管理员账户(Bind DN 和密码)来搜索用户,然后尝试绑定用户的凭据。
  • 直接用户绑定:直接使用用户提供的凭据尝试绑定,如果成功,则认证通过。

django-auth-ldap 中的 Bind DN

django-auth-ldap 通常使用一个 服务账户 来连接 LDAP 服务器并执行用户搜索。这就是为什么在 settings.py 中需要配置 AUTH_LDAP_BIND_DNAUTH_LDAP_BIND_PASSWORD

# settings.py

import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType

AUTH_LDAP_SERVER_URI = "ldap://your-ad-server.example.com"

AUTH_LDAP_BIND_DN = "cn=admin,dc=example,dc=com"
AUTH_LDAP_BIND_PASSWORD = "your-password"

AUTH_LDAP_USER_SEARCH = LDAPSearch(
    "ou=users,dc=example,dc=com",
    ldap.SCOPE_SUBTREE,
    "(sAMAccountName=%(user)s)"
)

# 其他配置...

这里的 AUTH_LDAP_BIND_DNAUTH_LDAP_BIND_PASSWORD 是用于绑定到 LDAP 服务器的服务账户凭据。这个账户需要有权限搜索用户和组信息。

ldap3 中的 Bind DN

使用 ldap3 时,绑定用户的配置取决于你的实现方式。在 ldap3 的自定义认证后端中,你有两种主要的绑定策略:

  1. 使用服务账户进行搜索
  • 先使用服务账户绑定,搜索用户信息。
  • 然后使用用户提供的凭据进行绑定验证。
  1. 直接使用用户凭据进行绑定
  • 直接尝试使用用户提供的凭据进行绑定。
  • 如果绑定成功,则认证通过;否则失败。
示例 1:使用服务账户进行搜索

在这种方法中,ldap3 会先使用服务账户绑定来搜索用户,然后再使用用户的凭据进行绑定验证。这与 django-auth-ldap 的方法类似。

# your_app/auth_backend.py

from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User
from ldap3 import Server, Connection, ALL, NTLM

class LDAPBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None):
        if username is None or password is None:
            return None

        # LDAP服务器配置
        LDAP_SERVER = 'ldap://your-ad-server.example.com'
        LDAP_DOMAIN = 'DOMAIN'
        LDAP_BASE_DN = 'DC=example,DC=com'

        # 服务账户绑定
        service_user = f'cn=admin,{LDAP_BASE_DN}'
        service_password = 'your-password'

        server = Server(LDAP_SERVER, get_info=ALL)
        conn = Connection(server, user=service_user, password=service_password, authentication=NTLM)
        if not conn.bind():
            return None

        # 搜索用户
        conn.search(
            search_base=LDAP_BASE_DN,
            search_filter=f'(sAMAccountName={username})',
            attributes=['cn', 'mail', 'givenName', 'sn']
        )

        if not conn.entries:
            return None

        user_entry = conn.entries[0]

        # 使用用户凭据进行绑定验证
        user_dn = user_entry.entry_dn
        user_conn = Connection(server, user=user_dn, password=password, authentication=NTLM)
        if not user_conn.bind():
            return None

        # 获取或创建Django用户
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            user = User(
                username=username,
                first_name=user_entry.givenName.value,
                last_name=user_entry.sn.value,
                email=user_entry.mail.value
            )
            user.set_unusable_password()
            user.save()

        return user

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

在这个示例中,ldap3 首先使用服务账户 service_userservice_password 绑定到 LDAP 服务器,执行用户搜索。然后,使用找到的用户 DN 和用户提供的密码尝试绑定验证。

示例 2:直接使用用户凭据进行绑定

在某些情况下,你可以直接使用用户的凭据来绑定 LDAP 服务器进行验证。这种方法不需要预先配置服务账户,但有一些限制,例如无法同步用户信息,或者无法执行更复杂的查询操作。

# your_app/auth_backend.py

from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User
from ldap3 import Server, Connection, ALL, NTLM

class LDAPBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None):
        if username is None or password is None:
            return None

        # LDAP服务器配置
        LDAP_SERVER = 'ldap://your-ad-server.example.com'
        LDAP_DOMAIN = 'DOMAIN'
        LDAP_BASE_DN = 'DC=example,DC=com'

        server = Server(LDAP_SERVER, get_info=ALL)
        user_dn = f'DCN=DOMAIN,{LDAP_BASE_DN}'  # 根据你的AD配置

        # 直接使用用户凭据绑定
        conn = Connection(server, user=f'{LDAP_DOMAIN}\\{username}', password=password, authentication=NTLM)
        if not conn.bind():
            return None

        # 获取用户信息
        conn.search(
            search_base=LDAP_BASE_DN,
            search_filter=f'(sAMAccountName={username})',
            attributes=['cn', 'mail', 'givenName', 'sn']
        )

        if not conn.entries:
            return None

        user_entry = conn.entries[0]

        # 获取或创建Django用户
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            user = User(
                username=username,
                first_name=user_entry.givenName.value,
                last_name=user_entry.sn.value,
                email=user_entry.mail.value
            )
            user.set_unusable_password()
            user.save()

        return user

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

在这个示例中,ldap3 直接使用用户提供的凭据绑定到 LDAP 服务器进行验证。如果绑定成功,则认证通过。这种方法适用于简单的身份验证场景,但不适合需要更复杂的 LDAP 操作(如用户同步)的情况。

为什么在 ldap3 示例中没有显式配置绑定用户?

在之前的 ldap3 示例中,我的示例使用了用户提供的凭据进行绑定,而没有使用服务账户。这种情况下,确实不需要配置服务账户的 DN 和密码。但是,为了执行搜索操作(例如获取用户的详细信息),通常需要一个具有读取权限的绑定用户。

如果你需要执行用户搜索,建议使用服务账户进行初步绑定,然后再验证用户凭据。下面是一个更完整的 ldap3 示例,展示如何使用服务账户进行用户搜索和凭据验证。

完整示例:使用 ldap3 和服务账户进行身份验证

1. 安装 ldap3

确保已经安装了 ldap3

pip install ldap3
2. 创建自定义认证后端

在你的 Django 应用(例如 your_app)中,创建 auth_backend.py

# your_app/auth_backend.py

from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User
from ldap3 import Server, Connection, ALL, NTLM
from django.conf import settings

class LDAPBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None):
        if username is None or password is None:
            return None

        # LDAP服务器配置
        LDAP_SERVER = 'ldap://your-ad-server.example.com'
        LDAP_DOMAIN = 'DOMAIN'
        LDAP_BASE_DN = 'DC=example,DC=com'

        # 服务账户绑定
        SERVICE_BIND_DN = "cn=admin,dc=example,dc=com"
        SERVICE_BIND_PASSWORD = "your-password"

        server = Server(LDAP_SERVER, get_info=ALL)
        service_conn = Connection(server, user=SERVICE_BIND_DN, password=SERVICE_BIND_PASSWORD, authentication=NTLM)

        if not service_conn.bind():
            # 绑定失败,可能是服务账户信息错误
            return None

        # 搜索用户
        service_conn.search(
            search_base=LDAP_BASE_DN,
            search_filter=f'(sAMAccountName={username})',
            attributes=['cn', 'mail', 'givenName', 'sn']
        )

        if not service_conn.entries:
            return None

        user_entry = service_conn.entries[0]
        user_dn = user_entry.entry_dn

        # 使用用户凭据进行绑定验证
        user_conn = Connection(server, user=user_dn, password=password, authentication=NTLM)
        if not user_conn.bind():
            return None

        # 获取或创建Django用户
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            user = User(
                username=username,
                first_name=user_entry.givenName.value,
                last_name=user_entry.sn.value,
                email=user_entry.mail.value
            )
            user.set_unusable_password()
            user.save()

        return user

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None
3. 配置 settings.py

settings.py 中配置自定义认证后端:

# settings.py

INSTALLED_APPS = [
    # 其他应用...
    'your_app',
]

AUTHENTICATION_BACKENDS = (
    'your_app.auth_backend.LDAPBackend',  # 自定义LDAP认证后端
    'django.contrib.auth.backends.ModelBackend',
)

# 其他必要配置...
4. 配置视图和权限

your_app/views.py 中配置认证视图,例如登录、登出和仪表板:

# your_app/views.py

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required, permission_required

def user_login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            return redirect('dashboard')
        else:
            return render(request, 'your_app/login.html', {'error': 'Invalid credentials'})
    return render(request, 'your_app/login.html')

@login_required
def dashboard(request):
    return render(request, 'your_app/dashboard.html')

def user_logout(request):
    logout(request)
    return redirect('login')

@login_required
@permission_required('your_app.manage_users', raise_exception=True)
def manage_users(request):
    from ldap3 import Server, Connection, ALL, NTLM

    # LDAP服务器配置
    LDAP_SERVER = 'ldap://your-ad-server.example.com'
    SERVICE_BIND_DN = "cn=admin,dc=example,dc=com"
    SERVICE_BIND_PASSWORD = "your-password"
    LDAP_BASE_DN = 'DC=example,DC=com'

    server = Server(LDAP_SERVER, get_info=ALL)
    conn = Connection(server, user=SERVICE_BIND_DN, password=SERVICE_BIND_PASSWORD, authentication=NTLM)
    if not conn.bind():
        return render(request, 'your_app/error.html', {'message': '无法连接到 AD 服务器'})

    # 查询所有用户
    conn.search(
        search_base=LDAP_BASE_DN,
        search_filter='(objectClass=person)',
        attributes=['cn', 'mail', 'givenName', 'sn']
    )
    users = conn.entries

    return render(request, 'your_app/manage_users.html', {'users': users})
5. 创建模板

your_app/templates/your_app/ 目录下创建 login.htmldashboard.htmlmanage_users.html

之前的文章已经提供了这些模板的示例。

6. 定义权限

your_app/models.py 中定义自定义权限:

# your_app/models.py

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    # 其他用户相关字段

    class Meta:
        permissions = [
            ("manage_users", "Can manage AD users"),
            ("manage_groups", "Can manage AD groups"),
        ]

同步数据库并在 Django Admin 中分配权限:

python manage.py makemigrations
python manage.py migrate
7. 分配权限
  1. 登录 Django Admin
  2. 为需要管理 AD 用户的管理员用户分配 manage_users 权限:
  • 进入用户详情页面。
  • 在权限部分,选择 Can manage AD users

为什么 ldap3 配置中没有显式设置绑定用户?

在前面的 ldap3 示例中,如果仅展示了使用用户凭据直接绑定进行认证的情形,就不需要显式配置服务账户的 DN 和密码。

然而,实际应用中,为了执行用户搜索和获取用户信息,通常需要一个服务账户来绑定 LDAP 服务器。

因此,在更完整的实现中,一般会需要配置服务账户的 DN 和密码,正如 django-auth-ldap 中所做的那样。

关键区别总结

特性

django-auth-ldap

ldap3

类型

Django 认证后端扩展

通用 LDAP 客户端库

依赖

python-ldap(需要编译 C 扩展)

纯 Python 实现,不需要编译

安装难度

高(尤其在 Windows 上)

低,简单通过 pip install ldap3 安装

配置复杂度

简单,通过 Django settings.py 配置

需要手动编写自定义认证后端

功能

提供现成的认证和用户同步功能

提供灵活的 LDAP 操作 API,适用于多种用途

跨平台兼容性

依赖 python-ldap 的编译,跨平台支持有限(Windows 上较难)

高,纯 Python 实现,跨平台兼容性好

灵活性

主要聚焦于认证和用户同步,灵活性有限

高,适用于各种自定义的 LDAP 交互需求

选择建议

  • 选择 django-auth-ldap:
  • 如果你希望快速集成 LDAP 认证,并且能够成功安装其依赖项(如在 Linux 环境下)。
  • 需要现成的功能,如用户同步和组权限映射,减少开发工作量。
  • 选择 ldap3:
  • 如果你在 Windows 上开发,或者希望避免编译依赖问题。
  • 需要更高的灵活性,能够根据项目需求自定义 LDAP 交互。
  • 愿意编写自定义的 Django 认证后端,投入一些开发工作以实现所需功能。

进一步优化

无论你选择哪种方法,都可以根据项目需求进行优化:

  • 安全性
  • 使用 LDAPS(LDAP over SSL)来加密与 LDAP 服务器的通信。
  • 确保服务账户拥有最小必要权限。
  • 性能
  • 对频繁的 LDAP 查询进行缓存,减少对 LDAP 服务器的负载。
  • 优化搜索过滤器和基础 DN,提升查询效率。
  • 用户同步
  • 定期同步 LDAP 用户到 Django 用户模型,保持数据一致性。
  • 使用信号或后台任务(如 Celery)实现自动同步。

django-auth-ldapldap3 都是强大的工具,用于将 Django 应用与 LDAP 服务器集成。django-auth-ldap 提供了一个简便的解决方案,适合希望快速集成并拥有现成功能的项目,而 ldap3 则提供了更高的灵活性和跨平台兼容性,适合需要自定义 LDAP 交互的项目。

标签:return,绑定,用户,auth,身份验证,user,LDAP,password
From: https://blog.51cto.com/mlxia/12080004

相关文章

  • 使用 Nextjs Prisma、TailwindCSS 和 Next Auth 进行旅行预订
    旅行预订应用程序使用next.js14、tailwindcss、typescript和prisma进行中的旅行应用程序。允许用户注册、游览浏览、预订和评论提交。对开发过程中的所有贡献开放。入门克隆存储库:gitclonehttps://github.com/saidmounaim/travel-booking.git安装依赖项:npminstall......
  • 使用 Nextjs TypeScript、Prisma 和 Next-Auth 的休闲服装应用
    休闲服装应用使用next.js14、tailwindcss、typescript和prisma的休闲服装应用。包括用户注册、产品过滤和购物车管理。对开发过程中的所有贡献开放。入门克隆存储库:gitclonehttps://github.com/saidmounaim/casualclothes.git安装依赖项:npminstall在根目录创建一个......
  • 搭建kuboard-v3并配置使用ldap登录
    官方文档:https://www.kuboard.cn/install/v3/install-in-k8s.htmlnamespace.yaml点击查看代码apiVersion:v1kind:Namespacemetadata:name:kuboardconfigMap.yaml点击查看代码apiVersion:v1kind:ConfigMapmetadata:name:kuboard-v3-confignamespa......
  • Python项目有哪些常用LDAP连接与认证的方法以及他们的特性
    django-auth-ldap和ldap3都是用于与LDAP(轻量级目录访问协议)服务器(如ActiveDirectory)交互的Python库,但它们在设计目标、实现方式和集成方式上有显著的不同。理解它们的区别对于选择适合你项目需求的解决方案至关重要。以下是对这两个库的详细比较,以及它们在配置后端身份验证......
  • oauth2为什么一定要授权码?
    引用网上图片来说明一下理解这个OAuth2的关键是要记住,code是前端持有的,access_token是后端持有的,code是用来一次性换access_token的,access_token才是后续请求资源的认证信息第一种情况:如果在第4步生成的不是code,而是access_token,然后在第5步重定向的时候返回给小兔软件,那么......
  • OAuth2.0授权-gitee授权码模式
    OAuth2.0授权验证-gitee授权码模式本文主要介绍如何笔者自己是如何使用gitee提供的OAuth2.0协议完成授权验证并登录到自己的系统,完整模式如图1、创建应用打开gitee个人中心->第三方应用->创建应用创建应用后在我的应用界面,查看已创建应用的ClientID和ClientSecret2、对接......
  • centos7LDAP服务搭建
    ladp服务搭建用户名:cn=admin,dc=test,dc=com密码:1234561)软件安装yuminstallopenldapopenldap-clientsopenldap-servers-y2)配置OpenLDAPServervim/etc/openldap/slapd.d/cn=config/olcDatabase={1}monitor.ldif将cn=Manager,dc=my-domain,dc=com改为你自己的,内容可以随......
  • 高可用!一个基于 SpingBoot + Oauth2 的单点认证授权中心!
    大家好,我是Java陈序员。现代企业中,往往包含多个系统,如果每个系统都需要登录注册,这样的用户体验很不好!这时,就需要设计一个单点登录系统,一次登录处处登录,一次退出处处退出!今天,给大家介绍一个基于SpingBoot+Oauth2的单点认证授权中心,支持分布式高可用!关注微信公众号:【Java......
  • Django Auth组件
    文章目录前言一、使用场景二、使用步骤1.验证用户(authenticate()方法)2.注册用户3.退出登陆4.装饰器前言Django的用户认证组件基于以下几个核心概念:1.用户认证:处理用户的登录、注销和密码管理,提供了一个User模型和相关的视图、表单和后台管理功能2.权限和组......
  • MongoDB增加身份验证
    1.数据库添加用户和密码mongo>useadmin>db.createUser({user:"nucRoot",pwd:"f71F!6",roles:["root"]}) 2.修改启动文件,通过auth方式启动,完整如下(INI格式)dbpath=/usr/local/mongodb/data/dblogpath=/usr/local/mongodb/data/logs/mongodb.loglogap......