django绑定keycloak单点登录
此django为archery数据库管理后台
pip install django-allauth安装模块,keycloak.py与settings.py放在同一目录一下了。
keycloak.py
from allauth.account.utils import perform_login from django.contrib.auth.models import User from sql.models import Users from allauth.socialaccount.adapter import DefaultSocialAccountAdapter import logging from allauth.socialaccount.models import SocialAccount logger = logging.getLogger('default') class AccountAdapter(DefaultSocialAccountAdapter): def populate_user(self, request, sociallogin, data): sociallogin.account.extra_data.update({'email_verified': True}) #logger.info("account adapter: populate user {}, {}, {}".format(sociallogin, data, dir(sociallogin))) return super(AccountAdapter, self).populate_user(request, sociallogin, data) def pre_social_login(self, request, sociallogin): username = sociallogin.account.extra_data.get('preferred_username', '') if username and username.strip() != '': users = Users.objects.filter(username = username) logger.info("-----{}".format(sociallogin.account.extra_data)) if users and users.count() != 0: bool = SocialAccount.objects.filter(user_id=users[0].id) if bool: perform_login(request, users[0], 'none') else: socialaccount=SocialAccount(user_id=users[0].id, provider="keycloak", uid=sociallogin.account.extra_data.get('id'), extra_data=sociallogin.account.extra_data) socialaccount.save() else: super(AccountAdapter, self).pre_social_login(request, sociallogin)
settings.py
# -*- coding: UTF-8 -*- # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'hfusaf2m4ot#7)fkw#di2bu6(cv0@opwmafx5n#6=3d%x^hpl6' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False ALLOWED_HOSTS = ['*'] # 解决nginx部署跳转404 USE_X_FORWARDED_HOST = True # 请求限制 DATA_UPLOAD_MAX_MEMORY_SIZE = 15728640 # Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django_q', 'sql', 'sql_api', 'common', 'django.contrib.sites', 'allauth', 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.keycloak', ) SITE_ID = 2 #数据库中可以查一下站点id是什么 AUTHENTICATION_BACKENDS = [ # Needed to login by username in Django admin, regardless of allauth 'django.contrib.auth.backends.ModelBackend', # allauth specific authentication methods, such as login by e-mail 'allauth.account.auth_backends.AuthenticationBackend', ] MIDDLEWARE = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'common.middleware.check_login_middleware.CheckLoginMiddleware', 'common.middleware.exception_logging_middleware.ExceptionLoggingMiddleware', ) ROOT_URLCONF = 'archery.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'common/templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'common.utils.global_info.global_info', ], }, }, ] WSGI_APPLICATION = 'archery.wsgi.application' # Internationalization LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_TZ = False # 时间格式化 USE_L10N = False DATETIME_FORMAT = 'Y-m-d H:i:s' DATE_FORMAT = 'Y-m-d' # Static files (CSS, JavaScript, Images) STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATICFILES_DIRS = [os.path.join(BASE_DIR, 'common/static'), ] STATICFILES_STORAGE = 'common.storage.ForgivingManifestStaticFilesStorage' # 扩展django admin里users字段用到,指定了sql/models.py里的class users AUTH_USER_MODEL = "sql.users" # 密码校验 AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': { 'min_length': 9, } }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] ###############以下部分需要用户根据自己环境自行修改################### # session 设置 SESSION_COOKIE_AGE = 60 * 300 # 300分钟 SESSION_SAVE_EVERY_REQUEST = True SESSION_EXPIRE_AT_BROWSER_CLOSE = True # 关闭浏览器,则COOKIE失效 # 该项目本身的mysql数据库地址 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'archery', 'USER': 'root', 'PASSWORD': '123456', 'HOST': 'mysql', 'PORT': '3306', 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", 'charset': 'utf8mb4' }, 'TEST': { 'NAME': 'test_archery', 'CHARSET': 'utf8mb4', }, } } # Django-Q Q_CLUSTER = { 'name': 'archery', 'workers': 4, 'recycle': 500, 'timeout': 60, 'compress': True, 'cpu_affinity': 1, 'save_limit': 0, 'queue_limit': 50, 'label': 'Django Q', 'django_redis': 'default', 'sync': False # 本地调试可以修改为True,使用同步模式 } # 缓存配置 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://redis:6379/0", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD": "123456" } }, "dingding": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://redis:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD": "123456" } } } # LDAP ENABLE_LDAP = False if ENABLE_LDAP: import ldap from django_auth_ldap.config import LDAPSearch AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', # 配置为先使用LDAP认证,如通过认证则不再使用后面的认证方式 'django.contrib.auth.backends.ModelBackend', # django系统中手动创建的用户也可使用,优先级靠后。注意这2行的顺序 ) AUTH_LDAP_SERVER_URI = "ldap://xxx" AUTH_LDAP_USER_DN_TEMPLATE = "cn=%(user)s,ou=xxx,dc=xxx,dc=xxx" AUTH_LDAP_ALWAYS_UPDATE_USER = True # 每次登录从ldap同步用户信息 AUTH_LDAP_USER_ATTR_MAP = { # key为archery.sql_users字段名,value为ldap中字段名,用户同步信息 "username": "cn", "display": "displayname", "email": "mail" } # LOG配置 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s]- %(message)s' }, }, 'handlers': { 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', 'filename': 'logs/archery.log', 'maxBytes': 1024 * 1024 * 100, # 5 MB 'backupCount': 5, 'formatter': 'verbose', }, 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'verbose' } }, 'loggers': { 'default': { # default日志 'handlers': ['console', 'default'], 'level': 'WARNING' }, 'django-q': { # django_q模块相关日志 'handlers': ['console', 'default'], 'level': 'WARNING', 'propagate': False }, 'django_auth_ldap': { # django_auth_ldap模块相关日志 'handlers': ['console', 'default'], 'level': 'WARNING', 'propagate': False }, 'django.db': { # 打印SQL语句,方便开发 'handlers': ['console', 'default'], 'level': 'DEBUG', 'propagate': False }, 'django.request': { # 打印请求错误堆栈信息,方便开发 'handlers': ['console', 'default'], 'level': 'DEBUG', 'propagate': False }, } } LOGIN_REDIRECT_URL = "/" SOCIALACCOUNT_PROVIDERS = { 'keycloak': { 'KEYCLOAK_URL': 'http://keycloak/auth', 'KEYCLOAK_REALM': 'mae' } } ACCOUNT_SESSION_REMEMBER=True #ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS=True #SOCIALACCOUNT_AUTO_SIGNUP=True SOCIALACCOUNT_ADAPTER = 'archery.keycloak.AccountAdapter'
参考url:https://number1.co.za/using-keycloak-as-the-identity-provider-for-users-on-django-and-django-admin/