单点登录 SSO(Single Sign On)是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。打通所有系统的账户密码,只需要记住一个就行,而且登录一个系统后,打开其他系统不需要再登录。广义的单点登录概念还涵盖了第三方登录:己方应用信任第三方应用,用户只需在第三方应用上登录一次,就可以访问己方应用,比如说常见的微信登录、微博登录。
OIDC 是 OpenID Connect,见官网介绍。OIDC 为用户身份认证提供了明确的规范指南,以方便我们更好地设计一个 SSO 产品。
OIDC 看上去会是和 OAuth 很相似(主要流程上)。但要说不同也很不同,因为两者出发点根本不一样,一个是识别用户是谁(OIDC),一个不太关心用户是谁,只关心用户能够做什么,可不可以做什么(OAuth)。如此,参与的角色自然有所不同,参见下面对比的表格。
OIDC 角色 | 说明 | 近似的 OAuth 对应角色 |
---|---|---|
EU:End User | 用户,一个人 | Resource Owner,资源拥有者,即用户 |
RP:Relying Party | 用户身份信息的使用方 | Client:客户机应用,用户数据资源的使用方 |
OP:OpenID Provider | 用户身份信息的提供方 | AS:Authentication Server,授权服务器 |
IDP:Identity Provider | 身份提供商,当 OP 是第三方应用时,一般称为 IDP | 无 |
无 | 无 | RS:Resource Server,资源服务器,用户数据资源的提供方 |
资历方面当然 OAuth 打头,而且容易给人一个错觉,OAuth 可以做 SSO(实际上很多人也那么做)。——为什么会有这错觉呢?——原因在于 OAuth 虽说不太关心用户是谁,但它实质隐含了一个前提,用户必须在 AS(授权服务器) 上先登录、先拥有身份才能做其他事,于是围绕这个用户,自然就有用户注册呀、登录呀等,做那些用户身份的事情,以致于大家几乎快忘了 OAuth 的侧重点是做资源授权的;另外一点就是 OAuth 问世比较早,所以大家先入为主。
把 OAuth 应用在 SSO,虽说实践来说问题不大,但终究是“牵强附会”,概念上给予人的感觉就是很别扭。——直到 OIDC 出现,才“名正言顺”、“正本清源”,放心做 SSO 了。
用户管理系统,自然要有 user 用户表,又因用户与账户分离,须有 user_account 账号表;客户端应用,是 app 表是也;另外针对 access_token,有 oauth_access_token 表。总之乃这四张基础表先是也,后面可视情况再增加。
user 表,关于用户的一些基本情况。双主键设计(id、uid)。
CREATE TABLE `user` (
`id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '主键 id,自增',
`uid` BIGINT(19) NULL DEFAULT NULL COMMENT '唯一不重复 id,可以是雪花 id',
`org_id` INT(10) NULL DEFAULT NULL COMMENT '部门 ID',
`tenant_id` INT(10) NULL DEFAULT NULL COMMENT '租户 id',
`name` VARCHAR(20) NOT NULL COMMENT '用户名称' ,
`content` VARCHAR(256) NULL DEFAULT NULL COMMENT '备注' COLLATE 'utf8mb4_unicode_ci',
`gender` TINYINT(3) NULL DEFAULT NULL COMMENT '性别',
`birthday` DATE NULL DEFAULT NULL COMMENT '出生日期',
`avatar` VARCHAR(200) NULL DEFAULT NULL COMMENT '头像' COLLATE 'utf8mb4_unicode_ci',
`stat` TINYINT(3) NULL DEFAULT NULL COMMENT '数据字典:状态',
`extend` TEXT NULL DEFAULT NULL COMMENT '扩展 JSON 字段' COLLATE 'utf8mb4_unicode_ci',
`creator` VARCHAR(50) NULL DEFAULT NULL COMMENT '创建人名称(可冗余的)' COLLATE 'utf8mb4_bin',
`creator_id` INT(10) NULL DEFAULT NULL COMMENT '创建人 id',
`create_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
`updater` VARCHAR(50) NULL DEFAULT NULL COMMENT '修改人名称(可冗余的)' COLLATE 'utf8mb4_bin',
`updater_id` INT(10) NULL DEFAULT NULL COMMENT '修改人 id',
`update_date` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `id_UNIQUE` (`id`) USING BTREE,
UNIQUE INDEX `uid` (`uid`) USING BTREE
)
COMMENT='关于用户的一些基本情况'
user_account 用户账号表,用户一对多账号,一个用户可以拥有多个账号。
CREATE TABLE `user_account` (
`id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '主键 id,自增',
`user_id` INT(10) NOT NULL COMMENT '用户id',
`login_type` TINYINT(3) NULL DEFAULT NULL COMMENT '登录类型(手机号 邮箱 用户名)或第三方应用名称(微信 微博等)',
`register_type` TINYINT(3) NOT NULL DEFAULT '0' COMMENT '注册类型',
`register_ip` VARCHAR(45) NULL DEFAULT NULL COMMENT '注册 ip' COLLATE 'utf8mb4_bin',
`identifier` VARCHAR(50) NULL DEFAULT NULL COMMENT '第三方应用的 User 唯一标识,如微信的 OpenId' COLLATE 'utf8mb4_bin',
`credential` VARCHAR(255) NULL DEFAULT NULL COMMENT '密码凭证(站内的保存密码,站外的不保存或保存 token)' COLLATE 'utf8mb4_bin',
`email_verified` TINYINT(1) NULL DEFAULT NULL COMMENT '邮箱是否验证',
`phone_verified` TINYINT(1) NULL DEFAULT NULL COMMENT '手机是否验证',
`stat` TINYINT(3) NULL DEFAULT NULL COMMENT '数据字典:状态',
`uid` BIGINT(19) NULL DEFAULT NULL COMMENT '唯一 id,通过 uuid 生成不重复 id',
`creator` VARCHAR(50) NULL DEFAULT NULL COMMENT '创建人名称(可冗余的)' COLLATE 'utf8mb4_bin',
`creator_id` INT(10) NULL DEFAULT NULL COMMENT '创建人 id',
`create_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
`updater` VARCHAR(50) NULL DEFAULT NULL COMMENT '修改人名称(可冗余的)' COLLATE 'utf8mb4_bin',
`updater_id` INT(10) NULL DEFAULT NULL COMMENT '修改人 id',
`update_date` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
`update_password_date` DATETIME NULL DEFAULT NULL COMMENT '上次更新密码日期',
PRIMARY KEY (`id`) USING BTREE
)
COMMENT='用户账号'
每个想要接入OIDC 授权服务的第三方客户端都需要事先在服务端这里“备案”,这样可以更好的管理接入的第三方应用,即 RP:Relying Party。客户端本身英文是 Client,但可泛化概念为 App,这里表名取 app,多数概念是一致的。
注册应用的时候一般需要提供一些基本信息,比如应用名称、网址、logo 等。主要需要以下几个字段:
表设计及界面设计如下
CREATE TABLE `app` (
`id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '主键 id,自增',
`pid` INT(10) NOT NULL DEFAULT '0' COMMENT '父级 id',
`name` VARCHAR(20) NOT NULL COMMENT '名称' COLLATE 'utf8mb4_unicode_ci',
`content` VARCHAR(256) NULL DEFAULT NULL COMMENT '简介' COLLATE 'utf8mb4_unicode_ci',
`client_id` VARCHAR(256) NOT NULL COMMENT '客户端 id' COLLATE 'utf8mb4_unicode_ci',
`client_secret` VARCHAR(256) NOT NULL COMMENT '客户端秘钥' COLLATE 'utf8mb4_unicode_ci',
`redirect_uri` VARCHAR(256) NOT NULL COMMENT '用户授权完成之后重定向回你的应用' COLLATE 'utf8mb4_unicode_ci',
`type` VARCHAR(20) NULL DEFAULT 'MISC' COMMENT '应用类型:HTML, APP,API_SERVICE, RPC_SERVICE, MISC' COLLATE 'utf8mb4_unicode_ci',
`logo` VARCHAR(200) NULL DEFAULT NULL COMMENT '图标' COLLATE 'utf8mb4_unicode_ci',
`stat` TINYINT(3) NULL DEFAULT NULL COMMENT '数据字典:状态',
`extend` TEXT NULL DEFAULT NULL COMMENT '扩展 JSON 字段' COLLATE 'utf8mb4_unicode_ci',
`creator` VARCHAR(50) NULL DEFAULT NULL COMMENT '创建人名称(可冗余的)' COLLATE 'utf8mb4_bin',
`creator_id` INT(10) NULL DEFAULT NULL COMMENT '创建人 id',
`create_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
`updater` VARCHAR(50) NULL DEFAULT NULL COMMENT '修改人名称(可冗余的)' COLLATE 'utf8mb4_bin',
`updater_id` INT(10) NULL DEFAULT NULL COMMENT '修改人 id',
`update_date` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `id_UNIQUE` (`id`) USING BTREE
)
COMMENT='应用/客户端'
抽象 App 概念,可纳入 System,利用 pid 做层级。
Token 从简单到复杂,可以采取下面几种方案。
Token 会存到 Redis 或者其他缓存。另外也要持久化到数据库 以方便管理。
CREATE TABLE `access_token` (
`id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '主键 id,自增',
`access_token` VARCHAR(255) NOT NULL COMMENT 'Access Token' COLLATE 'utf8mb4_unicode_ci',
`refresh_token` VARCHAR(255) NULL DEFAULT NULL COMMENT 'Refresh Token' COLLATE 'utf8mb4_unicode_ci',
`user_id` INT(10) NULL DEFAULT NULL COMMENT '关联的用户 id',
`user_name` VARCHAR(100) NULL DEFAULT NULL COMMENT '用户名(冗余的)' COLLATE 'utf8mb4_unicode_ci',
`client_id` VARCHAR(50) NULL DEFAULT '' COMMENT '接入的客户端ID' COLLATE 'utf8mb4_unicode_ci',
`expires_date` DATETIME NULL DEFAULT NULL COMMENT '过期的具体时间',
`grant_type` VARCHAR(50) NULL DEFAULT NULL COMMENT '授权类型,比如:authorization_code' COLLATE 'utf8mb4_unicode_ci',
`scope` VARCHAR(100) NULL DEFAULT NULL COMMENT '可被访问的用户的权限范围,比如:basic、super' COLLATE 'utf8mb4_unicode_ci',
`creator` VARCHAR(50) NULL DEFAULT NULL COMMENT '创建人名称(可冗余的)' COLLATE 'utf8mb4_bin',
`creator_id` INT(10) NULL DEFAULT NULL COMMENT '创建人 id',
`create_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
`update_date` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `id_UNIQUE` (`id`) USING BTREE
)
COMMENT='AccessToken 令牌信息'
关于用户系统的基础数据先说到这里。下一节将正式进入 SSO 接口的实现!
有时间的同学可以先预习一下 OIDC 协议的内容,下面这是很好的参考文章《IAM的主流身份验证方法之OIDC协议》https://blog.csdn.net/ZAWX_NETSTARSEC/article/details/130711256。