4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Microsoft Graph を 標準Javaで使う

Last updated at Posted at 2020-08-06

標準JavaパッケージでMicrosoft Graph

Microsoft SDK for Java を使うといいのですが、これを使うと関連Jarファイルがたくさん必要です。本質的にやっていることを理解するために、標準JavaパッケージだけでMicrosoft Graphの問い合わせを書いてみます。

やることは、

  1. Azure AD にアプリケーションを登録してクライアントIDとシークレットを作る(手順はチュートリアルを参考)
  2. Azure AD からOAuth2でアクセストークンをもらう。JSONで返ってくる。
  3. 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

アクセストークンが取得できる。
image.png

アクセストークンを使って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形式で値が返ってくるのがわかります。
image.png

このHTTPの処理をJavaで書くと

今回はJava8で記述しています。
またエラー処理は省略してます。ネットワークがプロキシ配下の場合はJava環境変数でプロキシ設定を行ってください。

App.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){}
		}
	}
}

コンパイルして実行するとちゃんと取得できました。
image.png

まとめ

SDKを使わなくても単純な取得は可能で、複数の追加Jarファイルがいらないのと、サーバとの手続きを隠蔽化されていないので本質を掴むことはできます。
一方、SDKを使えばトークン有効期限切れや、エンドポイントのURLを意識することなくコーディングに集中でき、また、Graph Explorerがコードスニペットを出力してくれるので生産性は上がるのではないかと思います。

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?