OAuth2.0で認証してアクセストークンを取得するまでのサンプルコード。
認証サーバ側の設定等は今回は記載していない。
仕組(超概要)
① 事前に認証サーバ側で発行したクライアントIDとシークレットKeyを取得
② 認証エンドポイントへリクエスト(URLアクセス)
③ 認証を行う(ユーザIDとパスワードの入力)
④ リダイレクトでコールバックページにリクエストが来る
⑤ 認証が成功していれば許可コードがクエリパラメタ等で貰える
⑥ 許可コードとクライアントID、シークレットKeyをトークンエンドポイントに送信(POST等)
⑦ アクセストークン(とリフレッシュトークン)を取得
⑧ あとはAPIを投げるなり、IDトークンも貰ってSSOするとかご自由に
環境
認証サーバにはOpenAMを使って検証した(このあたりの設定は今回は触れない)。
クライアント側はgoogleのoauth-clientを利用した。
あと、簡単に試すためspark-frameworkを利用している。
pom.xml
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
<version>1.23.0</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-jackson2</artifactId>
<version>1.23.0</version>
</dependency>
実装サンプル
MyOAuthServiceProviderSample.java
import java.util.Arrays;
import spark.Request;
import spark.Response;
import spark.Route;
import static spark.Spark.*;
import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl;
import com.google.api.client.auth.oauth2.AuthorizationCodeTokenRequest;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import java.io.IOException;
/**
* OAuth2.0で認証を行いアクセストークンを取得
* @author owl
*/
public class MyOAuthServiceProviderSample {
public static void main(String[] args) {
/**
* ① 認証要求
*/
get("/auth", new Route() {
@Override
public Object handle(Request req, Response resp) throws Exception {
/**
* 認証エンドポイントと、クライアントIDを指定
* 今回はOpenAMで構築した認証サーバを利用
*/
AuthorizationCodeRequestUrl codeUrl = new AuthorizationCodeRequestUrl("http://www1.openamexam.co.jp:18080/openam/oauth2/authorize?", "oauth_client_key");
codeUrl.setScopes(Arrays.asList("openid profile email")); // 取得したい情報を指定
codeUrl.setResponseTypes(Arrays.asList("code")); // 許可コードをもらう
codeUrl.setRedirectUri("http://localhost:4567/callback"); // CallBackUrlを指定
codeUrl.setState("this_is_test_state_code"); // ステートコードを指定、CSRF対策用
codeUrl.set("nonce", "this_is_one_time_phrase"); // nonceを指定、リプレイアタック対策用
codeUrl.set("access_type", "offline"); // リフレッシュトークンを要求する
codeUrl.set("realm", "/api"); // realmを指定
resp.redirect(codeUrl.build()); // 認証エンドポイントに飛ばす
return null;
}
});
/**
* ② アクセストークンの取得
* -- 認証エンドポイントで認証が行われると、リダイレクトでこちらにCallBackしてくる
*/
get("/callback", new Route() {
@Override
public Object handle(Request req, Response resp) throws Exception {
/**
* 許可コードからアクセストークンを要求
* この辺は認証サーバの設定にも依存する。今回はQueryStringで取得できるように設定している。
*/
String allowCode = req.queryParams("code"); // 許可コードを取得
// ステートコードを取得
// ここで要求時と値が変わらないかチェックすべき(今回は割愛)
String stateCode = req.queryParams("state");
/**
* 許可コードで、アクセストークンの取得要求を行う
* この辺は認証サーバの設定にも依存する。今回はPOST方式でシークレットキーを送る
*/
AuthorizationCodeTokenRequest tokenUrl = new AuthorizationCodeTokenRequest(
new NetHttpTransport(),
new JacksonFactory(),
new GenericUrl("http://www1.openamexam.co.jp:18080/openam/oauth2/access_token?realm=/api"),
allowCode
);
tokenUrl.setGrantType("authorization_code");
tokenUrl.setRedirectUri("http://localhost:4567/callback"); // 再度自身のURLを指定しないとエラーになる
tokenUrl.set("client_id", "oauth_client_key"); // クライアントIDを設定
tokenUrl.set("client_secret", "oauth_secret_key"); // シークレットIDを設定
TokenResponse tr = null;
String accessToken = null;
String refreshToken = null;
try {
tr = tokenUrl.execute();
accessToken = tr.getAccessToken();
refreshToken = tr.getRefreshToken() == null ? "null" : tr.getRefreshToken();
// idTokenもらって解析してその情報信用して自社サービスとかにログインさせればSSOとすることも可
String idToken = (String)tr.get("id_token");
} catch (IOException e) {
e.printStackTrace();
} finally {
tr = null;
}
return "AccessToken = " + accessToken
+ "<br>RefreshToken = " + refreshToken;
}
});
}
}