前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >使用dex搭建一个身份认证系统

使用dex搭建一个身份认证系统

作者头像
jeremyxu
发布于 2019-08-07 07:09:29
发布于 2019-08-07 07:09:29
2.9K00
代码可运行
举报
运行总次数:0
代码可运行

一个成熟的软件系统一般必须有一个可靠的身份认证与权限验证功能。这一块要自研快速实现还是需要花费挺多精力的,幸好开源领域目前已经有不错的解决方案,一般拿过来按项目的实际需求进行一些简单的定制基本就可以实现业务目标了。最近刚好在这方面进行了一些工作,这里将如何实现身份认证及权限验证分两篇博文大概梳理一下,这篇先讲身份认证。

什么是身份认证

身份验证(英语:Authentication)又称“验证”、“鉴权”,是指通过一定的手段,完成对用户身份的确认。 身份验证的目的是确认当前所声称为某种身份的用户,确实是所声称的用户。在日常生活中,身份验证并不罕见;比如,通过检查对方的证件,我们一般可以确信对方的身份。虽然日常生活中的这种确认对方身份的做法也属于广义的“身份验证”,但“身份验证”一词更多地被用在计算机、通信等领域。

以上是维基百科的解释,说白了就是用某种方式确保用户是某种身份,这种确保需要保证其它用户没那么容易伪装其身份。一般只有经过身份认证得到可靠的用户身份后,才能基于该身份进行后续的权限验证流程。

实现身份认证系统

一般来说业务系统会专注于业务逻辑的处理,而身份认证相关的功能会放入独立的身份认证系统进行开发维护。要纯自研地完成一套可靠、安全度高的身份认证系统还是比较花费精力的,幸好开源领域目前已经有不错的解决方案,一般拿过来按项目的实际需求进行一些简单的定制基本就可以实现业务目标了。我这里的示例使用dex实现了一个简单的用户认证系统

运行示例

前提是已经搭建好了go语言的开发环境,并设置好了GOPATH。

然后按以下步骤运行本程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 编译dexserver
$ make build-dexserver

# 编译dexclient
$ make build-dexclient

# 运行dexserver
$ make run-dexserver

# 运行dexclient
$ run-dexclient

然后用浏览器访问http://127.0.0.1:8080, 页面会自动跳转至dexserver的登录页面,输入用户名admin@example.com、密码password之后,会跳回dexclient的callback页面http://127.0.0.1:8080/callback

示例的技术细节

dexserver

这里使用的dexserver是由官方代码直接编译得出的,没有修改任何代码。只不过使用了自定义的配置文件dexserver-config.yaml,这里分析一下这个配置文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# The base path of dex and the external name of the OpenID Connect service.
# This is the canonical URL that all clients MUST use to refer to dex. If a
# path is provided, dex's HTTP service will listen at a non-root URL.
issuer: http://127.0.0.1:5556/dex

# The storage configuration determines where dex stores its state. Supported
# options include SQL flavors and Kubernetes third party resources.
#
# See the storage document at Documentation/storage.md for further information.
storage:
  type: sqlite3
  config:
    file: config/dex.db

# Configuration for the HTTP endpoints.
web:
  http: 0.0.0.0:5556
  # Uncomment for HTTPS options.
  # https: 127.0.0.1:5554
  # tlsCert: /etc/dex/tls.crt
  # tlsKey: /etc/dex/tls.key

# Configuration for telemetry
telemetry:
  http: 0.0.0.0:5558

# Uncomment this block to enable the gRPC API. This values MUST be different
# from the HTTP endpoints.
# grpc:
#   addr: 127.0.0.1:5557
#  tlsCert: examples/grpc-client/server.crt
#  tlsKey: examples/grpc-client/server.key
#  tlsClientCA: /etc/dex/client.crt

# Uncomment this block to enable configuration for the expiration time durations.
# expiry:
#   signingKeys: "6h"
#   idTokens: "24h"

# Options for controlling the logger.
# logger:
#   level: "debug"
#   format: "text" # can also be "json"

# Uncomment this block to control which response types dex supports. For example
# the following response types enable the implicit flow for web-only clients.
# Defaults to ["code"], the code flow.
# oauth2:
#   responseTypes: ["code", "token", "id_token"]

oauth2:
  skipApprovalScreen: true

# Instead of reading from an external storage, use this list of clients.
#
# If this option isn't chosen clients may be added through the gRPC API.
staticClients:
- id: demo-dexclient
  redirectURIs:
  - 'http://127.0.0.1:8080/callback'
  name: 'Demo dex client'
  secret: ZXhhbXBsZS1hcHAtc2VjcmV0

connectors: []
# - type: mockCallback
#   id: mock
#   name: Example
# - type: oidc
#   id: google
#   name: Google
#   config:
#     issuer: https://accounts.google.com
#     # Connector config values starting with a "$" will read from the environment.
#     clientID: $GOOGLE_CLIENT_ID
#     clientSecret: $GOOGLE_CLIENT_SECRET
#     redirectURI: http://127.0.0.1:5556/dex/callback
#     hostedDomains:
#     - $GOOGLE_HOSTED_DOMAIN

# Let dex keep a list of passwords which can be used to login to dex.
enablePasswordDB: true

# A static list of passwords to login the end user. By identifying here, dex
# won't look in its underlying storage for passwords.
#
# If this option isn't chosen users may be added through the gRPC API.
# staticPasswords: 
# - email: "admin@example.com"
#   # bcrypt hash of the string "password"
#   hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
#   username: "admin"
#   userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"

web段配置的是dexserver的监听地址及HTTPS证书参数,issuer配置的是外部会访问到的系统URL,这两者一般要对应地设置。

telemetry段配置的是监控指标抓取地址,本例中dexserver启动完毕后,可访问http://127.0.0.1:5558/metrics抓取到该dexserver的监控指标。

storage段配置的是dexserver的存储设置。dexserver在运行时跟踪refresh_tokenauth_codekeyspassword等的状态,因此需要将这些状态保存下来。dex提供了多种存储方案,如etcdCRDsSQLite3PostgresMySQLmemory,总有一款能满足需求。如果要其它需求,还可以参考现有Storage文档扩展一个。我这里使用的是比较简单的SQLite3Storage,提前插入了一条测试的用户数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sqlite3 config/dex.db
sqlite> insert info password values('admin@example.com', '$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W', 'admin', '08a8684b-db88-4b73-90a9-3cd1661f5466');
sqlite> .quit

oauth2.skipApprovalScreen这个选项我设置成了true,这样就不会有提示用户同意的页面出现。

staticClients段配置的是该dexserver允许接入的dexclient信息,这个要跟dexclient那边的配置一致。

connectors段我并没有配置任何ConnectorConnectordex中一项重要特性,其可以将dex这个身份认证系统与其它身份认证系统串联起来。dex目前自带的ConnectorLDAPGitHubSAML 2.0GitLabOpenID ConnectLinkedInMicrosoftAuthProxyBitbucket Cloud,基本上满足绝大部分需求,如果要扩展,参考某个现成的Connector实现即可。我这个示例里因为直接使用保存在DB里的帐户密码信息,因此只需要配置enablePasswordDBtrue,就会自动使用上passwordDB这个ConnectorpasswordDB的实现代码见这里

最近由于登录页面是由dexserver提供了,这里还将dex自带的登录页面web端资源带上了,具体的项目中根据场景对页面进行一些定制就可以了。

dexclient

dexclient就很简单了,就两个go文件,重点是cmd/dexclient/main.go

首先是根据一系列参数构造出oidc.Provideroidc.IDTokenVerifier,这个后面获取认证系统的跳转地址、获取id_token、校验id_token都会用到:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
            a.provider = provider
            a.verifier = provider.Verifier(&oidc.Config{ClientID: a.clientID})

然后声明处理三个请求地址,并启动Web Server:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
			http.HandleFunc("/", a.handleIndex)
			http.HandleFunc("/login", a.handleLogin)
			http.HandleFunc(u.Path, a.handleCallback)

			switch listenURL.Scheme {
			case "http":
				log.Printf("listening on %s", listen)
				return http.ListenAndServe(listenURL.Host, nil)
			case "https":
				log.Printf("listening on %s", listen)
				return http.ListenAndServeTLS(listenURL.Host, tlsCert, tlsKey, nil)
			default:
				return fmt.Errorf("listen address %q is not using http or https", listen)
			}

很明显handleIndex就是WEB应用的主页,这里一般逻辑应该是检查用户的登录身份信息是否合法,如果不合法则跳至dexserver的登录页面。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var indexTmpl = template.Must(template.New("index.html").Parse(`<html>
  <!-- TODO Redirect to login page if not logged  -->
  <body>
    <form action="/login" method="post">
       <p>
         Authenticate for:<input type="text" name="cross_client" placeholder="list of client-ids">
       </p>
       <p>
         Extra scopes:<input type="text" name="extra_scopes" placeholder="list of scopes">
       </p>
       <p>
         Connector ID:<input type="text" name="connector_id" placeholder="connector id">
       </p>
       <p>
         Request offline access:<input type="checkbox" name="offline_access" value="yes" checked>
       </p>
       <input type="submit" value="Login" id="submitBtn">
    </form>
  </body>
  <script type="text/javascript">
    <!-- Redirect to login page -->
	document.getElementById("submitBtn").click();
  </script>
</html>`))

handleLogin根据浏览器发来的cross_clientextra_scopesconnector_idoffline_access参数构造出登录页跳转地址,并提示浏览器跳至该地址:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    ...
    if r.FormValue("offline_access") != "yes" {
		authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState)
	} else if a.offlineAsScope {
		scopes = append(scopes, "offline_access")
		authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState)
	} else {
		authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState, oauth2.AccessTypeOffline)
	}
	if connectorID != "" {
		authCodeURL = authCodeURL + "&connector_id=" + connectorID
	}

	http.Redirect(w, r, authCodeURL, http.StatusSeeOther)

handleCallback处理登录成功后的回调请求,其根据回调请求中的code参数,调用dexserver的相关接口换取包含用户身份信息的Token

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        code := r.FormValue("code")
		if code == "" {
			http.Error(w, fmt.Sprintf("no code in request: %q", r.Form), http.StatusBadRequest)
			return
		}
		if state := r.FormValue("state"); state != exampleAppState {
			http.Error(w, fmt.Sprintf("expected state %q got %q", exampleAppState, state), http.StatusBadRequest)
			return
		}
		token, err = oauth2Config.Exchange(ctx, code)

一般来说,会将该Token中的id_token进行适当的编码发回到浏览器中保存(以Cookie或WebStorage等方式),这样浏览器中就保存了用户的身份信息。

安全起见,dexserver签发的id_token有效期通常不会太长,这就需要dexclient凭借Token中的refresh_token隔段时间重新换取新的Token,并通过某种机制将新Token中的id_token重新发回浏览器端保存。以refresh_token重新换取新的Token的代码实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
		t := &oauth2.Token{
			RefreshToken: refresh,
			Expiry:       time.Now().Add(-time.Hour),
		}
		token, err = oauth2Config.TokenSource(ctx, t).Token()

项目定制扩展

上面的示例基本说明得比较清楚了,在真实的生产项目需要进行必要的定制以满足项目需求,生产项目的代码必须保密,这里只简要说明下关键点:

  1. 生产系统,无论dexserver还是dexclient都必须配置HTTPS证书,通过HTTPS协议访问站点。
  2. dexserver会根据项目情况配置一个更合适的Storage,用以安全可靠地保存refresh_tokenauth_codekeyspassword等的状态。要考虑这个Storage实现方案的性能、稳定性、高可用性等多个因素。
  3. 一般还需要对保存进Storage的帐户身份信息进行维护管理,不可能每次都命令行增删帐户身份信息。
  4. 示例中dexserver直接使用的passwordDB这个Connector,实际场景可能会扩展passwordDB,比如需要往返回的claims里植入Groups等其它信息。
  5. 示例中dexserver只使用了passwordDB这个Connector,实际场景为了接入其它身份认证系统,会配置一些Connect以对接其它身份认证系统。
  6. 示例中dexserver只使用了dex官方自带的登录页模板,这个放在实际项目中还是太简陋了,一般需要一些定制美化。
  7. 示例中dexclient并没有在浏览器中完成用户身份信息验证,以决定是否跳转登录页面,这个在实际项目中肯定是要做的。
  8. 示例中dexclient并没有根据用户的选择跳转至指定的Connector登录页,对于支持多种认证方式的业务系统来说,这个也是必须的。
  9. 示例中dexclient并没有实现将Token中的id_token进行适当的编码发回到浏览器中保存的逻辑,这个一般也是必须的。
  10. 示例中dexclient并没有凭借Token中的refresh_token隔段时间重新换取新的Token,并通过某种机制将新Token中的id_token重新发回浏览器端保存,这个一般也是必须的。

总之这个示例要最终在生产项目中落地,还是要不少工作要处理的,不过要了解其核心工作原理,看这个示例也就差不多了。

总结

总体来说,采用dex实现一个身份认证系统相比纯自研,还是简单了不少。一旦掌握其原理,后续实现会很方便,其核心代码值得学习一下。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-08-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
使用YOLO11分割和高斯模糊创建人像效果
本文通过结合最新的YOLO11实例分割模型和高斯模糊,为你的图片应用人像效果。我们将使用YOLO11将人物从背景中分割出来,并对除了主体之外的所有内容应用模糊效果。
小白学视觉
2024/10/11
3760
使用YOLO11分割和高斯模糊创建人像效果
使用Python-去除视频背景
no怕不了木
2023/08/08
7800
使用Python-去除视频背景
视频处理系列︱利用达摩院ModelScope进行视频人物分割+背景切换(一)
做了一个简单的实验,利用modelscope的人像抠图模型对视频流进行抠像并更换背景。
悟乙己
2023/01/03
2K0
视频处理系列︱利用达摩院ModelScope进行视频人物分割+背景切换(一)
Github复现之TransUnet更新[通俗易懂]
上一篇关于TransUnet的GitHub复现,大家反映效果不好,调参也不好调,我把模型单独拿出来,放到另外一个框架,供大家参考学习(上一篇链接:https://blog.csdn.net/qq_20373723/article/details/115548900) 我这里训练了20个epoch,下面先给出效果正常的情况: 原图
全栈程序员站长
2022/10/04
1.9K0
Github复现之TransUnet更新[通俗易懂]
一行代码快速图像识别~一排代码搞定视频识别
一行代码能干嘛?这种噱头式的开头现在估计已经不香了。。。我只能在别人挖好的土堆上再刨一铲子。
一个有趣的灵魂W
2020/09/15
7920
一行代码快速图像识别~一排代码搞定视频识别
实战 | 基于YOLOv9+SAM实现动态目标检测和分割(步骤 + 代码)
本文主要介绍基于YOLOv9+SAM实现动态目标检测和分割,并给出详细步骤和代码。
Color Space
2024/04/12
2.8K0
实战 | 基于YOLOv9+SAM实现动态目标检测和分割(步骤 + 代码)
这个操作可能不值钱,但却值得学习 | 【图片批量裁剪】
有一句叫:数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已 这句话,在实际的应用中,让我体会很深,目前很多算法已经能到初步满足我们的需求,更多的时候是要花费大量的时间在处理数据上面,现实生活的数据经常是很难满足算法的基本要求,例如常见的样本不均衡问题的等,今天要介绍的就是1、固定区域图片裁剪;2、自定义裁剪图片
AI算法与图像处理
2019/05/22
7990
利用Python做一个小姐姐词云跳舞视频
本文将以哔哩哔哩–乘风破浪视频为例,you-get下载视频,同时利用python爬取B站视频弹幕,并利用opencv对视频进行分割,百度AI进行人像分割,moviepy生成词云跳舞视频,并添加音频。
Python编程与实战
2021/03/10
7760
利用Python做一个小姐姐词云跳舞视频
Segment Anything笔记 - plus studio
Segment Anything project是一个用于图像分割的新任务、模型和数据集。在他刚出来的那一天,知乎等平台就已经高呼CV已死。为了这个项目,作者创建了迄今为止最大的分割数据集,1100万张在10亿次授权且尊重隐私的图像上的数据集。模型也被设计和训练成了promptable,就是说可以给他一些提示。作者在多个数据集测试了他的结果并认为结果令人满意。
plus sign
2024/02/28
3860
Segment Anything笔记 - plus studio
OpenCV趣味应用系列--grabCut人物背景替换
本文将使用grabCut来提取图像中的人物,同时使用mask将提取的人物贴到新的背景中,实现给人物替换背景的功能。
Color Space
2022/12/22
7230
OpenCV趣味应用系列--grabCut人物背景替换
「 计算机视觉」帧差法移动侦测
这是我大学舍友小钊在前几天培训公司内部人员顺便记录的一篇关于帧差法移动侦测的文章,介绍下小钊,广西佬,我的大学舍友,特点是烟勤话少爱抠脚。是一名计算机视觉工程师,主要专注于桑拿沐足(啊?呸!)、将算法应用到嵌入式设备上,实现低成本的落地方案注册 。
JavaFish
2019/10/17
1.2K0
利用Python做一个漂亮小姐姐词云跳舞视频
B站上的漂亮的小姐姐真的好多好多,利用 you-get 大法下载了一个 B 站上跳舞的小姐姐视频,利用视频中的弹幕来制作一个漂亮小姐姐词云跳舞视频,一起来看看吧。
叶庭云
2021/02/02
1K0
学会这些Python美图技巧,就等着女朋友夸你吧
Python中有许多用于图像处理的库,像是Pillow,或者是OpenCV。而很多时候感觉学完了这些图像处理模块没有什么用,其实只是你不知道怎么用罢了。今天就给大家带了一些美图技巧,让你的图美翻全场,朋友圈赞不绝口,女朋友也夸你,富贵你好厉害啊!
ZackSock
2020/05/18
5700
学会这些Python美图技巧,就等着女朋友夸你吧
Python的Albumentations库
对 ImageNet validation set 中的前 2000 张图片进行处理,采用 Intel Core i7-7800X CPU. 不同数据增强库的处理速度对比(以秒为单位,时间越少越好).
狼啸风云
2021/05/13
2.4K0
Python的Albumentations库
OpenCV 图像分割之grabCut算法
GrabCut算法的原理涉及到以下知识: K均值聚类 高斯混合模型建模(GMM) max flow/min cut GrabCut算法的实现步骤: 在图片中定义(一个或者多个)包含物体的矩形。 矩形外的区域被自动认为是背景。 对于用户定义的矩形区域,可用背景中的数据来区分它里面的前景和背景区域。 用高斯混合模型(GMM)来对背景和前景建模,并将未定义的像素标记为可能的前景或者背景。 图像中的每一个像素都被看做通过虚拟边与周围像素相连接,而每条边都有一个属于前景或者背景的概率,这是基于它与周边像素颜色上的
用户6021899
2019/09/17
3.5K0
OpenCV 图像分割之grabCut算法
MaskRCNN 基于OpenCV DNN的目标检测与实例分割
这里主要记录基于 OpenCV 4.x DNN 模块和 TensorFlow MaskRCNN 开源模型的目标检测与实例分割 的实现.
AIHGF
2019/05/13
1.9K0
MaskRCNN 基于OpenCV DNN的目标检测与实例分割
Semantic Drone Dataset(语义无人机数据集)使用方法「建议收藏」
Semantic Drone Dataset数据集下载地址 该语义无人机数据集专注于城市场景的语义理解,以提高无人机自主飞行和着陆程序的安全性。该图像描绘了在距地面 5 至 30 米的高度从最低点(鸟瞰)视角拍摄的 20 多座房屋。高分辨率相机用于获取尺寸为 6000x4000px (24Mpx) 的图像。训练集包含 400 张公开可用的图像,测试集包含 200 张私有图像。
全栈程序员站长
2022/09/21
4850
Semantic Drone Dataset(语义无人机数据集)使用方法「建议收藏」
用 OpenCV 实现自动驾驶中的车道识别
“车道”,其相关解释在维基百科或者百度百科上都有,不过,正如我们日常所言,都是用来专指“机动车道”。所以,“车道识别”、“自动驾驶”等术语,也是针对机动车而言。
老齐
2021/12/13
7530
用 OpenCV 实现自动驾驶中的车道识别
交互式前景提取
算法:交互式前景提取是首先用矩形框指定要提取的前景所在的大致范围,接着执行前景提取算法得到初步结果。然后,用户复制原图像作为掩模图像,用白色标注要提取的前景区域,用黑色标注背景区域。最后,使用掩模图像执行前景提取算法从而获得理想的提取结果。
裴来凡
2022/09/21
6940
交互式前景提取
CV -- YOLOv8 图像分割(GPU环境)
https://download.csdn.net/download/2403_83182682/90405543?spm=1001.2014.3001.5503
用户11404404
2025/02/20
2660
CV -- YOLOv8 图像分割(GPU环境)
相关推荐
使用YOLO11分割和高斯模糊创建人像效果
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档