• 轻量级 SSO 方略


    单点登录 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

    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='关于用户的一些基本情况'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    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='用户账号'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    客户端应用 App

    每个想要接入OIDC 授权服务的第三方客户端都需要事先在服务端这里“备案”,这样可以更好的管理接入的第三方应用,即 RP:Relying Party。客户端本身英文是 Client,但可泛化概念为 App,这里表名取 app,多数概念是一致的。

    注册应用的时候一般需要提供一些基本信息,比如应用名称、网址、logo 等。主要需要以下几个字段:

    • name:应用的名称,这个便于后台管理用的
    • client_id:每个应用客户端的 client_id 是唯一的,相当于用户名,通常是一个随机生成的字符串,client_id 可以直接写在 Javascript 或者源码页面里面
    • client_secret:这个秘钥是应用客户端和 OAuth2.0 服务端共同持有,用于鉴别请求中的身份,通常也是一个随机生成的字符串。client_secret 必须保证绝对机密,不能泄露给其他人。如果你部署的应用无法保证 client_secret 安全的话,比如Javascript 应用或者 Native APP,那么则不能使用 client_secret。一般来说,只有服务器端才可以保存 client_secret
    • redirect_uri:第三方应用的地址。Redirect URI 可以在用户授权完成之后重定向回你的应用。Redirect URI 授权服务器只会重定向用户到已经注册过的 URI,以避免一些恶意攻击

    模块设计

    表设计及界面设计如下

    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='应用/客户端'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    计划

    抽象 App 概念,可纳入 System,利用 pid 做层级。

    管理 AccessToken 令牌信息

    Token 从简单到复杂,可以采取下面几种方案。

    • 最简单的 Token,就是随机字符串,那么我们用 UUID/GUID 即可
    • 如果有验证 AuthToken 合法性需求,可以将 UserName+ 时间戳加密生成,服务端解密之后验证合法性
    • JWT。我们后面重点说 JWT,这是 OIDC 规范的 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 令牌信息'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    小结

    关于用户系统的基础数据先说到这里。下一节将正式进入 SSO 接口的实现!

    有时间的同学可以先预习一下 OIDC 协议的内容,下面这是很好的参考文章《IAM的主流身份验证方法之OIDC协议》https://blog.csdn.net/ZAWX_NETSTARSEC/article/details/130711256

  • 相关阅读:
    react-native 打包报错 android-gradle-plugin-requires-java-11
    【Linux】信号(2)如何阻塞、处理信号
    力扣151 - 反转字符串中的单词【双指针与字符串的火花】
    分布式锁2:基于redis实现分布式锁
    ArrayList和LinkedList的区别?
    java-php-python-ssm面向大学生心理健康服务平台计算机毕业设计
    07.webpack5搭建Vue环境
    Camtasia mac版怎么加字幕 Camtasia mac版怎么打马赛克
    jsDate总的毫秒数(时间戳)
    操作系统学习笔记11 | 生磁盘的使用与管理
  • 原文地址:https://blog.csdn.net/zhangxin09/article/details/133605680