IdentityServer4
IdentityServer4 是为ASP.NET Core 系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证框架。将identityserver部署在你的应用中,具备如下的特点
- 认证服务
- 可以为你的应用(如网站、本地应用、移动端、服务)做集中式的登录逻辑和工作流控制。IdentityServer是完全实现了OpenID Connect协议标准。
- 单点登录登出(SSO)
- 在各种类型的应用上实现单点登录登出。
- API访问控制
- 为各种各样的客户端颁发access token令牌,如服务与服务之间的通讯、网站应用、SPAS和本地应用或者移动应用。
- 联合网关
- 支持来自Azure Active Directory, Google, Facebook这些知名应用的身份认证,可以不必关心连接到这些应用的细节就可以保护你的应用。
- 专注于定制
- 最重要的是identityserver可以根据需求自行开发来适应应用程序的变化。identityserver不是一个框架、也不是一个盒装产品或一个saas系统,您可以编写代码来适应各种场景。
- 成熟的开源系统
- IdentityServer拥有apache 2 授权许可,允许构建商业化的应用,也是.net基金会组织的成员之一,并未其提供法律支持。
IdentityServer4支持的规范
OpenID Connect
- OpenID Connect Core 1.0(规范)
- OpenID Connect Discovery 1.0(规范)
- OpenID Connect Session Management 1.0 – draft 22(规范)
- OpenID Connect HTTP-based Logout 1.0 –draft 03(规范)
OAuth2.0
- OAuth 2.0 (RFC 6749)
- OAuth 2.0 Bearer Token Usage (RFC 6750)
- OAuth 2.0 Multiple Response Types(规范)
- OAuth 2.0 Form Post Response Mode(规范)
- OAuth 2.0 Token Revocation(RFC 7009)
- OAuth 2.0 Token Introspection(RFC 7662)
- Proof Key for Code Exchange(RFC 7636)
- JSON Web Tokens for Client Authentication(RFC 7523)
授权和认证
- oauth2.0:授权 authorization(你能干什么)
- openid connect:身份认证authentication(你是谁)
什么是OAuth2.0
oauth2.0是一个委托协议,它可以让那些控制资源的人允许某个应用以代表他们来访问他们控制的资源,注意是代表这些人,而不是假冒或模仿这些人,这个应用从资源的所有者那里获得到授权(Authorization)和access token,随后就可以使用这个access token来访问资源.oauth2.0是一个关于授权authorization、access token 和支持各种客户端应用的协议(它无法做身份认证)
授权服务器authorization server
在othor2.0里,我们说了用户可以委派权限给客户端应用来访问受保护的资源,那怎么来委派呢,所以客户端应用和受保护资源之间就需要一个桥梁来进行连接,这个桥梁就是授权服务器,授权服务器可以和我们的资源服务器放在一起(在物理上),但是通常是不放在一起的
授权类型 Authorization Grant
授权码模式(authorization code)
使用授权服务器作为客户端软件资源所有者中介来获取权限的,授权码模式,功能最完整、流程最严密的授权模式
特点是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。
以微信公众平台公众号网页应用开发流程为例。步骤如下:
- 用户访问客户端,客户端将用户导向认证服务器。
- 用户选择是否给予客户端授权。
- 若用户给予授权,认证服务器将用户导向客户端指定的"重定向URI"(redirection URI),同时附上授权码code。
- 客户端收到授权码code,附上早先的"重定向URI",向认证服务器申请token。这一步是在客户端的后台的服务器上完成的,对用户不可见。
- 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)
一些重要参数:
- response_type:表示授权类型,必选项,此处的值固定为"code"
- appid:表示客户端的ID,必选项
- redirect_uri:表示重定向URI,可选项
- scope:表示申请的权限范围,可选项
- state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。用于防止恶意攻击
1.引导用户跳转到授权页面:
参数:
- appid:公众号的唯一标识
- redirect_uri:授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
- response_type:返回类型,请填写code
- scope:应用授权作用域,有snsapi_base 、snsapi_userinfo 两种
- state:重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
2.通过code获取Token
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数:
- appid:公众号的唯一标识
- secret:公众号的appsecret
- code:填写获取的code参数(存在有效期,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系)
- grant_type:填写为authorization_code
返回结果:
{
"access_token":"ACCESS_TOKEN", //网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
"expires_in":7200, // access_token接口调用凭证超时时间,单位(秒)
"refresh_token":"REFRESH_TOKEN", //用户刷新access_token
"openid":"OPENID", //用户唯一标识
"scope":"SCOPE" //用户授权的作用域,使用逗号(,)分隔
}
- access_token:表示访问令牌,必选项。
- token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
- expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
- refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
- scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
简化模式(implicit grant type)
不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过"授权码"这个步骤。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
步骤如下:
- 客户端将用户导向认证服务器。
- 用户决定是否给于客户端授权。
- 若用户授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
- 浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
- 资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
- 浏览器执行上一步获得的脚本,提取出令牌。
- 浏览器将令牌发给客户端。
下面是上面这些步骤所需要的参数。
A步骤中,客户端发出的HTTP请求,包含以下参数:
- response_type:表示授权类型,此处的值固定为"token",必选项。
- client_id:表示客户端的ID,必选项。
- redirect_uri:表示重定向的URI,可选项。
- scope:表示权限范围,可选项。
- state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值
例子:
GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
C步骤中,认证服务器回应客户端的URI,包含以下参数:
- access_token:表示访问令牌,必选项。
- token_type:表示令牌类型,该值大小写不敏感,必选项。
- expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
- scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
- state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。
例子:
HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
&state=xyz&token_type=example&expires_in=3600
认证服务器用HTTP头信息的Location栏,指定浏览器重定向的网址。注意,在这个网址的Hash部分包含了令牌。
根据上面的D步骤,下一步浏览器会访问Location指定的网址,但是Hash部分不会发送。接下来的E步骤,服务提供商的资源服务器发送过来的代码,会提取出Hash中的令牌。
密码模式(Resource Owner Password Credentials Grant)
用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。
步骤如下:
- 用户向客户端提供用户名和密码。
- 客户端将用户名和密码发给认证服务器,向后者请求令牌。
- 认证服务器确认无误后,向客户端提供访问令牌。
B步骤中,客户端发出的HTTP请求,包含以下参数:
- grant_type:表示授权类型,此处的值固定为"password",必选项。
- username:表示用户名,必选项。
- password:表示用户的密码,必选项。
- scope:表示权限范围,可选项。
例子:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w
C步骤中,认证服务器向客户端发送访问令牌,例子:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
整个过程中,客户端不得保存用户的密码。
4.客户端模式(Client Credentials Grant)
指客户端以自己的名义,而不以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。
步骤如下:
- 客户端向认证服务器进行身份认证,并要求一个访问令牌。
- 认证服务器确认无误后,向客户端提供访问令牌。
A步骤中,客户端发出的HTTP请求,包含以下参数:
- granttype:表示授权类型,此处的值固定为"clientcredentials",必选项。
- scope:表示权限范围,可选项。
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
认证服务器必须以某种方式,验证客户端身份。
B步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}
5.扩展模式(Extension)
扩展模式,是一种自定义模式。规范中仅对“grant type”参数提出了须为URI的要求。对于其他申请数据,可以根据需求进行自定义。
附规范例子。
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Asaml2-
bearer&assertion=PEFzc2VydGlvbiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDU
[...omitted for brevity...]aG5TdGF0ZW1lbnQ-PC9Bc3NlcnRpb24-
更新令牌
如果用户访问的时候,客户端的"访问令牌"已经过期,则需要使用"更新令牌"申请一个新的访问令牌。
客户端发出更新令牌的HTTP请求,包含以下参数:
- granttype:表示使用的授权模式,此处的值固定为"refreshtoken",必选项。
- refresh_token:表示早前收到的更新令牌,必选项。
- scope:表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致。
例子:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
Authorization Endpoint, 授权端点
Token EndPoint, token端点
Scope范围
代表资源所有者在被保护资源那里的一些权限
Access Token
- 有时候叫做token
- 用来访问被保护资源的凭据
- 代表了给客户端颁发的授权,也就是委托给客户端的权限
Refresh Token
- 用来获取Access Token的凭据
- 由Authorization Server颁发给客户端应用的
- 具备客户端应用主键降低访问权限的能力
授权服务器发生错误时
- error
- error_description
- error_uri
- state
Authorization Endpoint
https://xxx.xxx.xx//authorize?error=invalid request
Token Endpoint
HTTP/1.1 400 Bad Request
Content-Type:application/json
{
"error":"invalid_grant",
"error_description":"bad request:invalid or expired authoriztion code"
}
错误类型
- invalid_request
- invalid_client(401)
- invalid_grant
- unauthorized_client
- unsupported_grant_type
- invalid_scope