小白都能看得懂的教程之 Django 實作單點登錄(SSO)
??大家好,我叫亓官劼(qí guān jié ),在CSDN中記錄學習的點滴歷程,時光荏苒,未來可期,加油~博主目前僅在CSDN中寫博客,唯一博客更新的地址為:亓官劼的博客 ,同時正在嘗試在B站中做一些內容分享,B站主頁為: 亓官劼的B站主頁
本文原創為亓官劼,請大家支持原創,部分平臺一直在惡意盜取博主的文章!!!
若需聯系博主,可以聯系本人微信:qiguanjie2015
SSO簡介
單點登錄(Single Sign On)功能是一個非常常用的功能,尤其是我們在多個系統之間需要登錄同步的時候,例如我們在登錄QQ空間后,再去QQ的其他網站,都是默認登錄的狀態,這就是單點登錄,
單點登錄有很多種實作方法,這里介紹一個通過共享session的實作方法,實作共享session要做的就是要讓多個不同應用共用同一個session,但是session默認的是每個應用一個獨立的session和cookie的,所以這里要對session的存盤進行配置,
除了默認的session存盤,我也可以設定讓session存盤在檔案、快取或者資料庫中,
如果我們讓session存盤在一個固定位置或者資料庫中,然后我們設定各個應用cookie的domain為父域地址即可實作各個cookie的相同,從而時候各個cookie中存盤的sessionID一致,
搭建測驗環境
下面我們來創建兩個空的Django專案來進行演示,SSO1和SSO2,這里采用pycharm直接創建兩個Django專案,也可以在命令列中使用django-admin startproject sso來創建,其中sso是創建的專案名稱,這里也可以使用兩個完全相同的專案,在不同地址啟動,但是為了演示效果,這里創建了2個,

創建好兩個專案后,我們要給專案寫一個模擬的登錄,注銷的功能,
在templates檔案夾下創建檔案login.html檔案,這里直接使用之前寫過的登錄頁面的代碼,樣式就不加了,在SSO1和SSO2中都加入login.html,具體代碼為:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="login_content">
<div class="page-header" id="page_header">
<h1>登錄<small>Login</small></h1>
</div>
<div id="login_form">
<form method="post">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="input" class="form-control" name="usr" id="exampleInputEmail1" placeholder="username">
</div>
<div class="form-group">
<label for="exampleInputPassword1">密碼</label>
<input type="password" class="form-control" name="password" id="exampleInputPassword1" placeholder="密碼">
</div>
<div id="login_butt">
<button type="submit" class="btn btn-default">登錄</button>
<button type="button" class="btn btn-default" onclick="">注冊</button>
</div>
</form>
</div>
</div>
</body>
</html>
然后在SSO1檔案夾創建一個view.py檔案,用來存放視圖函式,(這里僅為演示SSO,就不分模塊了,)
創建檔案后的檔案目錄為:(SSO2專案一樣)
.
├── SSO1
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ ├── view.py
│ └── wsgi.py
├── manage.py
├── templates
│ └── login.html
└── venv
├── bin
├── include
├── lib
└── pyvenv.cfg
插入一個小BUG
macbook運行環境,pycharm創建的Django應用有時候初始化有個bug,缺少os庫,會報錯:
Traceback (most recent call last):
File "/Users/qiguan/Documents/develop_files/python_files/SSO1/manage.py", line 22, in <module>
main()
File "/Users/qiguan/Documents/develop_files/python_files/SSO1/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
utility.execute()
File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/core/management/__init__.py", line 345, in execute
settings.INSTALLED_APPS
File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/conf/__init__.py", line 82, in __getattr__
self._setup(name)
File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/conf/__init__.py", line 69, in _setup
self._wrapped = Settings(settings_module)
File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/conf/__init__.py", line 170, in __init__
mod = importlib.import_module(self.SETTINGS_MODULE)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/Users/qiguan/Documents/develop_files/python_files/SSO1/SSO1/settings.py", line 57, in <module>
'DIRS': [os.path.join(BASE_DIR, 'templates')]
NameError: name 'os' is not defined
如果有這個報錯的話,在setting.py中匯入os即可:import os
然后我們在兩個專案的view.py中寫入登錄和注銷函式:
from django.http import HttpResponse
from django.shortcuts import render, redirect
def login(request):
if request.method == 'GET':
if 'usr' in request.session:
# 如果session中已有資訊,則顯示
usr = request.session['usr']
password = request.session['password']
return HttpResponse("usr:{},password:{},sessionid:{},cookie:{}".format(usr,password,request.session.session_key,request.COOKIES))
return render(request,'login.html')
if request.method == 'POST':
usr = request.POST['usr']
password = request.POST['password']
request.session['usr'] = usr
request.session['password'] = password
return HttpResponse(
"usr:{},password:{},sessionid:{},cookie:{}".format(usr, password, request.session.session_key,
request.COOKIES))
def logout(request):
request.session.clear()
return redirect('/login')
在url.py中添加路由資訊:
"""SSO1 URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from . import view
urlpatterns = [
path('admin/', admin.site.urls),
path('login/',view.login),
path('logout/',view.logout),
]
Django默認配置了csrf,需要將它注釋掉,在settings.py檔案中搜csrf,然后注釋掉,
修改后的settings.py檔案為:
"""
Django settings for SSO1 project.
Generated by 'django-admin startproject' using Django 3.1.7.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'o=blc^vzeb1&g*b!si(wtxe44_=i5cv(3jqm2*u2u&7vgj%&=%'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'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',
]
ROOT_URLCONF = 'SSO1.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, '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',
],
},
},
]
WSGI_APPLICATION = 'SSO1.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/'
然后分別為兩個專案做資料庫遷移,創建一些Django專案的基礎庫:
python3 manage.py migrate
兩個專案都是同樣的配置,這樣我們目前兩個測驗的專案就搭建好了,然后我們分別啟動他們在不同的埠,這里我們就直接手動啟動了,分別啟動在5000和6000埠,
python3 manage.py runserver 127.0.0.1:5000
python3 manage.py runserver 127.0.0.1:7000
啟動兩個專案:


現在我們分別在瀏覽器中打開http://127.0.0.1:5000/login/和http://127.0.0.1:7000/login/,顯示的頁面都是登錄頁面,顯示如下:

這時我們在http://127.0.0.1:5000/login/隨意輸入賬戶密碼點擊登錄,顯示:
usr:123,password:123,sessionid:None,cookie:{'csrftoken': '8YPzJbY03sHJUZH6kdFZzr9TkDtdVTKflgDDeIn0wgGC6cAeudcrkXLyIxXBEnzG'}
此時我們進入http://127.0.0.1:7000/login/,發現這個應用中,顯示的還是之前的頁面,登錄沒有同步,下面我們來實作我們的SSO,這里的實作方法非常的簡單,這里提供2中實作方法:1.將session固定存盤在同一個檔案中,2.將session存盤在Redis中
將session存盤在同一個檔案中實作SSO
我們在SSO2檔案下創建了一個session檔案夾,這個檔案夾位置任意,寫絕對路徑即可,
然后我們在兩個專案的settings.py中對cookie和session進行配置,
# 設定cookie的domain為父域domain,
# 如果是使用域名,以百度為例,主域名為`www.baidu.com`,旗下各個應用為:'asd.baidu.com'
# 則這里設定為:`.baidu.com`
SESSION_COOKIE_DOMAIN = '127.0.0.1'
# 設定session存盤在檔案中
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
# 設定存盤位置,這里設為絕對路徑
SESSION_FILE_PATH = '/Users/qiguan/Documents/develop_files/python_files/SSO2/session'
注意一下,這里配置的都是一樣的,但是如果兩個專案名稱不一樣的話,是不能直接將完整的settings.py直接復制到另一個的,因為里面有一些專案的配置,例如ROOT_URLCONF = 'SSO1.urls'、WSGI_APPLICATION = 'SSO1.wsgi.application'這些前面的都是專案名,需要主要區分,
此時我們在打開http://127.0.0.1:5000/login/,輸入賬號密碼,此頁面顯示:
usr:123,password:123,sessionid:2bs2nx2iq879epxu7au7o1zq63o095v7,cookie:{'sessionid': '2bs2nx2iq879epxu7au7o1zq63o095v7', 'csrftoken': '8YPzJbY03sHJUZH6kdFZzr9TkDtdVTKflgDDeIn0wgGC6cAeudcrkXLyIxXBEnzG'}
此時我們在打開http://127.0.0.1:7000/login/,我們直接訪問,而不用登錄,發現顯示同樣的內容,即我們使用的是同樣的內容,實作了SSO,
使用Redis實作SSO
使用檔案系統上實作共享session在小并發系統上不會出現問題,但是并發量大的話,會出現一些問題,所以我們這里再介紹一下使用Redis的實作,
需要自行安裝Redis,并且在兩個專案使用的Python中安裝Django-redis:
pip3 install django-redis
在做好這些之后,修改settings.py檔案,將使用檔案存盤session的配置注釋掉,修改為:
# # 設定session存盤在檔案中
# SESSION_ENGINE = 'django.contrib.sessions.backends.file'
# # 設定存盤位置,這里設為絕對路徑
# SESSION_FILE_PATH = '/Users/qiguan/Documents/develop_files/python_files/SSO2/session'
# 使用Redis存盤session
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "123",
}
}
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
SESSION_COOKIE_AGE = 60 * 5
此時我們再來測驗一下兩個應用,這時我們先訪問一下logout,將session清空,然后訪問:http://127.0.0.1:5000/login/,輸入賬戶密碼后顯示:
usr:123,password:123,sessionid:None,cookie:{'csrftoken': '8YPzJbY03sHJUZH6kdFZzr9TkDtdVTKflgDDeIn0wgGC6cAeudcrkXLyIxXBEnzG'}
此時我們訪問http://127.0.0.1:7000/login/(不登錄),顯示同樣的usr和password資訊,
此時我們的SSO也可以正常實作,
好了,本文就先到這里,大家如有需要,可以根據具體的業務進行實作,這里就不贅述了,等以后有空再寫一些Django相關的開發博客,
??大家好,我叫亓官劼(qí guān jié ),在CSDN中記錄學習的點滴歷程,時光荏苒,未來可期,加油~博主目前僅在CSDN中寫博客,唯一博客更新的地址為:亓官劼的博客 ,同時正在嘗試在B站中做一些內容分享,B站主頁為: 亓官劼的B站主頁
本文原創為亓官劼,請大家支持原創,部分平臺一直在惡意盜取博主的文章!!!
若需聯系博主,可以聯系本人微信:qiguanjie2015
CSDN認證博客專家
Python
全堆疊
資料結構與演算法
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/267087.html
標籤:其他
