前言
接之前我的文章,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