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个:SSO1和SSO2。
创建好两个项目后,我们要给项目写一个模拟的登录,注销的功能。在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
然后我们在两个项目的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("user:{},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),
]
在settings.py中注释掉csrf相关的配置
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',
]
然后分别为两个项目做数据库迁移,创建一些Django项目的基础库:python3 manage.py migrate两个项目都是同样的配置,这样我们目前两个测试的项目就搭建好了,然后我们分别启动他们在不同的端口。这里我们就直接手动启动了,分别启动在5000和7000端口。
启动两个项目:
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/随意输入账户密码点击登录,显示:
user:123,password:123,sessionid:None,cookie:{'csrftoken': '8YPzJbY03sHJUZH6kdFZzr9TkDtdVTKflgDDeIn0wgGC6cAeudcrkXLyIxXBEnzG'}
此时我们进入http://127.0.0.1:7000/login/,发现这个应用中,显示的还是之前的页面,登录没有同步。下面我们来实现我们的SSO,这里的实现方法非常的简单:将session存储在同一个Redis中
pip3 install django-redis在做好这些之后,修改settings.py文件:
# 使用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/,输入账户密码后显示:
user:123,password:123,sessionid:None,cookie:{'csrftoken': '8YPzJbY03sHJUZH6kdFZzr9TkDtdVTKflgDDeIn0wgGC6cAeudcrkXLyIxXBEnzG'}
此时我们访问http://127.0.0.1:7000/login/(不登录),显示同样的user和password信息。
此时我们的SSO也可以正常实现。