登录认证模式
[一]概述
OAuth
全称是 Open Authentication
开放授权(OAuth
)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。 OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的 2 小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth
让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。
目前使用最广泛的是
OAuth 2.0
,OAuth 1.0
已经被废弃了。本文中的 OAuth 都是指OAuth 2.1
[1]OAuth2 授权流程中的角色
- 资源拥有者(
resource owner
):能授权访问受保护资源的一个实体,可以是一个人,那我们称之为最终用户; - 资源服务器(
resource server
):存储受保护资源,客户端通过access token
请求资源,资源服务器响应受保护资源给客户端; - 授权服务器(
authorization server
):成功验证资源拥有者并获取授权之后,授权服务器颁发授权令牌(Access Token
)给客户端。 - 客户端(
client
):第三方应用,也可以是它自己的官方应用;其本身不存储资源,而是资源拥有者授权通过后,使用它的授权(授权令牌)访问受保护资源,然后客户端把相应的数据展示出来/提交到服务器。
[2]令牌与密码
令牌(token
)与密码(password
)的作用是一样的,都可以进入系统,但是有三点差异。
- (1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。
- (2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。
- (3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。
上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0
的优点。
注意
只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。
[二]认证模式
[1]OAuth 2.0
OAuth 2.0
协议根据使用不同的适用场景,规定了四种获得令牌的流程。你可以选择最适合自己的那一种,向第三方应用颁发令牌。下面就是这四种授权方式。
- 授权码模式(
Authorization Code Grant
) - 隐式模式(
Implicit Grant
) - 密码模式(
Resource Owner Password Credentials Grant
) - 客户端凭证模式(
Client Credentials Grant
)
[2]OAuth 2.1
OAuth 2.1
是 OAuth 2.0
的下一个版本, OAuth 2.1
根据最佳安全实践(BCP), 目前是第18个版本,对 OAuth 2.0
协议进行整合和精简, 移除不安全的授权流程。
在 OAuth 2.1
中,密码模式和隐式模式均已经被弃用,主要支持的模式为:
- 授权码模式(
Authorization Code Grant
) - 客户端凭证模式(
Client Credentials Grant
)
[3]Dante Cloud
Dante Cloud 认证组件使用的是 Spring 生态中的 Spring Authorization Server
,Spring Authorization Server
是基于 OAuth 2.1
协议实现。
默认支持以下认证模式:
- 授权码模式(
Authorization Code Grant
) - 客户端凭证模式(
Client Credentials Grant
)
除此以外,在 Spring Authorization Server
新版本中,已经支持自 OAuth 2.0
开始支持的扩展认证模式:
- 设备码模式(
Device Authorization Grant
)
考虑到老旧项目的兼容性,以及实际应用需求,Dante Cloud 在 Spring Authorization Server
基础之上,有扩展了两种认证模式
- 密码模式(
Resource Owner Password Credentials Grant
) - 社会化模式(
Social Credentials Grant
)
所以,Dante Cloud 目前支持的认证模式有:
- 授权码模式(
Authorization Code Grant
) - 客户端凭证模式(
Client Credentials Grant
) - 设备码模式(
Device Authorization Grant
) - 密码模式(
Resource Owner Password Credentials Grant
) - 社会化模式(
Social Credentials Grant
)
说明
- 密码模式:Dante Cloud 之所以又扩展了
OAuth 2.1
中已经弃用的密码模式,是考虑到好多旧版本时代(使用Spring Security OAuth2
)的客户端,大多数使用的是密码模式登录,为了兼容这部分客户端所以保留了密码模式 - 社会化模式:是 Dante Cloud 为了支持互联网应用登录的需求,将比较常见的短信验证码登录、微信小程序登录、第三方系统认证登录等多种认证方式,容易融合为了社会化登录模式
[三]认证模式的原理
[1]授权码授权模式
授权码授权模式主要流程如下图所示:
- 第一步:用户访问页面或者出发认证地址
- 第二步:访问的页面将请求重定向到认证服务器
- 第三步:用户登录成功只有,认证服务器向用户展示授权页面,等待用户授权
- 第四步:用户授权,认证服务器生成一个
code
和带上client_id
发送给应用服务器。然后,应用服务器拿到code
,并用client_id
去后台查询对应的client_secret
- 第五步:将
code
,client_id
,client_secret
传给认证服务器换取access_token
和refresh_token
- 第六步:将
access_token
和refresh_token
传给应用服务器 - 第七步:验证
token
,访问真正的资源页面
[2]客户端凭证模式
客户端凭证模式主要流程如下图所示:
- 第一步:用户访问应用客户端
- 第二步:通过客户端定义的验证方法,拿到 token,无需授权
- 第三步:访问资源服务器 A
- 第四步:拿到一次 token 就可以畅通无阻的访问其他的资源页面。
说明
这是一种最简单的模式,只要 client 请求,我们就将 AccessToken 发送给它。这种模式是最方便但最不安全的模式。因此这就要求我们对 client 完全的信任,而 client 本身也是安全的。
因此这种模式一般用来提供给我们完全信任的服务器端服务。在这个过程中不需要用户的参与。
[3]密码模式
密码模式主要流程如下图所示:
- 第一步:用户访问用页面时,输入第三方认证所需要的信息(QQ/微信账号密码)
- 第二步:应用页面那种这个信息去认证服务器授权
- 第三步:认证服务器授权通过,拿到 token,访问真正的资源页面
说明
优点:不需要多次请求转发,额外开销,同时可以获取更多的用户信息。(都拿到账号密码了)
缺点:局限性,认证服务器和应用方必须有超高的信赖。(比如亲兄弟?)
应用场景:自家公司搭建的认证服务器
[四]认证模式的验证
[1]授权码授权模式验证
常见的应用场景是:第三方应用先申请一个授权码,然后再用该码获取令牌。
这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。
第一步:拼装申请 code 请求 URL
拼装客户端向资源端申请 code 请求的 URL。这里所说的客户端,是指任意想要使用 Dante Cloud
通过授权码授权模式获取 Token 的应用。
主要逻辑:假设,某网站或者系统(客户端),我们称之为 A 。Dante Cloud
向 A 网站提供一个认证链接,A 网站会把这个连接做成一个按钮,用户点击后就会根据这个连接跳转到 Dante Cloud
,在 Dante Cloud
认证通过后授权用户数据给 A 网站使用。
http://192.168.101.10:8847/herodotus-cloud-uaa/oauth2/authorize?response_type=code&client_id=14a9cf797931430896ad13a6b1855611&client_secret=a05fe1fc50ed42a4990c6c6fc4bec398&scope=read-user-by-page&state=0CI0ziUDEnqMgqW0nzRNRCzLrs-9IMbqJzGZ47Zb0gY&redirect_uri=http://192.168.101.10:3000/authorization-code
参数说明
response_type
:必选参数。值固定为“code”。client_id
:必选参数client_secret
:必选参数state
:客户端 提供的一个字符串,服务器会原样返回给客户端。这个要自己实现,用于防止恶意攻击。redirect_uri
:必选参数(授权成功后的重定向地址)scope
:可选参数(表示授权范围)
第二步:资源端返回 code 给客户端
可以采用以下两种方式进行验证:
- 第一种:如果有想要接入的系统,那么就在这个系统中做一个图标按钮,点击后跳转到上面的地址。(比如:很多系统都支持微信登录,那么在页面上就会有一个微信的图标按钮,点击后跳转到一个地址)
- 第二种:如果没有想接入的系统,可以将上面的地址输入到浏览器,获取信息后配合
Postman
等工具进行验证。
下面采用第二种浏览器的方式
在浏览器中输入上面的地址,会跳出如下登录界面。
输入用户名,密码和验证码进行用户验证。
可以使用系统默认用户:system 密码:123456
登录成功后,会跳转到授权页面进行授权,如下图所示:
授权成功后,就会跳转到一个新的地址,同时在地址的后面会跟随生成的 code, 如下所示。这个地址就是系统中设置的 redirect_uri
http://192.168.101.10:3000/authorization-code?code=Jw8NDqRqJB9dZPGKATCOwlEWDemCO7VIfVqozlpKOLuDSji6SyvuhZ-f3GYtURkBuF5l-WuBnLYV7wDTq_ikz7XkcYL1bFHMrthf5FL4P9I0Dam09aTHnX7lbkqlqwNX&state=0CI0ziUDEnqMgqW0nzRNRCzLrs-9IMbqJzGZ47Zb0gY
提示
redirect_uri
是由开发人员根据需要配置的,也是需要开发人员实现代码逻辑的。
URL 本质就是一个请求地址,授权服务器会将 code
和 state
参数发送到这个地址。这个请求地址对应的业务逻辑,就是接收 code
和 state
参数,然后拼装请求来获取 Token。
第三步:客户端根据 code 向资源端请求令牌
在你习惯的工具中,输入下面的地址:
http://192.168.101.10:8847/dante-cloud-uaa/oauth/token?client_id=010e659a-4005-4610-98f6-00b822f4758e&client_secret=04165a07-cffd-45cf-a20a-1c2a69f65fb1&grant_type=authorization_code&code=P6dxH5&scope=all&redirect_uri=http://localhost:9999/passport/login
参数
grant_type
:必选参数(固定值“authorization_code”)code
: 必选参数。上一步通过redirect_uri
返回的code
state
: 可选参数。上一步通过redirect_uri
返回的state
。避免在请求的过程中被篡改。如果在第一步就没有传递state
,这里就可以省略。redirect_uri
:必选参数(必须和 Request 中提供的 redirect_uri 相同)client_id
:必选参数client_secret
:必选参数scope
:可选参数(表示授权范围)。如果有多个值,以“空格”进行分隔。
下图以 POSTMAN 为例:
[2]客户端凭证模式验证
这种模式直接根据 client 的 id 和密钥即可获取 token,无需用户参与 这种模式比较合适消费 api 的后端服务,比如拉取一组用户信息等
直接使用如下地址获取 Token 即可
http://192.168.101.10:8847/herodotus-cloud-uaa/oauth2/token
POSTMAN 请求参考截图如下:
参数说明
grant_type
:必选参数(固定值“client_credentials”)client_id
:必选参数client_secret
:必选参数scope
:可选参数(表示授权范围)。如果有多个值,以“空格”进行分隔。虽然说是可选,但是如果没有该参数将没有权限访问任何接口
提示
使用客户端凭证模式,只能使用 Content-Type
为 x-www-form-urlencoded
的 POST
请求
[3]密码模式验证
提示
Spring Authorization Server 基于 OAuth2.1 协议开发并不支持密码模式。Dante Cloud 中的密码模式是自定义授权模式,为了兼容老的、还在使用 OAuth 2.0 协议的系统
密码模式,需要用户输入用户名和密码进行 OAuth 授权登录。密码模式安全性较低,为了保证安全性,首先要确保“客户端”是可信的。那么,在使用密码模式时,除了需要提供用户名和密码之外,还要有这个客户端对应的 Client ID
和 Client Secret
。
直接使用如下地址获取 Token 即可:
http://192.168.101.10:8847/herodotus-cloud-uaa/oauth2/token
POSTMAN 请求参考截图如下:
参数说明
grant_type
:必选参数(固定值“password”)username
: 必选参数password
:必选参数client_id
:必选参数client_secret
:必选参数scope
:可选参数(表示授权范围)。如果有多个值,以“空格”进行分隔。
参数传递方式一
密码模式的参数传递可以有几种方式,前面截图只是方式之一。使用 Content-Type
为 x-www-form-urlencoded
的 POST
请求,所有的参数都放入在 form 中
注
client_id
、client_secret
两个参数和其他参数统一放在 form,需要将客户端的 ClientAuthenticationMethod
配置为 CLIENT_SECRET_POST
参数传递方式二
这种方式下client_id
、client_secret
两个参数,并不是和其他参数一起放在 form 中。而是采用 Basic Auth 方式,将 client_id
、client_secret
两个参数使用 Base64 编码后,放入请求头中。
POSTMAN 请求参考截图如下:
注
这种方式下,需要将客户端的 ClientAuthenticationMethod
配置为 CLIENT_SECRET_BASIC
参数传递方式三
第三种方式, client_id
、client_secret
两个参数还是使用 Base64 编码后放入请求头中。不同的是,不再采用 Content-Type
为 x-www-form-urlencoded
形式将参数放入 Form 中,而是将参数直接一 QueryParam 的形式拼装在请求路径中
注
这种方式,不是和容易让人理解,所以不推荐使用。还是建议采用前面两种方式,这样还可以实现获取 Token 请求方式的统一,减少出错。
[4]社会化凭证模式验证
社会化凭证模式(Social Credentials),是 Dante Cloud 中自定义授权模式的授权模式。主要用于解决微信小程序等第三方系统认证登录的问题。
当然,不仅仅是微信小程序,还包括手机验证码登录、微信公众号以及JustAuth组件支持的所有第三方系统。而且是将所有这种涉及外部系统的登录方式,全部统一为社会化凭证模式,而且使用 OAuth2 统一的 /oauth2/token
地址进行登录。
这样做无需为各种第三方系统单独实现登录,而且与 Spring Authorization Server 认证体系完全一致,极大地简化了集成的复杂度,提升了系统使用的便捷性。
社会化凭证模式与密码模式的使用方式非常相似,直接使用如下地址获取 Token 即可:
http://192.168.101.10:8847/herodotus-cloud-uaa/oauth2/token
下图就分别以短信验证码登录以及微信小程序登录作为示例:
参数说明
grant_type
:必选参数(固定值“social-credentials”)client_id
:必选参数client_secret
:必选参数source
:必选参数,登录类型。- 其它参数。根据不同类型的登录所需的参数不同
当前支持的 source
类型,如下表所示:
编码 | 名称 |
---|---|
INSTITUTION | 机构人员 |
SMS | 手机验证码 |
WXAPP | 微信小程序 |
微博 | |
BAIDU | 百度 |
WECHAT_OPEN | 微信开放平台 |
WECHAT_MP | 微信公众号 |
WECHAT_ENTERPRISE | 企业微信二维码 |
WECHAT_ENTERPRISE_WEB | 企业微信网页 |
DINGTALK | 钉钉 |
DINGTALK_ACCOUNT | 钉钉账号 |
ALIYUN | 阿里云 |
TAOBAO | 淘宝 |
ALIPAY | 支付宝 |
TEAMBITION | Teambition |
HUAWEI | 华为 |
FEISHU | 飞书 |
JD | 京东 |
DOUYIN | 抖音 |
TOUTIAO | 今日头条 |
MI | 小米 |
RENREN | 人人 |
MEITUAN | 美团 |
ELEME | 饿了么 |
KUJIALE | 酷家乐 |
XMLY | 喜马拉雅 |
GITEE | 码云 |
OSCHINA | 开源中国 |
CSDN | CSDN |
GITHUB | Github |
GITLAB | Gitlab |
STACK_OVERFLOW | Stackoverflow |
CODING | Coding |
GOOGL | 谷歌 |
MICROSOFT | 微软 |
脸书 | |
领英 | |
推特 | |
AMAZON | 亚马逊 |
SLACK | Slack |
LINE | Line |
OKTA | Okta |
重要
上表中绝大多数登录方式均由 JustAuth 提供。每种登录所需的参数不同,而且随着时间的推移存在登录方式是否可用、参数变化、接口变化等问题。没有条件和精力保证所有登录方式都可用,如果你确实需要使用到某种登录,但是目前 Dante Cloud 不支持或者无效。可以提 ISSUE,会尽快补充。
[5]刷新令牌模式验证
刷新令牌模式,即使用 refresh_token
来重新申请 access_token
。利用这种方式,可以简化操作,避免用户的重复操作。
刷新令牌模式与社会化凭证模式与密码模式的使用方式非常相似
直接使用如下地址获取 Token 即可:
http://192.168.101.10:8847/herodotus-cloud-uaa/oauth2/token
参数说明
grant_type
:必选参数(固定值“refresh_token”)client_id
:必选参数client_secret
:必选参数refresh_token
:必选参数。即第一次申请access_token
参数时一同返回的refresh_token
。
提示
正因为刷新令牌模式的特性,而且refresh_token
也是存在有效期的,所以在使用时都会将 refresh_token
的有效期设置为比 access_token
有效期长。
[五]Dante Cloud 中的特性
[2]客户端凭证模式
标准的客户端凭证模式,仅是使用 Scope
作为权限。常规的实现,就是拿请求中的 Scope
与该客户端配置的 Scope
进行比较。但是,通常我们系统中关键的资源都是 REST 接口,那么又该如何使用 Scope
来确定具体使用接口的权限呢?
Dante Cloud 对客户端凭证模式进行了扩展,实现了通过分配 Scope
权限就可以对 REST 接口进行访问控制的能力。验证的方式如下:
- 以接口
/security/user
为例,先请求一下这个接口,可以看到系统返回没有访问权限的提示,如下图所示
- 我们为这个接口配置一个客户端凭证授权模式,同时指定其授权范围
Scope
为read-user-by-page
- 再次请求接口
/security/user
,系统返回了接口响应数据