75. Django 集成 CAS 实现 SSO 单点登陆

Devops海洋的渔夫 2020-11-17 13:58:42
django CAS SSO 实现 集成


需求

在公司平台的开发中,由于内部平台越来越多,本次要求我们开发的平台需要同步公司的 OA 账号。

那么怎么同步呢?简单来说就是采用 CAS 服务机制,实现 CAS 服务完成多应用单点登陆 功能。

image-20200909110936463

在了解 CAS 单点登陆之前,先来回顾一下 Django 默认的 Session + Cookie 的登陆机制:

  1. 浏览器发送登陆请求 至 Django 服务
  2. Django 服务接收到 浏览器发送过来的请求之后,则创建 CSRFToken 以及 相关用户信息,存储到 Session 中,并且返回浏览器 Set-Cookie 的信息,通知浏览器设置相关 Cookie
  3. 浏览器再次发送请求 至 Django 服务,则会携带前面设置的 Cookie 信息
  4. Django 服务接收到 浏览器发送过来的请求之后,发现携带了 CSRFToken 以及 记录用户信息的 sessionID,根据 sessionID 查询服务器上的 session 数据。

下面再来看看 CAS 的单点登陆机制

CAS 的 (Single Sign-On)SSO单点登陆机制

首先先不看 CAS 的一堆概念,我们直接上请求时序图,了解请求 CAS 对于服务登陆认证的过程先。

CAS 登陆服务请求时序图

cas登陆机制-CAS服务登陆机制

从上面的时序图来看,可以清晰知道 CAS 服务就是用来统一管理 APP 服务登陆认证的 独立服务。在时序图我写了 16 个处理步骤,在这16 个处理步骤中,可以知道,APP 服务 与 CAS 服务验证登陆是否通过是基于 服务票据 ST 来确认的。

基本认证过程简略如下:

  • 前端访问 APP 服务的一个页面, 此时未携带相关登陆参数。
  • 后端发现该请求未登陆,则返回前端 302 ,并 重定向到 CAS 服务器的登录页面,并携带当前用户访问的网页链接
  • 在CAS 服务器上,用户填写登录信息,浏览器发送请求到 CAS 服务器进行认证
  • CAS 服务 认证通过,将本次登录保存到会话,返回 服务票据 ST重定向 浏览器至 APP 服务
  • APP服务接收前端重定向请求过来路径 以及 服务票据 ST ,APP服务 再将 服务票据 ST 请求至 CAS 服务,验证 ST。验证通过,则创建该用户给登陆成功的 session 数据;反之,返回 前端 302, 重定向至 CAS 登陆页面。
  • APP 服务验证 ST 通过之后,返回 前端 登陆页面的 页面内容。

在清楚了 CAS 登陆服务请求的机制之后,我们来开始搭设服务,搭设一个完整的 CAS 服务。

CAS 示例服务

image-20200909165844507

说明:本次示例服务代码分别创建一个 CAS 服务端的 项目,再创建一个 CAS 客户端的 项目,通过两个项目来实现完整的 CAS 服务登陆机制。

相关使用库的 Github 地址

  • https://github.com/jbittel/django-mama-cas
  • https://django-mama-cas.readthedocs.io/en/latest/
  • https://github.com/django-cas-ng/django-cas-ng

CAS 服务端项目

安装Django

$ pip install Django==2.1.7

因为目前线上运行的是 2.1.7 的版本,还没有改用 3.x 系列版本,所以本次使用 2.1.7 的版本进行演示。

创建Django项目

$ django-admin startproject django_cas_server .

image-20200909173638238

测试启动Django项目

$ python manage.py runserver

image-20200909173737679

访问页面如下:

image-20200909173752193

停止服务,开始安装 django-mama-cas 库。

安装 django-mama-cas

$ pip install django-mama-cas

配置 settings,安装 mama-cas 应用

INSTALLED_APPS = [
'mama_cas', # 安装 mama_cas 应用
...
]

image-20200909171302285

配置 url,设置访问 cas 服务的路由

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('cas/', include('mama_cas.urls')), # 导入mama_cas应用的urls.py
path('admin/', admin.site.urls),
]

image-20200909171604589

在settings 配置 CAS 回调:

官网示例配置:

MAMA_CAS_SERVICES = [
{
'SERVICE': '^https://[^\.]+\.example\.com',
'CALLBACKS': [
'mama_cas.callbacks.user_name_attributes',
],
'LOGOUT_ALLOW': True,
'LOGOUT_URL': 'https://www.example.com/logout',
'PROXY_ALLOW': True,
'PROXY_PATTERN': '^https://proxy\.example\.com',
}
]

本次项目配置:

# 配置CAS
MAMA_CAS_SERVICES = [
{
# 必填项,客户端允许访问的域名
'SERVICE': 'http://127.0.0.1:8000',
# 回调模式,具体参考官方文档
'CALLBACKS': [
'mama_cas.callbacks.user_model_attributes',
],
},
]

image-20200909200755361

初始化表

$ python manage.py migrate

启动服务

$ python manage.py runserver 0.0.0.0:3000

在这里我不占用 8000 端口号,开启为 3000 端口号作为 cas 服务。

访问CAS登陆页面

访问 http://127.0.0.1:3000/cas/login

image-20200909201733073

那么账号、密码应该填写什么呢?

其实这个取决于Django的 User 表已经存储注册以及激活了的用户。在这里,我们就创建一个 admin 的 超级用户,作为 CAS 的用户。

创建超级用户

$ python manage.py createsuperuser
Username (leave blank to use 'lijw'): casuser01
Email address:
Password:
Password (again):
This password is too short. It must contain at least 8 characters.
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

登陆CAS服务

image-20200909202310620

提示已经登陆成功,要注意,这里没有其他配置,所以不会跳至其他的页面。只是在上面提示已经登陆成功!

CAS 的 测试用户:casuser01 密码:123456

如果登陆失败,则会提示如下:

image-20200914110114011

CAS 客户端项目

下面首先写一个项目,然后再接入 CAS 服务。

准备好客户端项目

首先准备好一个简单的客户端项目来进行演示,首先具备以下三个视图功能:

  • 注册:用来新增用户
  • 登陆:登陆项目新增的用户
  • 首页:用来演示登陆成功之后的视图页面。

注册页面

http://127.0.0.1:8000/register

image-20200914135239661

这个页面我只实现了最基础填写信息,然后点击注册按钮进行注册的功能,注册成功的话则自动跳转至登陆页面。

登陆页面

http://127.0.0.1:8000/login

image-20200914135857849

在登陆页面,我提供了填写用户、密码以及验证码,然后点击登录按钮的功能。

这里我自己注册的一个 测试用户为: testuser01 密码:123456

要注意:这个用户是在这个项目中注册的数据,后续对接 CAS ,要用的是 CAS 项目的用户。 ”

登陆成功之后,则跳转至 index 页面如下:

image-20200914135935427

安装 CAS 的 Client 库

在 python 中对于 cas 的 client 客户端功能有不少开源库。例如:

  • python-cas:https://github.com/python-cas/python-cas
  • django-cas-ng: https://github.com/django-cas-ng/django-cas-ng

因为我的项目采用的是 django 框架,所以安装 django-cas-ng 即可。

django-cas-ng 的安装文档:https://djangocas.dev/docs/latest/install.html

image-20200914141205269

使用 pip 安装:

pip install django-cas-ng

配置项目使用 CAS 的客户端

在项目的配置文件 settings.py 添加以下配置。

参考官网的配置文档:https://djangocas.dev/docs/latest/configuration.html

image-20200914141452552

配置INSTALLED_APPS, 安装CAS应用

INSTALLED_APPS = [
'user.apps.UserConfig', # 注册user应用
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_cas_ng', # 安装cas客户端应用
]

配置 MIDDLEWARE_CLASSES,设置CAS客户端的中间件类

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_cas_ng.middleware.CASMiddleware', # 设置cas客户端的中间件类
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

配置AUTHENTICATION_BACKENDS ,指定认证授权的后端

# 指定授权认证的后端
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'django_cas_ng.backends.CASBackend',
)

配置准备接入的 CAS 服务地址和版本,添加几个对应的配置:

# CAS 服务的访问地址
CAS_SERVER_URL = 'http://127.0.0.1:3000/cas/'
# CAS 版本
CAS_VERSION = '3'
# 存入所有 CAS 服务端返回的 User 数据。
CAS_APPLY_ATTRIBUTES_TO_USER = True

配置 CAS客户端 访问 CAS服务的视图页面 URL

官网的示例配置:

# Django 2.0+
from django.urls import path
import django_cas_ng.views
urlpatterns = [
# ...
path('accounts/login', django_cas_ng.views.LoginView.as_view(), name='cas_ng_login'),
path('accounts/logout', django_cas_ng.views.LogoutView.as_view(), name='cas_ng_logout'),
]

配置项目的路由 urls.py 如下:

from django.contrib import admin
from django.urls import path, include
import django_cas_ng.views # 导入cas的登陆视图
urlpatterns = [
# path('user/', include('user.urls')), # 导入user应用的urls.py
path('', include('user.urls')), # 导入user应用的urls.py
path('cas/login', django_cas_ng.views.LoginView.as_view(), name='cas_ng_login'), # 访问cas服务的登陆
path('cas/logout', django_cas_ng.views.LogoutView.as_view(), name='cas_ng_logout'), # 访问cas服务的登出
path('admin/', admin.site.urls),
]

说明:也就是说配置了这两个路径之后,具体操作过程如下:

  • 访问客户端服务:http://127.0.0.1:8000/cas/login 判断如果未登陆服务,则自动重定向至 后台配置的 CAS 服务 http://127.0.0.1:3000/cas/login ,然后在 cas 服务器上登陆成功之后,重新重定向回客户端服务。
  • 访问客户端服务:http://127.0.0.1:8000/cas/logout,则自动重定向至 后台配置的 CAS 服务 http://127.0.0.1:3000/cas/logout,则注销退出用户

初始化 django_cas_ng 的相关数据表

You have 1 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): django_cas_ng.
Run 'python manage.py migrate' to apply them.
$ python manage.py migrate

启动客户端的服务

$ python manage.py runserver

测试客户端访问 CAS 服务

  • 1.访问 http://127.0.0.1:8000/cas/login ,登陆用户

image-20200914162201532

自动重定向至 CAS 服务如下:

image-20200914162617249

登陆成功之后,返回客户端的服务如下:

image-20200914162641249

  • 2.访问 http://127.0.0.1:8000/cas/logout 退出登陆状态

访问之后,自动重定向至未登录状态:

image-20200914162842100

总结

  • 1.成功访问CAS服务,登陆用户之后,通过配置,可以自动将用户同步在客户端项目的用户数据中

通过在 settings.py 配置自动同步用户数据:

# 存入所有 CAS 服务端返回的 User 数据。
CAS_APPLY_ATTRIBUTES_TO_USER = True

登陆成功之后,可以查询到登陆成功的用户数据,如下:

image-20200914163317309

  • 2.同步CAS的用户的其他字段根据默认值设置,例如:角色按照默认设置

首先确认一下,我定义用户模型类的角色字段默认值,如下:

image-20200914163639274

查询CAS同步用户 的 角色数据:

In [13]: User.objects.get(username="casuser01").role
Out[13]: 0
In [14]: User.objects.get(username="casuser01").get_role_display()
Out[14]: '组员'
  • 3.可以保留两个登陆页面

因为 客户端项目的登陆CAS服务的登陆 是通过不同的 url 访问的,并且都可以设置登陆的状态。

也就是说,我可以在一个页面中设置不同的登陆访问,如下:

image-20200914165552564

点击CAS登陆,显示如下:

image-20200914165617366

image-20200914165737365

  • 4.在项目的登陆视图,增加用户的登陆状态判断,如果已登陆,则直接重定向至首页

image-20200914170313767

 def get(self, request):
# get请求返回登录页面
# 判断用户是否已登陆
# 获取当前的用户
user = request.user # 获取当前的用户
# 判断用户是否已登陆
if user.is_authenticated: # 用户已登陆, 则跳至首页
return redirect('user:index')
# 用户未登陆,则进入登陆页
return render(request, "user/login.html")
  • 5.登陆、用户数据、RBAC的方案策略

从上面的尝试过程中,可以确认 客户端项目 是可以保留 两种登陆用户的 方式的,并且两种方式的用户数据都会保存在 客户端项目中。

而同步过来的用户则会采用默认的角色字段,所以在配置RBAC的时候,直接根据默认角色配置可以显示的菜单即可。

其实也就是在做 RBAC 功能开发 并不受 CAS 用户的影响,CAS 用户只是增加了一种登陆的方式而已。

  • 6.客户端采用 http 服务,可以配置 https 的 CAS 服务

在一开始我还担心 http 的客户端服务能否 对接 https 的CAS 服务,其实是可以的。

演示项目的仓库地址

  • CAS 服务端演示项目:https://gitee.com/kubernete/django_cas_server
  • CAS 客户端演示项目:https://gitee.com/kubernete/django_cas_client

本文分享自微信公众号 - 海洋的渔夫(DevOpsFreshMan) ,作者:Devops海洋的渔夫

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间: 2020-11-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

版权声明
本文为[Devops海洋的渔夫]所创,转载请带上原文链接,感谢
https://cloud.tencent.com/developer/article/1748513

  1. 利用Python爬虫获取招聘网站职位信息
  2. Using Python crawler to obtain job information of recruitment website
  3. Several highly rated Python libraries arrow, jsonpath, psutil and tenacity are recommended
  4. Python装饰器
  5. Python实现LDAP认证
  6. Python decorator
  7. Implementing LDAP authentication with Python
  8. Vscode configures Python development environment!
  9. In Python, how dare you say you can't log module? ️
  10. 我收藏的有关Python的电子书和资料
  11. python 中 lambda的一些tips
  12. python中字典的一些tips
  13. python 用生成器生成斐波那契数列
  14. python脚本转pyc踩了个坑。。。
  15. My collection of e-books and materials about Python
  16. Some tips of lambda in Python
  17. Some tips of dictionary in Python
  18. Using Python generator to generate Fibonacci sequence
  19. The conversion of Python script to PyC stepped on a pit...
  20. Python游戏开发,pygame模块,Python实现扫雷小游戏
  21. Python game development, pyGame module, python implementation of minesweeping games
  22. Python实用工具,email模块,Python实现邮件远程控制自己电脑
  23. Python utility, email module, python realizes mail remote control of its own computer
  24. 毫无头绪的自学Python,你可能连门槛都摸不到!【最佳学习路线】
  25. Python读取二进制文件代码方法解析
  26. Python字典的实现原理
  27. Without a clue, you may not even touch the threshold【 Best learning route]
  28. Parsing method of Python reading binary file code
  29. Implementation principle of Python dictionary
  30. You must know the function of pandas to parse JSON data - JSON_ normalize()
  31. Python实用案例,私人定制,Python自动化生成爱豆专属2021日历
  32. Python practical case, private customization, python automatic generation of Adu exclusive 2021 calendar
  33. 《Python实例》震惊了,用Python这么简单实现了聊天系统的脏话,广告检测
  34. "Python instance" was shocked and realized the dirty words and advertisement detection of the chat system in Python
  35. Convolutional neural network processing sequence for Python deep learning
  36. Python data structure and algorithm (1) -- enum type enum
  37. 超全大厂算法岗百问百答(推荐系统/机器学习/深度学习/C++/Spark/python)
  38. 【Python进阶】你真的明白NumPy中的ndarray吗?
  39. All questions and answers for algorithm posts of super large factories (recommended system / machine learning / deep learning / C + + / spark / Python)
  40. [advanced Python] do you really understand ndarray in numpy?
  41. 【Python进阶】Python进阶专栏栏主自述:不忘初心,砥砺前行
  42. [advanced Python] Python advanced column main readme: never forget the original intention and forge ahead
  43. python垃圾回收和缓存管理
  44. java调用Python程序
  45. java调用Python程序
  46. Python常用函数有哪些?Python基础入门课程
  47. Python garbage collection and cache management
  48. Java calling Python program
  49. Java calling Python program
  50. What functions are commonly used in Python? Introduction to Python Basics
  51. Python basic knowledge
  52. Anaconda5.2 安装 Python 库(MySQLdb)的方法
  53. Python实现对脑电数据情绪分析
  54. Anaconda 5.2 method of installing Python Library (mysqldb)
  55. Python implements emotion analysis of EEG data
  56. Master some advanced usage of Python in 30 seconds, which makes others envy it
  57. python爬取百度图片并对图片做一系列处理
  58. Python crawls Baidu pictures and does a series of processing on them
  59. python链接mysql数据库
  60. Python link MySQL database