Vert.x OAuth2认证

OAuth2认证(记忆法则:3次302)

  1. 用户请求登录(第1次302)
    • 用户在你的应用中点击“使用GitHub登录”,本例为:**/login**
    • Vert.x 服务器(通过 OAuth2AuthHandler)响应用户的浏览器,发送一个 HTTP 302 重定向响应,其location为OAuth2服务器地址。
    • Location Header 指向 GitHub 的授权页面,并附带 client_idredirect_uri (http://localhost:8888/callback) 等参数。
  2. 用户在GitHub授权
    • 用户的浏览器跳转到 GitHub 网站。
    • 用户输入用户名密码,并点击“授权”,同意让你的应用访问其信息。
  3. GitHub重定向回你的应用(第2次302跳转)
    • GitHub 的服务器在用户授权后,并不会直接调用你的服务器。
    • 它会向用户的浏览器发送一个 HTTP 302 重定向响应。
    • 这个响应的 Location Header 就是你在第一步中提供的回调URL,并且附加上了授权码 code 和一个 state 值,例如:http://localhost:8888/callback?code=ABCDEFG&state=HIJKLMNOP
  4. 浏览器发起回调请求
    • 用户的浏览器收到这个 302 响应后,会立即根据 Location Header 的指示,向 http://localhost:8888/callback?code=... 发起一个新的GET请求
    • 所以,最终是用户的浏览器,而不是 GitHub 的服务器,向你的 Vert.x 应用发起了 /callback 请求。
  5. 你的服务器处理回调(第3次302)
    • 你的 Vert.x 应用收到来自浏览器的 /callback 请求。
    • 后续流程就和我们之前讨论的一样了:Router 将这个请求交给了 oauth2Handler 设置的内部处理器。
    • 这个处理器从 URL 中提取 code,然后在后台(服务器到服务器的通信)向 GitHub 的 API 发送请求,用 code 换取最终的 access_token
    • 向用户的浏览器发送一个 HTTP 302 重定向响应,location为:/login(也即第1次请求的路径)。
  6. 浏览器继续发起登录请求
    • 进入到用户路由Handler

流程图


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