Vert.x OAuth 2

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

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

    • Maven(在文件中)

    • Gradle(在build.gradle文件中)

      1. compile io.vertx3.2.1

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

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

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

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

    密码证书——Password Credentials Flow

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

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

    更多:OAuth2 Section 4.3

    客户端证书——Client Credentials Flow

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

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

    更多:OAuth2 Section 4.4

    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. if (res.failed()) {
    18. // error, the code provided is not valid
    19. } else {
    20. // save the token and continue...
    21. }
    22. });

    授权码流程

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

    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. 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. // Get the access token object (the authorization code is given from the previous step).
    14. AccessToken token = res.result();
    15. }
    16. });

    访问令牌对象

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

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

    当你使用令牌访问完成过后想要注销,你可以撤销(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配置示例

    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);
    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);

    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. OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);