標準JavaパッケージでMicrosoft Graph
Microsoft SDK for Java を使うといいのですが、これを使うと関連Jarファイルがたくさん必要です。本質的にやっていることを理解するために、標準JavaパッケージだけでMicrosoft Graphの問い合わせを書いてみます。
やることは、
- Azure AD にアプリケーションを登録してクライアントIDとシークレットを作る(手順はチュートリアルを参考)
- Azure AD からOAuth2でアクセストークンをもらう。JSONで返ってくる。
- Microsoft Graph API のエンドポイントに問い合わせする。JSON返ってくる。
だけです。
ひとまずCurlコマンドで書くとこうなります
アクセストークンを取得する。
OAuthの手続きに従い、まずはアクセストークンを Azure Active Directoryから取得する。
取得する先のURL(エンドポイント)は、
https://login.microsoftonline.com/テナントID/oauth2/v2.0/token
テナントIDはAzure ADのテナント概要にあり。
ここに向かって作成したクライアントIDとシークレットをPOST。
scopeはURLをエスケープしているので%3Aや%2Fとなっています。どんなのが指定できるか詳しくはこちら。grant_typeはOAuthのパラメータで固定値です。
curl -d "client_id=クライアントID" \
-d "scope=https%3A%2F%2Fgraph.microsoft.com%2F.default" \
-d "client_secret=クライアントシークレット" \
-d "grant_type=client_credentials" \
-H "Content-Type: application/x-www-form-urlencoded" \
-X POST https://login.microsoftonline.com/テナントID/oauth2/v2.0/token
アクセストークンを使ってMicrosoft Graph APIにお問い合わせ
先のアクセストークンをコピーして今度はMicrosoft Graph APIに問い合わせや操作を行います。例えばグループメンバーを取得したかったらこんな感じになります。
エンドポイントはこちら。
https://graph.microsoft.com/v1.0/groups/{group-id}/members
エンドポイントがどこかや形式はGraph Explorerを使って確認します。
省略しますが、グループIDはAzure ADにログインし「すべてのグループ」一覧に表示されているIDを事前に取得しています。
curl -H GET 'https://graph.microsoft.com/v1.0/groups/グループID/members'\
-H 'Content-Type: application/json;charset=utf-8'\
-H 'Authorization: Bearer アクセストークン'
結果のスクリーンショットは省略してますが、JSON形式で値が返ってくるのがわかります。
このHTTPの処理をJavaで書くと
今回はJava8で記述しています。
またエラー処理は省略してます。ネットワークがプロキシ配下の場合はJava環境変数でプロキシ設定を行ってください。
import java.io.*;
import java.net.*;
//標準パッケージと言いつつここだけjavax追加ライブラリを使いました。
//ユニコードエスケープされた文字もデコードしてくれます
//Jarファイルはこちら
//https://repo1.maven.org/maven2/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar
import javax.json.*;
public class App {
public static void main(String[] args) throws Exception {
String tenant_id = "テナントID";
String client_id = "クライアントID";
String client_secret = "クライアントシークレット";
String group_id = "グループID";
HttpURLConnection conn = null;
InputStream input = null;
HttpURLConnection conn2 = null;
InputStream input2 = null;
try {
//①
//Oauth2でアクセストークンを取得
//Java標準のHTTPリクエスト処理
URL url = new URL("https://login.microsoftonline.com/" + tenant_id + "/oauth2/v2.0/token");
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);// 接続にかかる時間 ミリ秒
conn.setReadTimeout(5000);// データの読み込みにかかる時間 ミリ秒
conn.setRequestMethod("POST");// HTTPメソッド
conn.setUseCaches(false);// キャッシュ利用
conn.setDoOutput(true);// リクエストのボディの送信を許可(GETのときはfalse,POSTのときはtrueにする)
conn.setDoInput(true);// レスポンスのボディの受信を許可
conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
String data = //ポストするデータ
"client_id=" + client_id
+ "&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default" //java.net.URLEncoderでエスケープしてもよい
+ "&client_secret=" + client_secret
+ "&grant_type=client_credentials";
conn.getOutputStream().write(data.getBytes("utf-8"));
conn.getOutputStream().close(); // send
//結果を受け取ってJsonオブジェクトに変換
//JSONライブラリを使わない場合は戻ってきたテキストを自力でパースするだけです
//Curlで取得していた文字列と同じようなものが返ってきています
int code = conn.getResponseCode();
input = (code == 200 ? conn.getInputStream() : conn.getErrorStream());
JsonReader jsonReader = Json.createReader(new BufferedReader(new InputStreamReader(input, "utf-8")));
JsonObject json = jsonReader.readObject();
jsonReader.close();
conn.disconnect();
//アクセストークンが取り出せました!
String access_token = json.getString("access_token");
//②
//次はMicrosoft Graph APIにグループメンバーを問い合わせ
URL url2 = new URL("https://graph.microsoft.com/v1.0/groups/" + group_id + "/members");
conn2 = (HttpURLConnection) url2.openConnection();
conn2.setConnectTimeout(5000);// 接続にかかる時間 ミリ秒
conn2.setReadTimeout(5000);// データの読み込みにかかる時間 ミリ秒
conn2.setRequestMethod("GET");
conn2.setUseCaches(false);// キャッシュ利用
conn2.setDoOutput(false);// リクエストのボディの送信を許可(GETのときはfalse,POSTのときはtrueにする)
conn2.setDoInput(true);// レスポンスのボディの受信を許可
conn2.setRequestProperty("Authorization", "Bearer " + access_token); //取得したアクセストークン
conn2.setRequestProperty("Accept", "application/json"); //これ重要です!!! ハマったポイント
conn2.connect();
int code2 = conn2.getResponseCode();
input2 = (code2 == 200 ? conn2.getInputStream() : conn2.getErrorStream());
JsonReader jsonReader2 = Json.createReader(new BufferedReader(new InputStreamReader(input2, "utf-8")));
JsonStructure json2 = jsonReader2.read();
jsonReader2.close();
conn2.disconnect();
JsonArray members = json2.asJsonObject().getJsonArray("value");
//グループのメンバーが取れました!
System.out.println(members);
}
catch(Error e) {
e.printStackTrace();
}
finally {
if(input != null) try { input.close(); } catch(Exception e){}
if(conn != null) try { conn.disconnect(); } catch(Exception e){}
if(input2 != null) try { input2.close(); } catch(Exception e){}
if(conn2 != null) try { conn2.disconnect(); } catch(Exception e){}
}
}
}
まとめ
SDKを使わなくても単純な取得は可能で、複数の追加Jarファイルがいらないのと、サーバとの手続きを隠蔽化されていないので本質を掴むことはできます。
一方、SDKを使えばトークン有効期限切れや、エンドポイントのURLを意識することなくコーディングに集中でき、また、Graph Explorerがコードスニペットを出力してくれるので生産性は上がるのではないかと思います。