zoukankan      html  css  js  c++  java
  • 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后端保存。

         +----------+
         | 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)用户代理(浏览器)将响应参数传递给客户端
    

    资源所有者密码模式

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

    
         +----------+
         | 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)

    利用不可逆算法,确保在被窃取了授权码或其他密匙的情况下,也无法向授权服务器换取访问令牌

    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
    

    资源所有者密码模式

  • 相关阅读:
    微信扫码
    vue h5公众号支付
    vue h5支付宝支付
    vue PDF预览
    vue 中AES加密
    vue 动态路由配置
    移动端调试工具
    Ajax工作原理
    yahoo军规
    Flex 布局教程
  • 原文地址:https://www.cnblogs.com/Kane-Blake/p/11096322.html
Copyright © 2011-2022 走看看