【Java】OAuth 1.0 の認可処理について

                                               f:id:tm200:20210423222508p:plain

 

JavaでOAuth 1.0を使用して認可処理を実装する機会があったのですがシグネチャの生成に少し手間取ったので、備忘録として残しておきます。

ユーザーからのリクエストがGadgetサーバーを経由してクライアントサーバーに転送され、更にAPIサーバーにリクエストする際の認証ヘッダー生成処理となります。

API通信部にはHttpURLConnectionクラスを使用しています。

 

⬇️ リバースプロキシ やキャッシュの使い方が詳しく記載されていて、お勧めです

マスタリングNginx

マスタリングNginx

 

OAuth1.0

ドキュメントはこちらです。

openid-foundation-japan.github.io

Java

以下のコードでは、クライアントから受け取ったOAuth tokenなどを元にAuthorizationヘッダを生成、付与してAPIサーバーにリクエストを送信しています。

公開する訳にはいかないので値は適当に変えて抜粋しています

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import java.nio.charset.StandardCharsets;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.apache.commons.codec.binary.Base64; @Controller @RequestMapping("/test")
public class TestController { @RequestMapping("/oauth-proxy") @ResponseBody public String oauthTest(HttpServletRequest request, HttpServletResponse response) { // Authorization headerから oauthToken と oauthTokenSecret を抽出 String header = request.getHeader("Authorization"); Map<String, String> authMap = getHeaderMap(header); String baseUrl = "https://test.com/api/rest/v1/xxxxx"; String apiUrl = baseUrl + "?_method=check";
String method = "POST";
String response = "";
// OAuthで利用するパラメータ String consumerSecret = "xxxxxxxxxx"; String oauthConsumerKey = "xxxxxxxxxx"; String oauthSignatureMethod = "HMAC-SHA1"; String oauthVersion = "1.0";
String oauthToken = authMap.get("oauth_token");
String oauthTokenSecret = authMap.get("oauth_token_secret"); HttpURLConnection huc = null; InputStream stream = null; OutputStream os = null; try { // 昇順ソートが必要なのでSortedMap SortedMap<String, String> params = new TreeMap<String, String>(); params.put("oauth_consumer_key", oauthConsumerKey); params.put("oauth_signature_method", oauthSignatureMethod); params.put("oauth_timestamp", String.valueOf(getUnixTime())); params.put("oauth_nonce", String.valueOf(Math.random())); params.put("oauth_token", oauthToken); params.put("oauth_version", oauthVersion); // クエリパラメータがある場合は含める params.put("_method", "check"); // baseStringに含めるURLにはクエリパラメータは含めない String baseString = makeBaseString(params, baseUrl, method); // baseStringからsignatureを生成 String signature = makeSignature(consumerSecret, oauthTokenSecret, baseString); params.put("oauth_signature", signature); // Authorizationヘッダを生成、クエリパラメータは含めないので削除 params.remove("_method");
String authHeader = makeAuthHeader(params); URL url = new URL(apiUrl); huc = (HttpURLConnection) url.openConnection(); huc.setRequestMethod(method); huc.addRequestProperty("Content-Type", "application/json; charset=UTF-8"); huc.setRequestProperty("Authorization", authHeader); huc.setDoOutput(true); os = huc.getOutputStream(); // リクエストボディ PrintStream ps = new PrintStream(os, true, StandardCharsets.UTF_8.toString()); ps.print("{\"data\": \"test\"}"); ps.close(); huc.connect(); // レスポンス取得 System.out.println(huc.getResponseCode()); stream = huc.getInputStream();
response = getResponse(stream, huc); System.out.println(response); } catch (Exception e) { System.out.println(e.getMessage()); try {
response = getResponse(stream, huc); System.out.println(response); } catch (Exception ex) {} } finally { if (stream != null) { try { stream.close(); } catch (Exception e) {} } if (os != null) { try { os.close(); } catch (Exception e) {} } }
return response; }

private Map<String, String> getHeaderMap(String header) {
String[] split = header.replace("\"", "").replace(" ", "").split(",");
Map<String, String> map = new HashMap<>(); for (int i = 1; i < split.length; i++) { String[] split1 = split[i].split("="); map.put(split1[0], split1[1]); } return map; } private String urlEncodeUTF8(String s) throws UnsupportedEncodingException { if (s == null || s.isEmpty()) { return ""; } return URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); } private String makeBaseString(Map<String, String> params, String baseUrl, String method) throws UnsupportedEncodingException { String baseParamString = ""; for (Entry<String, String> param : params.entrySet()) { baseParamString += "&" + param.getKey() + "=" + param.getValue(); } baseParamString = baseParamString.substring(1);
// 各パラメータをurlEncode return method.toUpperCase() + "&" + urlEncodeUTF8(baseUrl) + "&" + urlEncodeUTF8(baseParamString); } private String makeSignature(String consumerSecret, String oauthTokenSecret, String baseString) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
String signatureKey = urlEncodeUTF8(consumerSecret) + "&" + urlEncodeUTF8(oauthTokenSecret);
SecretKeySpec signingKey = new SecretKeySpec(signatureKey.getBytes(), "HmacSHA1"); Mac mac = Mac.getInstance(signingKey.getAlgorithm()); mac.init(signingKey);
byte[] rawHmac = mac.doFinal(baseString.getBytes());
// HmacSHA1形式でハッシュ化してbase64エンコード return new String(Base64.encodeBase64(rawHmac), StandardCharsets.UTF_8); } private String makeAuthHeader(Map<String, String> params) throws UnsupportedEncodingException {
String paramStr = "";
// ヘッダ項目もurlEncode for (Entry<String, String> param : params.entrySet()) { paramStr += ", " + param.getKey() + "=\"" + urlEncodeUTF8(param.getValue()) + "\""; } return "OAuth realm=\"\"" + paramStr; } private int getUnixTime() { return (int) (System.currentTimeMillis() / 1000L); }

private String getResponse(InputStream stream, HttpURLConnection huc) throws IOException { StringBuffer sb = new StringBuffer(); String line = ""; BufferedReader br = null; InputStreamReader reader = null; try { reader = new InputStreamReader(stream, StandardCharsets.UTF_8); br = new BufferedReader(reader); while ((line = br.readLine()) != null) { sb.append(line); } } catch (Exception e) { System.out.println(e.getMessage()); throw e; } finally { if (reader != null) { try { reader.close(); } catch (Exception e) {} } if (br != null) { try { br.close(); } catch (Exception e) {} } } return sb.toString(); } }

 

Java、久しぶりに書きました😝