前言

接之前我的文章,django+ldap+memcache实现单点登录+统一认证ldap部署相关,ldap双机\LAM配置管理\ldap备份还原,目前来说,我们已经有了高可用性的ldap环境了,里边也有了一些用户信息,后边要说一说通过django调用ldap的实现方式,里边主要涉及3个模块,django-auth-ldap:用于从ldap同步账户、登录验证,它和ldap结合的很好,但它不能反向直接操作ldap,只能进行从ldap向下游系统的同步,所以还需要python-ldap模块,以便实现反向对ldap的增删改查,我这边的具体需求就是注册用户、重置密码等,另外还需要一个python-memcached,用于把生成的session放到mc中
查了网上大量的关于django+ldap实现统一认证的文章,大部分都是只写了django-auth-ldap或者python-ldap来实现,但是如果要实现一套完整的统一认证系统,实际上这2个模块都是需要的

django-auth-ldap

这个模块,基本在settings.py通过配置就可以拿来用了,基本不需要对代码做什么修改。用于从ldap里拿到信息传给sso系统做后续处理,但通过它无法让sso系统反向操作ldap,无论django的前台和admin都适用
官方文档: https://pythonhosted.org/django-auth-ldap/authentication.html
中文翻译: https://darkcooking.gitbooks.io/django-auth-ldap/content/chapter9.html
不过这个翻译版是个简化版本,有的东西不全,不过基本也够用了,并且网站打开很慢,我把它的pdf放在这里,可以自行查看 http://files.cnblogs.com/files/caseast/django-auth-ldap.pdf
django官网(session部分):https://docs.djangoproject.com/en/1.10/topics/http/sessions/
ok!直接上代码吧,具体的配置说明,不明确的还请参考官方文档,里边我把log模块和session这部分配置也写一下顺道,不单独开篇描述了
—settings.py—

SESSION_ENGINE = "django.contrib.sessions.backends.cache" # 另外还有个cached_db,这两个的区别是,cache只写缓存,cached_db除了写缓存还同时写数据库,如果对于session的安全性要求高可以选择cached_db
SESSION_COOKIE_AGE = 86400  # 设置session有效期为一天,默认两周
SESSION_COOKIE_DOMAIN = ".ssotest.net"  # 此配置不能解决跨域问题,但是能解决a.ssotest.net与b.ssotest.net的session共享问题,不加此属性,跨站(非跨域)时,无法传递session
CACHES = {
    \'default\': {
        \'BACKEND\': \'django.core.cache.backends.memcached.MemcachedCache\',
        \'LOCATION\': [  # 连接的是2个mc,python-memcached本身有监活,挂一个mc会自动将请求分配到好的mc上
            \'ldap1.prod.bj1.ssotest.net:11211\',
            \'ldap2.prod.bj1.ssotest.net:11211\',
        ],
        \'TIMEOUT\': 30,
        \'OPTIONS\': {
            \'MAX_ENTRIES\': 3000
        }
    }
}

LOGIN_URL = "/account/login/"

# ### ldap 配置部分BEGIN ### #
import ldap
from django_auth_ldap.config import LDAPSearch, PosixGroupType

AUTHENTICATION_BACKENDS = (
    \'django_auth_ldap.backend.LDAPBackend\',  # 配置为先使用LDAP认证,如通过认证则不再使用后面的认证方式
    \'django.contrib.auth.backends.ModelBackend\', # sso系统中手动创建的用户也可使用,优先级靠后。注意这2行的顺序
)

base_dn = \'dc=ldap,dc=ssotest,dc=net\'
AUTH_LDAP_SERVER_URI = \'ldap://ldap.ssotest.net\'
AUTH_LDAP_BIND_DN = \'uid=ssoadmin,ou=People,dc=ldap,dc=ssotest,dc=net\'
AUTH_LDAP_BIND_PASSWORD = \'ssotest@123\'
AUTH_LDAP_USER_SEARCH = LDAPSearch(\'ou=People,%s\' % base_dn, ldap.SCOPE_SUBTREE, "(uid=%(user)s)") # 用户的DN是uid=caojun,ou=People,dc=ldap,dc=ssotest,dc=net,所以用uid
AUTH_LDAP_ALWAYS_UPDATE_USER = True  # This is the default, but I like to be explicit.

AUTH_LDAP_USER_ATTR_MAP = {  # key为数据库字段名,value为ldap中字段名,此字典解决django model与ldap字段名可能出现的不一致问题
    "username": "uid",
    "name": "cn",
    "email": "mail"
}

# 组权限管理 #
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(\'ou=Group,dc=ldap,dc=ssotest,dc=net\', ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)")
AUTH_LDAP_GROUP_TYPE = PosixGroupType(name_attr="cn") # 组的DN是cn=员工,ou=Group,dc=ldap,dc=ssotest,dc=net,所以type是cn
AUTH_LDAP_USER_FLAGS_BY_GROUP = { # django admin的is_staff|superuser属性映射为ldap的管理员
    "is_staff": u"cn=管理员,ou=Group,dc=ldap,dc=ssotest,dc=net",
    "is_superuser": u"cn=管理员,ou=Group,dc=ldap,dc=ssotest,dc=net"
}
AUTH_LDAP_REQUIRE_GROUP = u"cn=员工,ou=Group,dc=ldap,dc=ssotest,dc=net"  # 只有此group可用ldap进行认证
AUTH_LDAP_DENY_GROUP = u"cn=黑名单,ou=Group,dc=ldap,dc=ssotest,dc=net"  # 此group不能使用ldap进行认证,直接deny掉,不会后续往django创建信息
AUTH_LDAP_MIRROR_GROUPS = True  # 直接把ldap的组复制到django一份,和AUTH_LDAP_FIND_GROUP_PERMS互斥.用户每次登录会根据ldap来更新数据库的组关系
# AUTH_LDAP_FIND_GROUP_PERMS = True  # django从ldap的组权限中获取权限,这种方式,django自身不创建组,每次请求都调用ldap
# AUTH_LDAP_CACHE_GROUPS = True  # 如打开FIND_GROUP_PERMS后,此配置生效,对组关系进行缓存,不用每次请求都调用ldap
# AUTH_LDAP_GROUP_CACHE_TIMEOUT = 600  # 缓存时间
# ### ldap 配置部分END ### #

# ### log 配置部分BEGIN ### #
LDAP_LOGS = os.path.join(BASE_DIR, \'logs/ldap.log\')
stamdard_format = \'[%(asctime)s][%(threadName)s:%(thread)d]\' + \
                  \'[task_id:%(name)s][%(filename)s:%(lineno)d] \' + \
                  \'[%(levelname)s]- %(message)s\'
LOGGING = {
    \'version\': 1,
    \'disable_existing_loggers\': False,
    \'formatters\': {
        \'standard\': {  # 详细
            \'format\': stamdard_format
        },
    },
    \'handlers\': {
        \'default\': {
            \'level\': \'DEBUG\',
            \'class\': \'logging.handlers.RotatingFileHandler\',
            \'filename\': LDAP_LOGS,
            \'maxBytes\': 1024 * 1024 * 100,  # 5 MB
            \'backupCount\': 5,
            \'formatter\': \'standard\',
        },
        \'console\': {
            \'level\': \'DEBUG\',
            \'class\': \'logging.StreamHandler\',
        }
    },
    \'loggers\': {
        \'\': {  # default日志,存放于log中
            \'handlers\': [\'default\'],
            \'level\': \'DEBUG\',
        },
        \'django_auth_ldap\': {  # django_auth_ldap模块相关日志打印到console
            \'handlers\': [\'console\'],
            \'level\': \'DEBUG\',
            \'propagate\': False,  # 选择关闭继承,不然这个logger继承自默认,日志就会被记录2次了(\'\'一次,自己一次)
        },
        # \'django.db.backends\': {  # 数据库相关执行过程log打印到console
        #     \'handlers\': [\'console\'],
        #     \'level\': \'DEBUG\',
        # },
    }
}

# ### log 配置部分END ### #

logging模块的说明,可以参考这篇文章 http://www.jianshu.com/p/d615bf01e37b ,这个配置可以配置到settings.py中,这样django项目都会自动调用,或者单独写入一个log_config文件中,需要时手动调用,例子如下
log_config.py

import logging
from logging.config import dictConfig
logging_config={
........
}
dictConfig(logging_config)    #注册一下配置

调用方法:

import logging
logger = logging.getLogger()
logger.error(\'ldap conn失败,原因为: %s\' % str(e))

额外说一点,以上配置是在sso系统进行,如果下游系统接入到sso系统的话,顺序就是ldap–> sso –> 项目A,默认接入后,项目A的前台账户就可以使用ldap进行管理,但项目A如果使用了django admin的话(因为前台的登录动作会强制转到sso上,而admin如果不改源码做不到这点),django admin是会走本地的账户的,这样就会出现,一个下游系统的管理员用户,前台一个密码,后台一个密码,所以我们需要给下游系统的settings.py做一定配置,让下游的admin账户也从ldap中同步,下游同样需要开篇说的3个python模块
项目A的settings.py

# ### ldap 配置部分BEGIN (If use Django-admin,configure it!) ### #
\'\'\'just for admin,前台认证统一走认证平台,所以下游系统不保存密码,导致下游无法登录admin(如使用),所以admin添加ldap认证,只用ldap只读账户即可\'\'\'
import ldap
from django_auth_ldap.config import LDAPSearch, PosixGroupType

AUTHENTICATION_BACKENDS = (
    \'django_auth_ldap.backend.LDAPBackend\',  # 配置为先使用LDAP认证,如通过认证则不再使用后面的认证方式
    \'django.contrib.auth.backends.ModelBackend\',  # 同时打开本地认证,因为下游系统的权限和组关系需要用到
)

base_dn = \'dc=ldap,dc=ssotest,dc=net\'
AUTH_LDAP_SERVER_URI = \'ldap://ldap.ssotest.net\'
AUTH_LDAP_BIND_DN = \'uid=ssoread,ou=People,dc=ldap,dc=ssotest,dc=net\'  # read only ldap user
AUTH_LDAP_BIND_PASSWORD = \'ssotest@123\'
AUTH_LDAP_USER_SEARCH = LDAPSearch(\'ou=People,%s\' % base_dn, ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
AUTH_LDAP_ALWAYS_UPDATE_USER = False  # Default is True,是否登录后从ldap同步用户,不进行同步,因为下游的用户表是什么样的不能确定,只能确定它也使用邮箱前缀
# 下游系统不从ldap同步group staff/superuser相关,但需要从ldap验证用户是否离职
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(\'ou=Group,dc=ldap,dc=ssotest,dc=net\', ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)")
AUTH_LDAP_GROUP_TYPE = PosixGroupType(name_attr="cn")
AUTH_LDAP_REQUIRE_GROUP = u"cn=员工,ou=Group,dc=ldap,dc=ssotest,dc=net"
AUTH_LDAP_DENY_GROUP = u"cn=黑名单,ou=Group,dc=ldap,dc=ssotest,dc=net"
AUTH_LDAP_FIND_GROUP_PERMS = True  # django从ldap的组权限中获取权限,这种方式,django自身不创建组,每次请求都调用ldap,下游子系统,我们并不需要让他同步ldap里的"员工","管理员"这种表,所以不用mirror_groups
AUTH_LDAP_CACHE_GROUPS = True  # 如打开FIND_GROUP_PERMS后,才生效,对组关系进行缓存,不用每次请求都调用ldap
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 600
# ### ldap 配置部分END ### #

以上,我们通过配置,就可以完成django对于ldap的调用了,我们可以通过ldap来管理我们的前台和admin中的账户和密码,但是这个动作是向下进行的,如果想通过我们的单点登录系统来操作ldap,这个django-auth-ldap是没有这个功能的。这就需要后边的python-ldap了.

篇幅限制,python-ldap请参考我的这篇文章[原创]django+ldap实现统一认证部分二(python-ldap实践)

参考资料

https://pythonhosted.org/django-auth-ldap/authentication.html
https://darkcooking.gitbooks.io/django-auth-ldap/content/chapter9.html
https://docs.djangoproject.com/en/1.10/topics/http/sessions/
http://www.jianshu.com/p/d615bf01e37b

版权声明:本文为caseast原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/caseast/p/6090742.html