Vert.x OAuth 2

基本术语

  • Authentication:认证
  • Authorisation:授权
  • Authority:权限
  • Permission:许可
  • Role:角色
  • User:用户
  • Token:令牌
  • Principal:凭证
  • Handler:处理器
  • Credential:证书
  • Token:令牌

Vert.X的这个组件包含了标准的OAuth2的实现。若要在自己的项目中使用它,则需要在构建描述信息的dependencies节点中添加如下信息:

  • Maven(在pom.xml文件中)

    1. <dependency>
    2. <groupId>io.vertx</groupId>
    3. <artifactId>vertx-auth-oauth2</artifactId>
    4. <version>3.2.1</version>
    5. </dependency>
  • Gradle(在build.gradle文件中)

    1. compile io.vertx:vertx-auth-oauth2:3.2.1

如果用户以第三方应用的方式访问想要的资源,OAuth2可以对这些用户进行授权,任何时候可以根据用户想要都有可能启用或禁用它的访问权限。

Vert.X中的OAuth2支持下边三种流程:

  • Authorization Code:授权码流程(对服务器和App可持久化存储信息)
  • Password Credentials:密码证书流程(之前的流程无法使用或开发阶段使用)
  • Client Credentials:客户端证书流程(客户端可仅仅可凭借客户端整数申请访问令牌【Access Token】)

授权码——Authorization Code Flow

授权码授权类型可以用来获取访问令牌(Access Token)和刷新令牌(Refresh Token),对安全性要求高的客户端(Confidential Client)是很不错的(Optimized)一种方式。作为一个基于重定向的流程,客户端必须能和资源拥有者的用户代理交互(通常是浏览器),同时要能接受从授权服务器(Authorization Server)通过重定向发送过来的请求。

更多:OAuth2 Section 4.1

密码证书——Password Credentials Flow

资源拥有者密码证书授权类型比较适合于这样一种情况:当资源拥有者和客户端之间有可信任的关系时使用,如设备操作系统、或高特权应用。授权服务器应该在很特殊的情况时启用这种授权类型,并且仅仅当其他应用都不可见时允许使用这种类型。

这种类型对于客户端要获取资源拥有者证书(用户名和密码,通常使用交互式表单)是合适的。它用于从已经存在的直接使用认证模式的如Basic或Digest的客户端向OAuth2认证方式迁移的时候,它会将证书直接转换成OAuth2中的访问令牌。

更多:OAuth2 Section 4.3

客户端证书——Client Credentials Flow

当客户端想要申请访问控制之下受保护的资源时,或者其他资源拥有者先前被授权服务器托管时(这种方式超出了本文范畴),客户端仅仅可以使用客户端证书(或者其他表示认证含义的信息)申请访问令牌。

客户端证书类型必须用于且仅用于机密客户端。

更多:OAuth2 Section 4.4

初探

下边是基于GitHub中使用Vert.X的OAuth2 Provider的认证示例实现代码:

  1. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.AUTH_CODE, new JsonObject()
  2. .put("clientID", "YOUR_CLIENT_ID")
  3. .put("clientSecret", "YOUR_CLIENT_SECRET")
  4. .put("site", "https://github.com/login")
  5. .put("tokenPath", "/oauth/access_token")
  6. .put("authorizationPath", "/oauth/authorize")
  7. );
  8. // when there is a need to access a protected resource or call a protected method,
  9. // call the authZ url for a challenge
  10. String authorization_uri = oauth2.authorizeURL(new JsonObject()
  11. .put("redirect_uri", "http://localhost:8080/callback")
  12. .put("scope", "notifications")
  13. .put("state", "3(#0/!~"));
  14. // when working with web application use the above string as a redirect url
  15. // in this case GitHub will call you back in the callback uri one should now complete the handshake as:
  16. String code = "xxxxxxxxxxxxxxxxxxxxxxxx"; // the code is provided as a url parameter by github callback call
  17. oauth2.getToken(new JsonObject().put("code", code).put("redirect_uri", "http://localhost:8080/callback"), res -> {
  18. if (res.failed()) {
  19. // error, the code provided is not valid
  20. } else {
  21. // save the token and continue...
  22. }
  23. });

授权码流程

授权码流程主要包含两部分内容:

  1. 第一步,你的应用客户端向用户申请允许访问它们的数据,如果用户审批后,OAuth2服务器发送给客户端一个授权码;
  2. 第二步,客户端将这个授权码和客户端密钥放到POST请求中发送给授权服务器(Authority Server)得到访问令牌;

示例代码:

  1. JsonObject credentials = new JsonObject()
  2. .put("clientID", "<client-id>")
  3. .put("clientSecret", "<client-secret>")
  4. .put("site", "https://api.oauth.com");
  5. // Initialize the OAuth2 Library
  6. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.AUTH_CODE, credentials);
  7. // Authorization oauth2 URI
  8. String authorization_uri = oauth2.authorizeURL(new JsonObject()
  9. .put("redirect_uri", "http://localhost:8080/callback")
  10. .put("scope", "<scope>")
  11. .put("state", "<state>"));
  12. // Redirect example using Vert.x
  13. response.putHeader("Location", authorization_uri)
  14. .setStatusCode(302)
  15. .end();
  16. JsonObject tokenConfig = new JsonObject()
  17. .put("code", "<code>")
  18. .put("redirect_uri", "http://localhost:3000/callback");
  19. // Callbacks
  20. // Save the access token
  21. oauth2.getToken(tokenConfig, res -> {
  22. if (res.failed()) {
  23. System.err.println("Access Token Error: " + res.cause().getMessage());
  24. } else {
  25. // Get the access token object (the authorization code is given from the previous step).
  26. AccessToken token = res.result();
  27. }
  28. });

密码证书流程

资源拥有者密码证书授权类型比较适合于这样一种情况:当资源拥有者和客户端之间有可信任的关系时使用,如设备操作系统、或高特权应用。授权服务器应该在很特殊的情况时启用这种授权类型,并且仅仅当其他应用都不可见时允许使用这种类型。

  1. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.PASSWORD);
  2. JsonObject tokenConfig = new JsonObject()
  3. .put("username", "username")
  4. .put("password", "password");
  5. // Callbacks
  6. // Save the access token
  7. oauth2.getToken(tokenConfig, res -> {
  8. if (res.failed()) {
  9. System.err.println("Access Token Error: " + res.cause().getMessage());
  10. } else {
  11. // Get the access token object (the authorization code is given from the previous step).
  12. AccessToken token = res.result();
  13. oauth2.api(HttpMethod.GET, "/users", new JsonObject().put("access_token", token.principal().getString("access_token")), res2 -> {
  14. // the user object should be returned here...
  15. });
  16. }
  17. });

客户端证书流程

这种类型对于客户端要获取资源拥有者证书是合适的。

  1. JsonObject credentials = new JsonObject()
  2. .put("clientID", "<client-id>")
  3. .put("clientSecret", "<client-secret>")
  4. .put("site", "https://api.oauth.com");
  5. // Initialize the OAuth2 Library
  6. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);
  7. JsonObject tokenConfig = new JsonObject();
  8. // Callbacks
  9. // Save the access token
  10. oauth2.getToken(tokenConfig, res -> {
  11. if (res.failed()) {
  12. System.err.println("Access Token Error: " + res.cause().getMessage());
  13. } else {
  14. // Get the access token object (the authorization code is given from the previous step).
  15. AccessToken token = res.result();
  16. }
  17. });

访问令牌对象

当一个令牌过期后你需要刷新令牌,OAuth2提供了访问令牌类AccessToken,它包含了很多实用的方法可以在令牌过期过后对令牌进行刷新。

  1. if (token.expired()) {
  2. // Callbacks
  3. token.refresh(res -> {
  4. if (res.succeeded()) {
  5. // success
  6. } else {
  7. // error handling...
  8. }
  9. });
  10. }

当你使用令牌访问完成过后想要注销,你可以撤销(Revoke)访问令牌和刷新令牌。

  1. token.revoke("access_token", res -> {
  2. // Session ended. But the refresh_token is still valid.
  3. // Revoke the refresh_token
  4. token.revoke("refresh_token", res1 -> {
  5. System.out.println("token revoked.");
  6. });
  7. });

其他通用OAuth2的Provider配置示例

Google

  1. JsonObject credentials = new JsonObject()
  2. .put("clientID", "CLIENT_ID")
  3. .put("clientSecret", "CLIENT_SECRET")
  4. .put("site", "https://accounts.google.com")
  5. .put("tokenPath", "https://www.googleapis.com/oauth2/v3/token")
  6. .put("authorizationPath", "/o/oauth2/auth");
  7. // Initialize the OAuth2 Library
  8. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);

GitHub

  1. JsonObject credentials = new JsonObject()
  2. .put("clientID", "CLIENT_ID")
  3. .put("clientSecret", "CLIENT_SECRET")
  4. .put("site", "https://github.com/login")
  5. .put("tokenPath", "/oauth/access_token")
  6. .put("authorizationPath", "/oauth/authorize");
  7. // Initialize the OAuth2 Library
  8. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);

Linkedin

  1. JsonObject credentials = new JsonObject()
  2. .put("clientID", "CLIENT_ID")
  3. .put("clientSecret", "CLIENT_SECRET")
  4. .put("site", "https://www.linkedin.com")
  5. .put("authorizationPath", "/uas/oauth2/authorization")
  6. .put("tokenPath", "/uas/oauth2/accessToken");
  7. // Initialize the OAuth2 Library
  8. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);

Twitter

  1. JsonObject credentials = new JsonObject()
  2. .put("clientID", "CLIENT_ID")
  3. .put("clientSecret", "CLIENT_SECRET")
  4. .put("site", "https://api.twitter.com")
  5. .put("authorizationPath", "/oauth/authorize")
  6. .put("tokenPath", "/oauth/access_token");
  7. // Initialize the OAuth2 Library
  8. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);

Facebook

  1. JsonObject credentials = new JsonObject()
  2. .put("clientID", "CLIENT_ID")
  3. .put("clientSecret", "CLIENT_SECRET")
  4. .put("site", "https://www.facebook.com")
  5. .put("authorizationPath", "/dialog/oauth")
  6. .put("tokenPath", "https://graph.facebook.com/oauth/access_token");
  7. // Initialize the OAuth2 Library
  8. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);

JBoss Keycloak

  1. JsonObject credentials = new JsonObject()
  2. .put("clientID", "CLIENT_ID")
  3. .put("clientSecret", "CLIENT_SECRET")
  4. .put("site", "https://www.your-keycloak-server.com")
  5. .put("authorizationPath", "/realms/" + realm + "/protocol/openid-connect/auth")
  6. .put("tokenPath", "/realms/" + realm + "/protocol/openid-connect/token");
  7. // Initialize the OAuth2 Library
  8. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);