OAuth2认证(记忆法则:3次302)
- 用户请求登录(第1次302):
- 用户在你的应用中点击“使用GitHub登录”,本例为:**/login**
- Vert.x 服务器(通过
OAuth2AuthHandler
)响应用户的浏览器,发送一个 HTTP 302 重定向响应,其location为OAuth2服务器地址。
Location
Header 指向 GitHub 的授权页面,并附带 client_id
和 redirect_uri
(http://localhost:8888/callback
) 等参数。
- 用户在GitHub授权:
- 用户的浏览器跳转到 GitHub 网站。
- 用户输入用户名密码,并点击“授权”,同意让你的应用访问其信息。
- GitHub重定向回你的应用(第2次302跳转):
- 浏览器发起回调请求:
- 用户的浏览器收到这个 302 响应后,会立即根据
Location
Header 的指示,向 http://localhost:8888/callback?code=...
发起一个新的GET请求。
- 所以,最终是用户的浏览器,而不是 GitHub 的服务器,向你的 Vert.x 应用发起了
/callback
请求。
- 你的服务器处理回调(第3次302):
- 你的 Vert.x 应用收到来自浏览器的
/callback
请求。
- 后续流程就和我们之前讨论的一样了:
Router
将这个请求交给了 oauth2Handler
设置的内部处理器。
- 这个处理器从 URL 中提取
code
,然后在后台(服务器到服务器的通信)向 GitHub 的 API 发送请求,用 code
换取最终的 access_token
- 向用户的浏览器发送一个 HTTP 302 重定向响应,location为:/login(也即第1次请求的路径)。
- 浏览器继续发起登录请求
流程图
sequenceDiagram
%% 时序图例子,-> 直线,-->虚线,->>实线箭头
participant 浏览器
participant OAuth2AuthHandlerImpl
participant OAuth2AuthProviderImpl
participant OAuth2API
participant GitHub
Note over OAuth2AuthHandlerImpl,OAuth2API: Vert.x OAuth2框架
浏览器 ->> OAuth2AuthHandlerImpl: /login
Note over 浏览器,OAuth2AuthHandlerImpl: http://localhost:8888/login
loop ctx.user()为空
OAuth2AuthHandlerImpl -->> OAuth2AuthHandlerImpl: HttpException(302, redirect_url)
OAuth2AuthHandlerImpl -->> OAuth2AuthHandlerImpl: session.put("state", wj9awH9D)
OAuth2AuthHandlerImpl ->> 浏览器: http status: 302
Note over 浏览器,OAuth2AuthHandlerImpl: location:<br/>https://github.com/login/oauth/authorize?<br/>redirect_uri=http://localhost:8888/callback&<br/>state=wj9awH9D&response_type=code&<br/>client_id=xxx
end
浏览器 ->> GitHub: https://github.com/login/oauth/authorize?redirect_uri=http://localhost:8888/callback&state=wj9awH9D&response_type=code&client_id=xxx
Note over 浏览器,GitHub: 用户在GitHub登录并授权
GitHub ->> 浏览器: http status: 302
Note over 浏览器,GitHub: http://localhost:8888/callback?code=172376f83cd7cd78f6d3&state=wj9awH9D
浏览器 ->> OAuth2AuthHandlerImpl: /callback
Note over 浏览器,OAuth2AuthHandlerImpl: http://localhost:8888/callback?<br/>code=172376f83cd7cd78f6d3&<br/>state=wj9awH9D
OAuth2AuthHandlerImpl ->> OAuth2AuthHandlerImpl: callback.handler
OAuth2AuthHandlerImpl -->> OAuth2AuthHandlerImpl: check state && session.remove("state")
Note over OAuth2AuthHandlerImpl,OAuth2AuthHandlerImpl: 防止重放攻击
OAuth2AuthHandlerImpl ->> OAuth2AuthProviderImpl: authProvider.authenticate
OAuth2AuthProviderImpl ->> OAuth2API: api.token(grantType, params)
OAuth2API ->> GitHub: POST /login/oauth/access_token
Note over OAuth2API,GitHub: header<br/>User-Agent : vertx-auth-oauth2<br/>Accept : application/json,...<br/>Content-Type : application/x-www-form-urlencoded
GitHub ->> OAuth2AuthHandlerImpl: response
Note over GitHub, OAuth2AuthHandlerImpl: {"access_token" :"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",<br/> "token_type" : "bearer",<br/> "scope" : "user:email"}
OAuth2AuthHandlerImpl ->> OAuth2AuthHandlerImpl: session.regenerateId()
OAuth2AuthHandlerImpl ->> 浏览器: http status 302
Note over OAuth2AuthHandlerImpl, 浏览器: cache-control : no-cache, no-store, must-revalidate<br/>expires : 0<br/>location : /login<br/>set-cookie : vertx-web.session=dfdxxxx950bfe1ac580
loop ctx.user()不为空
浏览器 ->> OAuth2AuthHandlerImpl: /login
Note over 浏览器,OAuth2AuthHandlerImpl: vertx-web.session : dfdxxxx950bfe1ac580
OAuth2AuthHandlerImpl ->> OAuth2AuthHandlerImpl: postAuthentication(ctx)
OAuth2AuthHandlerImpl ->> OAuth2AuthHandlerImpl: 检查用户的scope是否满足路由的scope
OAuth2AuthHandlerImpl ->> OAuth2AuthHandlerImpl: ctx.next()继续流转到用户路由
end