zoukankan      html  css  js  c++  java
  • 【测试平台学习3】 Django 实现LDAP连接Freeipa登录,重写AuthToken

    前言

    初步使用了Django搭建了一个后端接口,本文单独就django的登录进行解读。

    关于登录

    Django的登录编写是比较简单的,主要现在做系统或者平台一般是接入成熟的登录,例如: 钉钉、freeipa、公司内部账号管理系统等等。

    关于接入钉钉的登录,网上很多,本文就不再赘述,主要介绍一种通过Django对接freeipa的方式,登录认证方式通过LDAP。

    1. 安装ldap3

    (网上很多介绍让你安装另外一种方式Python-LDAP的,你安装下去会吐血。基本不可用了,会出很多问题)

     pip install ldap3

    2. settings配置

        这些值需要和你系统内的FREEIPA对齐

    LDAP_AUTH_URL 
    LDAP_AUTH_SEARCH_BASE 
    LDAP_AUTH_URL = 'ldap://ipa.xxx.cn:389'
    LDAP_AUTH_USE_TLS = False
    LDAP_AUTH_SEARCH_BASE = 'dc=in,dc=***,dc=cn'
    LDAP_AUTH_OBJECT_CLASS = "*"
    LDAP_AUTH_USER_FIELDS = {
        "username": "uid",
    }
    # LDAP_AUTH_USER_LOOKUP_FIELDS = ("uid",)
    LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
    LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
    LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
    LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_openldap"

    3. 登录脚本的编写

      这里面有几个坑:

      3.1 登录是接入式的,你需要新增一个用户UserToken表来存储用户数据和token以及设置用户token的失效时间等等

            加入你用django自带的user表【亲测,非常不好用,里面的字段都定制化了,例如包含了first_name,last_name,email 但是和可能你接入的用户完全没有这些信息】

            还有就是自带的user_token 后续你使用的请求里面带token或者id会不方便, 它是用request.user.pk 去携带用户id的,实际使用的时候这个pk值很可能是null (原因可能就是你没有first_name什么的)

            解决方案: 新增一个UserToken表, 重写AuthToken 逻辑,加上token过期判断

    3.2  LDAP连接的时候报错,[LDAP bind failed: LDAPInvalidCredentialsResult - 49 - invalidCredentials]

           解决方案:修改 依赖包site-packages/django_python3_ldap/ldap.py

           原因是django-ldap里面设置了user查询的参数默认是错误的,一定要修改成你项目组Freeipa要求的形式,如下。

        if kwargs:
            password = kwargs.pop("password")
            username = format_username(kwargs)
            # 需要改造       
            username='uid={},cn=***,cn=***,dc=in,dc=***,dc=cn'.format(kwargs.pop("username"))
    class Login_LDAP(APIView):
    # 访问登录接口不需要授权 authentication_classes
    = [] permission_classes = ()
    # 序列化请求 serializer_class
    = AuthTokenSerializer def post(self, request, *args, **kwargs): """ 用户登录 :param request: :param args: :param kwargs: :return: """ serializer = self.serializer_class(data=request.data, context={"request": request}) serializer.is_valid(raise_exception=True) user = serializer.validated_data["username"] password = serializer.validated_data["password"] print(user, password) server = Server(host=ldap_host, port=ldap_port, use_ssl=False, get_info='ALL') dn = 'uid={},cn=***,cn=***,'.format(user) + search_dn # 通过用户名和密码去访问Freeipa,获取Freeipa的认证信息 conn = Connection(server, dn, password, auto_bind=True) if conn.result["description"] == "success": time_now = datetime.datetime.now() user_instance = UserToken.objects.filter(username=user) # 生成一个MD5格式的token,切记别用Django自带的token
    token
    = md5(user) if user_instance:
    # 更新你的用户表 UserToken.objects.update(username
    =user, token=token, expiration_time=time_now) else: UserToken.objects.create(username=user, token=token, expiration_time=time_now) return JsonResponse(data=token, code="200", msg="成功") else: return JsonResponse(data={'用户Freeipa登录失败'}, code="400", msg="失败") obtain_auth_token = Login_LDAP.as_view()

    4. 重写认证AuthToken

        4.1 这里可以用 cache 存储登录的信息, 发请求的时候调用这里,使用 request.user.id 即可调用用户id  ,  request.user.username则是用户名

        4.2 设置了token超时时间,可以配置到settings.py里面去, REST_FRAMEWORK_TOKEN_EXPIRE_MINUTES=60 

        4.3 其他接口调用该认证则是在接口下使用: authentication_classes = [TokenAuthentication,]   另外要设置到settings.py 里面添加这个  

      'DEFAULT_AUTHENTICATION_CLASSES': [
      # 'rest_framework.authentication.BasicAuthentication',
      'utils.authentication.TokenAuthentication', ],

        

    from django.conf import settings
    from rest_framework.authentication import BaseAuthentication

    from django.core.cache import cache
    from rest_framework.exceptions import APIException

    EXPIRE_MINUTES = getattr(settings, 'REST_FRAMEWORK_TOKEN_EXPIRE_MINUTES', 1) class TokenAuthentication(BaseAuthentication): """Set up token expired time""" def authenticate(self, request): token = request.META.get('HTTP_TOKEN') # 将token放入请求头中 user = cache.get(token) token_obj = UserToken.objects.filter(token=token).first() user = {"user": user, "id": token_obj.id} if user['user'] is not None: print(user) return user, token if not token_obj: raise APIException('Auth Failed!!') create_time = token_obj.expiration_time now_time = datetime.datetime.now() delta = now_time - create_time if delta < datetime.timedelta(days=EXPIRE_MINUTES): remain_time = datetime.timedelta(days=EXPIRE_MINUTES) - delta cache.set(token_obj.token, token_obj.username, min(remain_time.total_seconds(), 3600 * 24)) user = { "user": cache.get(token), "id": token_obj.id } print("user:", user) return user, token else: raise APIException('认证超时')

    5. 设置UserToken的Models

         5.1 本来django有内置的User表,问题是它不适合大部分的情况,上面也说了原因,所以才有如下的设置。其他表在调用用户表的时候使用外键链接既可

         5.2 注意这里的外键只能是int型的id,有些设置的时候返回一些str类型会报错,包括序列化的时候。

         5.3  比较诡异的是在设置如下的用户表,进行数据库迁移,表名没有按照我写的db_name来, 而是 【appname_usertoken】

    # 存放用户登录成功后的token
    class UserToken(models.Model):
        id = models.AutoField(primary_key=True, verbose_name='id主键')
        username = models.CharField(max_length=50, unique=True, verbose_name='用户名')
        token = models.CharField(max_length=64, verbose_name="用户token")
        expiration_time = models.DateTimeField(default=datetime.now, verbose_name="Token过期时间")
    
        class Mate:
            db_table = "test_user_token"
            verbose_name = "用户Token"
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.token
    
    
    
    
    # 通过外键关联其他的SQL表
        user = models.ForeignKey(UserToken, on_delete=models.CASCADE, max_length=50, verbose_name='创建人')

    结语

    Django 提供了太多定制化的操作,不是很好用,单单对于初学者来说,写个用户认证+Token都这么麻烦... 优缺点总结如下:

    1. 框架本身提供的model-view-urls-form 这一套可以和spring mvc架构媲美,而且简易程度差不多,用户不用写sql ,而可以关注业务本身

    2. 框架提供的User-Token 过于定制化了,肯定不适用于公司项目,要开发人员自行去修改大量的配置以及重写等等,这一块是完全不合适的。

    3. 用户千万别一不小心升级django, 升级后会造成一系列不适配的问题,只能回退原版本,这一点来说java的spring是碾压,因为java的高版本肯定是兼容低版本的。

    未完待续...

  • 相关阅读:
    GoldenGate 19.1实时文本文件加载攻略
    windows 10 excel 打开超连接提示 组织策略阻止...
    验证ogg同步数据库表无主键表且目标表包含隐藏字段
    配置ogg从Oracle到PostgreSQL的同步复制json数据
    pi
    GoldenGate 19.1 发布
    ogg同步DDL时,源和目标端表空间名称不同的解决思路
    总目录索引(开发精华总结)
    Spring Cloud Nacos分布式配置中心
    Spring Cloud Nacos&Feign负载均衡
  • 原文地址:https://www.cnblogs.com/Ronaldo-HD/p/14517561.html
Copyright © 2011-2022 走看看