博客与笔记没法实时同步, 笔记最新连接
定义:OAuth 2.0是一个开放受权标准:容许资源全部者(用户)受权第三方应用访问该用户在某服务上的特定私有资源,但不提供帐号密码给第三方应用。
安全提示:受权码和全部令牌必须经过Tsl加密传输。进入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 }
定义:它在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议浏览器
+--------+ +--------+ | | | | | |---------(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。
"response_type"参数值 | OIDC受权类型 |
---|---|
code | Authorization Code Flow |
id_token token | Implicit Flow |
code id_token | Hybrid Flow |
定义: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"
1 RSA算法特色: 1.1 公钥私钥对等,能够互换 1.2 私钥须要保存在可靠方 1.3 没法从公钥计算出私钥(理论上从私钥也没法计算出公钥,例外:openssl的公钥e固定为65537,且私钥文件中含有额外的参数,能够从私钥计算出公钥) 2 RSA应用 2.1 加密:消息发送方:公钥加密=>消息接收方:私钥解密(用于加密传递消息) ##缺点:没法防止伪造消息 2.2 签名:消息发送方:私钥加密=>消息接收方:公钥解密(用于消息签名,防止消息被篡改) ##缺点:消息明文传输
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服务器
利用不可逆算法,确保在被窃取了受权码或其余密匙的状况下,也没法向受权服务器换取访问令牌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对比,若是一致,颁发访问令牌
#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
#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