IdentityServer4笔记整理(更新中)

博客与笔记没法实时同步, 笔记最新连接

OAuth 2.0

定义:OAuth 2.0是一个开放受权标准:容许资源全部者(用户)受权第三方应用访问该用户在某服务上的特定私有资源,但不提供帐号密码给第三方应用。
安全提示:受权码和全部令牌必须经过Tsl加密传输。进入OAuth 2.0官网前端

OAuth 2.0协议流程图

     +--------+                               +---------------+
     |        |--(A)- Authorization Request ->|   Resource    |
     |        |                               |     Owner     |
     |        |<-(B)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(C)-- Authorization Grant -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+
                         (OAuth 2.0协议流程)
                         
    (A)客户端发起受权请求,客户端能够向资源全部者直接发起受权请求,建议的作法是将受权服务器做为中间人,客户端向受权服务器发起受权请求
    (B)客户端收到受权凭证(和具体的受权类型有关)
    (C)客户端经过受权凭证向受权服务器请求访问令牌
    (D)受权服务器验证受权凭证和客户端,若是经过验证,返回给客户端访问令牌
    (E)客户端经过访问令牌向资源服务器发出访问请求
    (F)资源服务器验证访问令牌有效后,返回请求的资源

受权码模式

受权码模式:code的生命周期必须短暂、或者只能单次使用,若是code被屡次使用,受权服务器必须使该code生成的全部令牌失效。访问令牌由client后端保存。web

     +----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier      +---------------+
     |         -+----(A)-- & Redirection URI ---->|               |
     |  User-   |                                 | Authorization |
     |  Agent  -+----(B)-- User authenticates --->|     Server    |
     |          |                                 |               |
     |         -+----(C)-- Authorization Code ---<|               |
     +-|----|---+                                 +---------------+
       |    |                                         ^      v
      (A)  (C)                                        |      |
       |    |                                         |      |
       ^    v                                         |      |
     +---------+                                      |      |
     |         |>---(D)-- Authorization Code ---------'      |
     |  Client |          & Redirection URI                  |
     |         |                                             |
     |         |<---(E)----- Access Token -------------------'
     +---------+       (w/ Optional Refresh Token)
     
    (A)客户端引导用户代理(浏览器)到达受权终结点,并携带参数:response_type(code)、client_id、redirect_uri、scope、state
    (B)受权服务器经过用户代理(浏览器)验证资源全部者,并肯定资源全部者是否给予客户端受权
    (C)验证资源全部者成功,而且容许受权,受权服务器将用户代理(浏览器)重定向到redirect_uri,并返回code和受权请求的state
    (D)客户端向受权服务器令牌终结点请求令牌,携带参数:grant_type、code、redirect_uri、client_id、secret(可选)
    (E)受权服务器验证code、client_id、secret是否有效,并验证redirect_uri是否与步骤C中一致,若是验证成功,携带Access Token将用户代理(浏览器)重定向到redirect_uri

简化模式

要点:不支持刷新令牌,访问令牌编码在Url中,因此会有暴露的风险(在Oauth2.0官网中已经不推荐使用)算法

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier     +---------------+
     |         -+----(A)-- & Redirection URI --->|               |
     |  User-   |                                | Authorization |
     |  Agent  -|----(B)-- User authenticates -->|     Server    |
     |          |                                |               |
     |          |<---(C)--- Redirection URI ----<|               |
     |          |          with Access Token     +---------------+
     |          |            in Fragment
     |          |                                +---------------+
     |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
     |          |          without Fragment      |     Client    |
     |          |                                |    Resource   |
     |     (F)  |<---(E)------- Script ---------<|               |
     |          |                                +---------------+
     +-|--------+
       |    |
      (A)  (G) Access Token
       |    |
       ^    v
     +---------+
     |         |
     |  Client |
     |         |
     +---------+
    (A)客户端引导用户代理(浏览器)到达受权终结点,并携带参数:response_type(token)、client_id、redirect_uri、scope、state
    (B)受权服务器经过用户代理(浏览器)验证资源全部者,并肯定资源全部者是否给予客户端受权
    (C)验证资源全部者成功,而且容许受权,受权服务器将用户代理(浏览器)重定向到redirect_uri,redirect_uri的URL 锚点(fragment)部分包括了响应参数(以#hash值的方式追加):access_token、token_type、expires_in、scope、state
    (D)用户代理(浏览器)向redirect_uri发出请求,但不包括URL 锚点,用户代理在本地保存了fragment。
    (E)redirect_uri的服务器返回页面给浏览器,该页面须要包含解码fragment的脚本。
    (F)浏览器接收到页面后,执行脚本,将C中的响应参数解码出来
    (G)用户代理(浏览器)将响应参数传递给客户端

资源全部者密码模式

用户将用户名及密码提供给可信任的第三方应用,第三方应用向令牌终结点请求令牌。经常使用于第一方应用进行登录. 优势:应用不须要存储用户帐号、密码,经过刷新令牌保持持久登录,下降密码泄露的风险。json

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          v
          |    Resource Owner
         (A) Password Credentials
          |
          v
     +---------+                                  +---------------+
     |         |>--(B)---- Resource Owner ------->|               |
     |         |         Password Credentials     | Authorization |
     | Client  |                                  |     Server    |
     |         |<--(C)---- Access Token ---------<|               |
     |         |    (w/ Optional Refresh Token)   |               |
     +---------+                                  +---------------+

            Figure 5: Resource Owner Password Credentials Flow

   (A)  资源全部者向客户端提供其用户名及密码
   (B)  客户端携带用户名、密码向受权服务器的令牌终结点发起请求
   (C)  受权服务器受权客户端,并验证用户名、密码是否有效。若是有效,返回访问令牌
#请求token
Request:
    POST /connect/token HTTP/1.1
    Host: idsrv-server.com
    Content-type: application/x-www-form-urlencoded
    body:
    {
        grant_type:password
        username:dd
        password:dd
        client_id:eshopOnVue
        scope:orders(可选参数)
    }

#请求刷新令牌:原刷新令牌失效、以前颁发的access_token不受影响(须要实现手动失效)
Request:
    POST /connect/token HTTP/1.1
    Host: idsrv-server.com
    Content-type: application/x-www-form-urlencoded
    body:
    {
        grant_type:refresh_token
        refresh_token:e4364377ec69c8d5c06a49d7b74efbd2a29015ac37e9ede8e17597d348931d32
        client_id:eshopOnVue
    }
Respose:
{
    "id_token": "eyJhbGciO.iJSUzI1NiI.sImtpZCw",
    "access_token": "eyJhb.GciOiJSUz.I1NiIsIm",
    "expires_in": 3600,
    "token_type": "Bearer",
    "refresh_token": "60e7dda6e30473ce6dc0a1656b38c174a74ef73310d"
}
#经过access_token请求用户终结点(须要scope:profile):/connect/userinfo

客户端凭证模式

客户端直接使用自身的凭证向受权服务器终结点请求访问令牌。只能用于可信的客户端。不支持刷新令牌、没法访问用户资源scope(openid、profile、email等)后端

     +---------+                                  +---------------+
     |         |                                  |               |
     |         |>--(A)- Client Authentication --->| Authorization |
     | Client  |                                  |     Server    |
     |         |<--(B)---- Access Token ---------<|               |
     |         |                                  |               |
     +---------+                                  +---------------+

   (A)  客户端向受权服务器令牌终结点请求访问令牌
   (B)  受权服务器对客户端进行认证,若是成功,返回访问令牌

Request:
    POST /connect/token HTTP/1.1  #请求方式只能为post
    Host: idsrv-server.com
    Content-type: application/x-www-form-urlencoded #参数只能放在body里面
    body:
    {
        grant_type:client_credentials
        client_id:ClientCredentials
        client_secret:iwiaXNzIjoibnVsbCIsImF1ZCI6WyJudWxsL3Jlc291cmNlcyIsIm9yZGVycyJdLCJjbGllbnRfaWQiOiJDb
        scope:orders openid(可选,默认请求全部scope)
    }
Response:
    HTTP/1.1 200 OK
    Content-Type: application/json
    Cache-Control: no-store
    Pragma: no-cache
     
    {
      "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
      "token_type":"bearer",
      "expires_in":3600
    }

OpenID Connect(OIDC)

定义:它在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议浏览器

image

OIDC协议流程图

+--------+                                   +--------+
|        |                                   |        |
|        |---------(1) AuthN Request-------->|        |
|        |                                   |        |
|        |  +--------+                       |        |
|        |  |        |                       |        |
|        |  |  End-  |<--(2) AuthN & AuthZ-->|        |
|        |  |  User  |                       |        |
|   RP   |  |        |                       |   OP   |
|        |  +--------+                       |        |
|        |                                   |        |
|        |<--------(3) AuthN Response--------|        |
|        |                                   |        |
|        |---------(4) UserInfo Request----->|        |
|        |                                   |        |
|        |<--------(5) UserInfo Response-----|        |
|        |                                   |        |
+--------+                                   +--------+
EU:End User:一我的类用户。
RP:Relying Party ,用来代指OAuth2中的受信任的客户端,身份认证和受权信息的消费方;
OP:OpenID Provider,有能力提供EU认证的服务(好比OAuth2中的受权服务),用来为RP提供EU的身份认证信息;
ID Token:JWT格式的数据,包含EU身份认证的信息。
UserInfo Endpoint:用户信息接口(受OAuth2保护),当RP使用Access Token访问时,返回受权用户的信息,此接口必须使用HTTPS。
    (1).RP发送一个认证请求给OP;
    (2).OP对EU进行身份认证,而后提供受权;
    (3).OP把ID Token和Access Token(须要的话)返回给RP;
    (4).RP使用Access Token发送一个请求到UserInfo EndPoint;
    (5).UserInfo EndPoint返回EU的Claims。

OIDC在OAuth之上的扩展

  • [ ] Scope:openid(用来区分这是一个OIDC的Authentication请求,而不是OAuth2的Authorization请求)
  • [ ] response_type:id_token,在implicit模式下请求id_token
  • [ ] id_token:身份令牌,是一个受权服务器提供的包含用户信息(由一组Cliams构成以及其余辅助的Cliams)的JWT格式的数据结构
  • [ ] UserInfo Endpoint:经过id_token从用户信息终结点得到一组EU相关的Claims,好比能够将Claims从token移除,避免token过大,只保留sub,经过UserInfo Endpoint查询Claims
"response_type"参数值 OIDC受权类型
code Authorization Code Flow
id_token token Implicit Flow
code id_token Hybrid Flow
  • Authorization Code Flow:从受权终结点返回code,从令牌终结点返回token
  • Implicit Flow:从受权终结点返回全部token
  • Hybrid Flow:id_token返回给前端,access_token在后端保存(access_token的安全性要求比id_token)高

JSON Web Token

定义:JWT是一个定义一种紧凑的,自包含的而且提供防篡改机制的传递数据的方式的标准协议安全

JWT格式组成

JWT由3部分构成:header.payload.signature
在IdSrv中,payload中的键值对根据token类型和受权流程的不一样有所区别
header:
{
  "alg": "RS256",//签名算法
  "kid": "9dcf733a1192a6da053e64c6ee22ff87",
  "typ": "JWT"//token类型
}
payload://须要传递的数据
{
  "nbf": 1556591630,//该jwt在此以前无效
  "exp": 1556595230,//该jwt在此以后无效
  "iss": "http://localhost:7102",//jwt颁发者
  "iat": 1516239022,//jwt颁发时间
  "aud": "http://localhost:7102/resources",//jwt接收者
  "client_id": "jsImplicit",
  "sub": "SubjectId",//用户惟一id
  "auth_time": 1556591629,//受权时间
  "idp": "local",//identityProvider
  "name": "Username",
  "scope": [
    "openid",
    "profile"
  ],
  "amr": [
    "pwd"//authenticationMethod
  ]
}
signature://token生成方使用私匙生成token,token消费方使用用公匙验证token是否被修改过
RSASHA256(
  base64UrlEncode(header) + "." +base64UrlEncode(payload),Public Key,Private Key
  )
#RSA签名过程
#1.将消息内容进行base64编码:base64UrlEncode(header) + "." +base64UrlEncode(payload)
#2.对编码后的内容进行SHA256哈希计算(不可逆)
#3.对hash值使用privateKey加密,加密后内容做为签名
#4.token接收方经过publicKey对签名进行解密获得hash值,并将内容进行hash计算,比较两个hash值,确保消息未被篡改
expires_in="exp"-"nbf"

RSA非对称加密算法

1 RSA算法特色:
1.1 公钥私钥对等,能够互换
1.2 私钥须要保存在可靠方
1.3 没法从公钥计算出私钥(理论上从私钥也没法计算出公钥,例外:openssl的公钥e固定为65537,且私钥文件中含有额外的参数,能够从私钥计算出公钥)
2 RSA应用
2.1 加密:消息发送方:公钥加密=>消息接收方:私钥解密(用于加密传递消息) 
##缺点:没法防止伪造消息
2.2 签名:消息发送方:私钥加密=>消息接收方:公钥解密(用于消息签名,防止消息被篡改)
##缺点:消息明文传输

HTTPS简单流程

1.服务器经过非对称算法生成私钥公钥
2.服务方将公钥提供给相关机构(CA)
3.CA生成证书并颁发给服务器:
{
    证书发布机构CA
    证书有效期
    公钥
    证书全部者
    签名(签名方法与JWT相似,私钥属于CA)
    ...
}
4.服务器将证书发送给客户端:
{
    1.TCP三次握手
    2.创建tunnel
    3.client hello(包括SessionId,能够避免从新握手,并从新使用已有对话密钥)
    4.server hello
    5.发送Certificate给客户端
}
5.客户端经过系统中内置的CA公钥验证证书的合法性
6.客户端经过服务方公钥与服务器协商对称加密算法与密钥
7.进行对称加密通讯

参考:https://zhuanlan.zhihu.com/p/22142170 https://zhuanlan.zhihu.com/p/27395037服务器

IdentityServer4

受权码模式

PKCE(Proof Key for Code Exchange)

利用不可逆算法,确保在被窃取了受权码或其余密匙的状况下,也没法向受权服务器换取访问令牌session

PKCE流程:
1.客户端随机生成一串字符:
{
    code_verifier=base64url(RandomString),
    code_challenge=base64url(sha256(code_verifier)) 
}
2.客户端携带code_challenge向受权服务器发起受权请求
3.受权服务器对客户端进行认证成功后返回受权码,并保存code_challenge
4.客户端获取到受权码以后,携带code_verifier向令牌终结点发起请求,换取Access Token
5.受权服务器验证受权码,将code_verifier进行sha256计算并url编码后与code_challenge对比,若是一致,颁发访问令牌

参考:https://tonyxu.io/zh/posts/2018/oauth2-pkce-flow/数据结构

受权码模式流程

#Step 1 客户端向受权服务器受权终结点发起请求:
    GET /connect/authorize HTTP/1.1
    Host: Idsrv.com,
    Query String Parameters:
    {
        client_id: Swagger_UI
        redirect_uri: http://localhost:9528/Callback
        response_type: code
        scope: openid profile orders
        state: 668ae852a74f4923ad140d79d2f10fee
        code_challenge: i2CnOeIHTBZZrAsgzEZV3-KpMTb_OCvl05ydETjrqIc
        code_challenge_method: S256
    }
state:客户端随机生成的字符串,受权服务器在重定向到redirect_uri时会原样返回,客户端检查state是否相同,来防止CSRFF攻击
#step 2 受权服务器对客户端进行认证,认证成功,受权服务器返回302重定向并携带ReturnUrl参数:
Response:
{
    Status Code: 302 Found,
    Location: http://localhost:6102/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3DSwagger_UI%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A9528%252FCallback%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520orders%26state%3D668ae852a74f4923ad140d79d2f10fee%26code_challenge%3Di2CnOeIHTBZZrAsgzEZV3-KpMTb_OCvl05ydETjrqIc%26code_challenge_method%3DS256
}
#step 3 /Account/Login请求返回登录页面,并将ReturnUrl写入登录页面
#step 4 用户在登录页面发起登录请求:
参数:
{
    1.用户名
    2.密码
    3.ReturnUrl
}
#step 4 受权服务器验证用户名、密码成功,且ReturnUrl有效:
{
    1.经过HttpContext.SignInAsync为当前请求上下文颁发登录凭证:
    Set-Cookie:
    {
        idsrv.session:'8c9e9e80f92da2551c77dc6ab03c69ca,path=/'
        idsrv:'CfDJ8IOXoUULE4dDgZ02v48m533Xg,expires=Mon,08 Jul 2019 08:38:26 GMT, path=/,httponly'
    }
    2.发起重定向到ReturnUrl
}
#step 5 在 /connect/authorize/callback 终结点:
{
    受权服务器更新Cookie:idsrv,并重定向到redirect_uri,携带如下参数:
    {
        code: 21a9deaac4457e29f669a91bb36795c048aae5680b49ae3a1ffafa50aff0d169
        scope: openid profile orders
        state: 668ae852a74f4923ad140d79d2f10fee
        session_state: ZcDaWfAmzNGLsXS3-1ofTvNGryU-KxeurTpPLxP6oF0.89cd4e2083165f0701dbd2181ede5b7b(会话状态)
    }
}
#step 6 在redirect_uri向令牌终结点请求访问令牌:
POST /connect/token HTTP/1.1
Host: Idsrv.com,
Content-type: application/x-www-form-urlencoded
body:
{
    client_id: Swagger_UI
    code: 21a9deaac4457e29f669a91bb36795c048aae5680b49ae3a1ffafa50aff0d169
    redirect_uri: http://localhost:9528/Callback
    code_verifier: dde8c25afc8d42728225154fea0aa098556671d3344c423f9007f1895b47dbf2be2116c7c5f34720842383ec9a7e0a66
    grant_type: authorization_code
}
#step 7 受权服务器验证code和code_verifier,并颁发访问令牌

刷新访问令牌

配置new Oidc.UserManager()时 设置scope=offline_access,直接经过刷新令牌请求访问令牌。offline_access优先于silent_redirect_uri

经过Iframe页面进行静默刷新:
#step 1 经过Iframe页面向受权终结点发起请求:
    GET /connect/authorize HTTP/1.1
    Host: Idsrv.com,
    Cookie: 
    {
        idsrv.session=8c9e9e80f92da2551c77dc6ab03c69ca;
        idsrv=CfDJ8IOXoUULE4dDgZ02v48m53JzoKhJcuEwwXcdvHRYodIZ2nTuD
    }
    Query String Parameters:
    {
        client_id: Swagger_UI
        redirect_uri: http://localhost:9528/SilentCallback
        response_type: code
        scope: openid profile orders
        state: 54207b37bb644f90800d0993d5a5c210
        code_challenge: 8bOHunvRggEM9m3Hwb-8m24KIiRV9rPSbz0OOvTP7D0
        code_challenge_method: S256
        prompt: none
        id_token_hint: eyJhbGciOiJSUzI1NiIsImtp2UFyLw
    }
id_token_hint://为以前获取的id_token
prompt:none//用来指示受权服务器是否引导用户从新认证和赞成受权 
#step 2 重定向到redirect_uri,携带如下参数: 
{
    code: a956650cd653debe11989d225c2caa2619521b9176b444fbcc83d3a1663bf1ed
    scope: openid profile orders
    state: 54207b37bb644f90800d0993d5a5c210
    session_state: SGmLH8gIy6VAPtdlT6_zQtix_VM229bPkpY0OpwQ6fc.229a9d1e0be856145c035db0aa6033cc
}
#step 3 在redirect_uri页面执行new Oidc.UserManager().signinSilentCallback()
#step 4 向令牌终结点请求访问令牌
登出操做:
Js客户端mgr.signoutRedirect()
1.向IdSrv请求/connect/endsession 并携带如下两个参数
id_token_hint:
post_logout_redirect_uri: 登出回调地址
2.重定向到/Account/Logout 并携带生成的logoutId参数
3.在Logout里清除await HttpContext.SignOutAsync() 并跳转到post_logout_redirect_uri

资源全部者密码模式

相关文章
相关标签/搜索