[{"data":1,"prerenderedAt":479},["ShallowReactive",2],{"content:\u002F2021\u002Foauth2":3,"surround:\u002F2021\u002Foauth2":468},{"id":4,"title":5,"body":6,"categories":438,"date":440,"description":16,"draft":441,"extension":442,"image":443,"meta":444,"navigation":446,"path":447,"permalink":443,"published":443,"readingTime":448,"recommend":443,"references":453,"seo":460,"sitemap":461,"stem":462,"tags":463,"type":466,"updated":443,"__hash__":467},"content\u002Fposts\u002F2021\u002Foauth2.md","第三方应用授权：OAuth 2.0",{"type":7,"value":8,"toc":432},"minimark",[9,13,17,20,23,26,29,42,49,53,56,71,77,82,85,103,106,111,122,136,145,151,161,167,190,199,207,213,217,240,245,251,260,268,274,279,284,288,295,300,306,322,327,333,337,344,349,355,369,374,379,382,385,392,398,401,404,407,413,429],[10,11,12],"h1",{"id":12},"介绍",[14,15,16],"p",{},"OAuth 2.0 是目前最流行的授权机制，用来授权第三方应用，获取用户数据。",[14,18,19],{},"简单说，OAuth 就是一种授权机制。数据的所有者告诉系统，同意授权第三方应用进入系统，获取这些数据。系统从而产生一个短期的进入令牌（access_token），用来代替密码，供第三方应用使用。",[14,21,22],{},"OAuth 2.0 的优点：保证了令牌既可以让第三方应用获得权限，同时又随时可控，不会危及系统安全",[10,24,25],{"id":25},"令牌与密码的区别",[14,27,28],{},"令牌（token）与密码（password）的作用是一样的，都可以进入系统，但是有三点差异。",[30,31,32,36,39],"ol",{},[33,34,35],"li",{},"令牌是短期的，到期会自动失效，用户自己无法修改。密码一般长期有效，用户不修改，就不会发生变化。",[33,37,38],{},"令牌可以被数据所有者撤销，会立即失效。密码一般不允许被他人撤销。",[33,40,41],{},"令牌有权限范围（scope）。对于网络服务来说，只读令牌就比读写令牌更安全。密码一般是完整权限。",[43,44,46],"alert",{"type":45},"warning",[14,47,48],{},"只要知道了令牌，就能进入系统。系统一般不会再次确认身份，所以令牌必须保密，泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期，一般都设置得很短的原因。",[10,50,52],{"id":51},"四种授权类型authorization-grant","四种授权类型（authorization grant）",[14,54,55],{},"即四种颁发令牌的方式：",[57,58,59,62,65,68],"ul",{},[33,60,61],{},"授权码（authorization-code）",[33,63,64],{},"隐藏式（implicit）",[33,66,67],{},"密码式（password）：",[33,69,70],{},"客户端凭证（client credentials）",[43,72,74],{"type":73},"info",[14,75,76],{},"不管哪一种授权方式，第三方应用申请令牌之前，都必须先到系统备案，说明自己的身份，然后会拿到两个身份识别码：客户端 ID（client ID）和客户端密钥（client secret）。这是为了防止令牌被滥用，没有备案过的第三方应用，是不会拿到令牌的。",[78,79,81],"h2",{"id":80},"第一种授权方式授权码","第一种授权方式：授权码",[14,83,84],{},"授权码（authorization code）方式，指的是第三方应用先申请一个授权码，然后再用该码获取令牌。",[43,86,87],{"type":73},[14,88,89,90,94,95,98,99,102],{},"这种方式是",[91,92,93],"strong",{},"最常用的流程","，安全性也最高，它适用于那些",[91,96,97],{},"有后端的 Web 应用","。授权码通过前端传送，",[91,100,101],{},"令牌则是储存在后端","，而且所有与资源服务器的通信都在后端完成。这样的前后端分离，可以避免令牌泄漏。",[14,104,105],{},"举例说明：",[30,107,108],{},[33,109,110],{},"A 网站提供一个链接，用户点击后就会跳转到 B 网站，授权用户数据给 A 网站使用",[112,113,118],"pre",{"className":114,"code":116,"language":117},[115],"language-text","https:\u002F\u002Fb.com\u002Foauth\u002Fauthorize?\n  response_type=code&\n  client_id=CLIENT_ID&\n  redirect_uri=CALLBACK_URL&\n  scope=read\n","text",[119,120,116],"code",{"__ignoreMap":121},"",[14,123,124,127,128,131,132,135],{},[119,125,126],{"code":126},"response_type","参数表示要求返回授权码（code），",[119,129,130],{"code":130},"client_id","参数让 B 知道是谁在请求，",[119,133,134],{"code":134},"redirect_uri","参数是 B 接受或拒绝请求后的跳转网址，scope参数表示要求的授权范围（这里是只读）。",[30,137,139],{"start":138},2,[33,140,141,142,144],{},"用户跳转后，B 网站会要求用户登录，然后询问是否同意给予 A 网站授权。用户表示同意，这时 B 网站就会跳回",[119,143,134],{"code":134},"参数指定的网址。跳转时，会传回一个授权码",[112,146,149],{"className":147,"code":148,"language":117},[115],"https:\u002F\u002Fa.com\u002Fcallback?code=AUTHORIZATION_CODE\n",[119,150,148],{"__ignoreMap":121},[30,152,154],{"start":153},3,[33,155,156,157,160],{},"A 网站拿到授权码以后，就可以",[91,158,159],{},"在后端","，向 B 网站请求令牌",[112,162,165],{"className":163,"code":164,"language":117},[115],"https:\u002F\u002Fb.com\u002Foauth\u002Ftoken?\n client_id=CLIENT_ID&\n client_secret=CLIENT_SECRET&\n grant_type=authorization_code&\n code=AUTHORIZATION_CODE&\n redirect_uri=CALLBACK_URL\n",[119,166,164],{"__ignoreMap":121},[14,168,169,171,172,175,176,178,179,182,183,186,187,189],{},[119,170,130],{"code":130},"参数和",[119,173,174],{"code":174},"client_secret","参数用来让 B 确认 A 的身份（",[119,177,174],{"code":174},"参数是保密的，因此只能在后端发请求），",[119,180,181],{"code":181},"grant_type","参数的值是",[119,184,185],{"code":185},"AUTHORIZATION_CODE","，表示采用的授权方式是授权码，code参数是上一步拿到的授权码，",[119,188,134],{"code":134},"参数是令牌颁发后的回调网址。",[30,191,193],{"start":192},4,[33,194,195,196,198],{},"B 网站收到请求以后，就会颁发令牌。具体做法是向",[119,197,134],{"code":134},"指定的网址，发送一段 JSON 数据",[112,200,205],{"className":201,"code":203,"language":204,"meta":121},[202],"language-json","{    \n  \"access_token\":\"ACCESS_TOKEN\",\n  \"token_type\":\"bearer\",\n  \"expires_in\":2592000,\n  \"refresh_token\":\"REFRESH_TOKEN\",\n  \"scope\":\"read\",\n  \"uid\":100101,\n  \"info\":{...}\n}\n","json",[119,206,203],{"__ignoreMap":121},[14,208,209,212],{},[119,210,211],{"code":211},"access_token","字段就是令牌，A 网站在后端拿到了。",[78,214,216],{"id":215},"第二种方式隐藏式","第二种方式：隐藏式",[14,218,219,220,223,224,227,228,235,236,239],{},"有些 Web 应用是",[91,221,222],{},"纯前端应用","，没有后端。这时就不能用上面的方式了，必须",[91,225,226],{},"将令牌储存在前端","。",[229,230,234],"a",{"href":231,"rel":232},"https:\u002F\u002Fdatatracker.ietf.org\u002Fdoc\u002Fhtml\u002Frfc6749",[233],"nofollow","RFC 6749"," 就规定了第二种方式，允许直接向前端颁发令牌。这种方式",[91,237,238],{},"没有授权码这个中间步骤","，所以称为（授权码）\"隐藏式\"（implicit）。",[30,241,242],{},[33,243,244],{},"A 网站提供一个链接，要求用户跳转到 B 网站，授权用户数据给 A 网站使用",[112,246,249],{"className":247,"code":248,"language":117},[115],"https:\u002F\u002Fb.com\u002Foauth\u002Fauthorize?\n  response_type=token&\n  client_id=CLIENT_ID&\n  redirect_uri=CALLBACK_URL&\n  scope=read\n",[119,250,248],{"__ignoreMap":121},[14,252,253,255,256,259],{},[119,254,126],{"code":126},"参数为",[119,257,258],{"code":258},"token","，表示要求直接返回令牌。",[30,261,262],{"start":138},[33,263,264,265,267],{},"用户跳转到 B 网站，登录后同意给予 A 网站授权。这时，B 网站就会跳回",[119,266,134],{"code":134},"参数指定的跳转网址，并且把令牌作为 URL 参数，传给 A 网站。",[112,269,272],{"className":270,"code":271,"language":117},[115],"https:\u002F\u002Fa.com\u002Fcallback#token=ACCESS_TOKEN\n",[119,273,271],{"__ignoreMap":121},[14,275,276,278],{},[119,277,258],{"code":258},"参数就是令牌，A 网站因此直接在前端拿到令牌。",[43,280,281],{"type":73},[14,282,283],{},"注意，令牌的位置是 URL 锚点（fragment），而不是查询字符串（querystring），这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议，因此存在\"中间人攻击\"的风险，而浏览器跳转时，锚点不会发到服务器，就减少了泄漏令牌的风险。\n另外，这种方式把令牌直接传给前端，是很不安全的。因此，只能用于一些安全要求不高的场景，并且令牌的有效期必须非常短，通常就是会话期间（session）有效，浏览器关掉，令牌就失效了。",[78,285,287],{"id":286},"第三种方式密码式","第三种方式：密码式",[14,289,290,291,294],{},"如果你高度信任某个应用，",[229,292,234],{"href":231,"rel":293},[233]," 也允许用户把用户名和密码，直接告诉该应用。该应用就使用你的密码，申请令牌，这种方式称为\"密码式\"（password）。",[30,296,297],{},[33,298,299],{},"A 网站要求用户提供 B 网站的用户名和密码。拿到以后，A 就直接向 B 请求令牌。",[112,301,304],{"className":302,"code":303,"language":117},[115],"https:\u002F\u002Foauth.b.com\u002Ftoken?\n  grant_type=password&\n  username=USERNAME&\n  password=PASSWORD&\n  client_id=CLIENT_ID\n",[119,305,303],{"__ignoreMap":121},[14,307,308,310,311,314,315,318,319,321],{},[119,309,181],{"code":181},"参数是授权方式，这里的",[119,312,313],{"code":313},"password","表示\"密码式\"，",[119,316,317],{"code":317},"username","和",[119,320,313],{"code":313},"是 B 的用户名和密码。",[30,323,324],{"start":138},[33,325,326],{},"B 网站验证身份通过后，直接给出令牌。注意，这时不需要跳转，而是把令牌放在 JSON 数据里面，作为 HTTP 响应，A 因此拿到令牌。",[43,328,330],{"type":329},"error",[14,331,332],{},"这种方式需要用户给出自己的用户名\u002F密码，显然风险很大，因此只适用于其他授权方式都无法采用的情况，而且必须是用户高度信任的应用。",[78,334,336],{"id":335},"第四种方式凭证式","第四种方式：凭证式",[14,338,339,340,343],{},"最后一种方式是凭证式（client credentials），适用于",[91,341,342],{},"没有前端的命令行应用","，即在命令行下请求令牌。",[30,345,346],{},[33,347,348],{},"A 应用在命令行向 B 发出请求。",[112,350,353],{"className":351,"code":352,"language":117},[115],"https:\u002F\u002Foauth.b.com\u002Ftoken?\n  grant_type=client_credentials&\n  client_id=CLIENT_ID&\n  client_secret=CLIENT_SECRET\n",[119,354,352],{"__ignoreMap":121},[14,356,357,359,360,363,364,318,366,368],{},[119,358,181],{"code":181},"参数等于",[119,361,362],{"code":362},"client_credentials","表示采用凭证式，",[119,365,130],{"code":130},[119,367,174],{"code":174},"用来让 B 确认 A 的身份。",[30,370,371],{"start":138},[33,372,373],{},"B 网站验证通过以后，直接返回令牌",[43,375,376],{"type":73},[14,377,378],{},"这种方式给出的令牌，是针对第三方应用的，而不是针对用户的，即有可能多个用户共享同一个令牌。",[10,380,381],{"id":381},"令牌的使用",[14,383,384],{},"A 网站拿到令牌以后，就可以向 B 网站的 API 请求数据了。",[14,386,387,388,391],{},"此时，每个发到 API 的请求，都必须带有令牌。具体做法是在请求的头信息，加上一个",[119,389,390],{"code":390},"Authorization","字段，令牌就放在这个字段里面。",[112,393,396],{"className":394,"code":395,"language":117},[115],"curl -H \"Authorization: Bearer ACCESS_TOKEN\" \\\n\"https:\u002F\u002Fapi.b.com\"\n",[119,397,395],{"__ignoreMap":121},[10,399,400],{"id":400},"更新令牌",[14,402,403],{},"令牌的有效期到了，如果让用户重新走一遍上面的流程，再申请一个新的令牌，很可能体验不好，而且也没有必要。OAuth 2.0 允许用户自动更新令牌。",[14,405,406],{},"具体方法是，B 网站颁发令牌的时候，一次性颁发两个令牌，一个用于获取数据，另一个用于获取新的令牌（refresh token 字段）。令牌到期前，用户使用 refresh token 发一个请求，去更新令牌。",[112,408,411],{"className":409,"code":410,"language":117},[115],"https:\u002F\u002Fb.com\u002Foauth\u002Ftoken?\n  grant_type=refresh_token&\n  client_id=CLIENT_ID&\n  client_secret=CLIENT_SECRET&\n  refresh_token=REFRESH_TOKEN\n",[119,412,410],{"__ignoreMap":121},[14,414,415,255,417,420,421,171,423,425,426,428],{},[119,416,181],{"code":181},[119,418,419],{"code":419},"refresh_token","表示要求更新令牌，",[119,422,130],{"code":130},[119,424,174],{"code":174},"参数用于确认身份，",[119,427,419],{"code":419},"参数就是用于更新令牌的令牌。",[14,430,431],{},"B 网站验证通过以后，就会颁发新的令牌。",{"title":121,"searchDepth":192,"depth":192,"links":433},[434,435,436,437],{"id":80,"depth":138,"text":81},{"id":215,"depth":138,"text":216},{"id":286,"depth":138,"text":287},{"id":335,"depth":138,"text":336},[439],"学习笔记","2021-06-21",false,"md",null,{"comments":441,"slots":445},{},true,"\u002F2021\u002Foauth2",{"text":449,"minutes":450,"time":451,"words":452},"10 min read",9.68,580800,1936,[454,457],{"title":455,"link":456},"OAuth 2.0 的一个简单解释","https:\u002F\u002Fwww.ruanyifeng.com\u002Fblog\u002F2019\u002F04\u002Foauth_design.html",{"title":458,"link":459},"OAuth 2.0 的四种方式","https:\u002F\u002Fwww.ruanyifeng.com\u002Fblog\u002F2019\u002F04\u002Foauth-grant-types.html",{"title":5,"description":16},{"loc":447},"posts\u002F2021\u002Foauth2",[464,465],"OAuth 2.0","第三方应用授权","tech","ifqjiqM6jnI6dmSFhbuGTrOQWNaDnD7224EtdAU6_tc",[469,474],{"title":470,"path":471,"stem":472,"date":473,"type":466,"children":-1},"npm & package.json","\u002F2021\u002Fnpm-package-json","posts\u002F2021\u002Fnpm-package-json","2021-06-17",{"title":475,"path":476,"stem":477,"date":478,"type":466,"children":-1},"JavaScript Snippets","\u002F2021\u002Fjavascript-snippets","posts\u002F2021\u002Fjavascript-snippets","2021-07-23",1793699839951]