2011/04/17
GAE/J の OAuth 認証を試してみた。その1
CUI のクライアントから GAE にアクセスするバッチ的な
プログラムの為に OAuth 認証を調べてみた。
GAE 側は極めて単純で
.appspot.com を指定して [add domain] をクリックする。
※普通は https にして置いた方が良いと思う。
するとサイトの所有権を確認される。
今回は meta ダグをトップページに張り付ける方法を選択した。
meta ダグを張り付けた後で [確認] をクリックすると ドメインが追加される。
ドメイン名をクリックするとドメインの管理画面に移動するので
URL を入力して [Save] する。
これて OAuth の Key と Secret が登録され OAuth が使えるようになる。
テスト用のサーバ側コードを用意する。
var OAuthServiceFactory = Packages.com.google.appengine.api.oauth.OAuthServiceFactory;
function doGet(req, res) {
var oauthService = OAuthServiceFactory.getOAuthService();
var text =
"user:"+oauthService.getCurrentUser()+"\n"
+"admin:"+oauthService.isUserAdmin()+"\n"
;
res.writer.write(text);
}
これてサーバ側の準備完了。
package org.kotemaru.test;
import java.io.*;
import java.net.*;
import java.util.*;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import net.oauth.client.OAuthClient;
import net.oauth.OAuthServiceProvider;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthAccessor;
import net.oauth.OAuth;
import net.oauth.ParameterStyle;
import net.oauth.OAuthMessage;
import net.oauth.OAuthException;
import net.oauth.http.HttpClient;
import net.oauth.signature.*;
import net.oauth.client.httpclient3.HttpClient3;
//import net.oauth.client.URLConnectionClient;
public class OAuthTestClient {
public static final String APP_ID = "app_id";
public static final String CONSUMER_SECRET = "consumer_secret";
public static final String OAUTH_TOKEN = "oauth_token";
public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret";
public static final String PRIVATE_KEY = "private_key";
public static final String APP_NAME = "application_name";
public static final String GET_REQ_TOKEN = "/_ah/OAuthGetRequestToken";
public static final String GET_AUTH_TOKEN = "/_ah/OAuthAuthorizeToken";
public static final String GET_ACCESS_TOKEN = "/_ah/OAuthGetAccessToken";
public static final String HTTPS = "https://";
public static final String APPSPOT_COM = "appspot.com";
private Properties props;
private String appId;
private String consumerSecret;
private String domain;
private String reqUrl ;
private String authUrl ;
private String accessUrl ;
private OAuthAccessor accessor;
private OAuthClient client;
public OAuthTestClient(Properties p) throws Exception {
props = p;
appId = props.getProperty(APP_ID);
consumerSecret = props.getProperty(CONSUMER_SECRET);
domain = appId + "." + APPSPOT_COM;
reqUrl = HTTPS + domain + GET_REQ_TOKEN;
authUrl = HTTPS + domain + GET_AUTH_TOKEN;
accessUrl = HTTPS + domain + GET_ACCESS_TOKEN;
accessor = getOAuthAccessor();
client = getOAuthClient();
}
protected OAuthAccessor getOAuthAccessor() throws Exception {
OAuthServiceProvider provider =
new OAuthServiceProvider(reqUrl, authUrl, accessUrl);
OAuthConsumer consumer =
new OAuthConsumer(null, domain, consumerSecret, provider);
String key = props.getProperty(PRIVATE_KEY);
if (key != null) {
EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(
OAuthSignatureMethod.decodeBase64(key)
);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(privKeySpec);
consumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey);
consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);
}
return new OAuthAccessor(consumer);
}
protected OAuthClient getOAuthClient() {
OAuthClient client = new OAuthClient(new HttpClient3());
client.getHttpParameters().put(HttpClient.FOLLOW_REDIRECTS, Boolean.TRUE);
return client;
}
protected OAuthMessage send(String url, String token) throws Exception {
List params = new ArrayList();
params.add(new OAuth.Parameter(APP_NAME, appId));
params.add(new OAuth.Parameter(OAUTH_TOKEN, token));
OAuthMessage response = client.invoke(accessor, "GET", url, params);
return response;
}
public String getAuthUrl() throws Exception {
client.getRequestToken(accessor);
OAuthMessage response = send(authUrl, accessor.requestToken);
props.setProperty(OAUTH_TOKEN, accessor.requestToken);
props.setProperty(OAUTH_TOKEN_SECRET, accessor.tokenSecret);
return response.URL;
}
public void access() throws Exception {
accessor.tokenSecret = props.getProperty(OAUTH_TOKEN_SECRET);
OAuthMessage response = send(accessUrl, props.getProperty(OAUTH_TOKEN));
props.setProperty(OAUTH_TOKEN, response.getParameter(OAUTH_TOKEN));
props.setProperty(OAUTH_TOKEN_SECRET,
response.getParameter(OAUTH_TOKEN_SECRET));
}
public OAuthMessage request(String url) throws Exception {
accessor.consumer.setProperty(OAuthClient.PARAMETER_STYLE,
ParameterStyle.AUTHORIZATION_HEADER);
accessor.tokenSecret = props.getProperty(OAUTH_TOKEN_SECRET);
List params = new ArrayList();
params.add(new OAuth.Parameter(APP_NAME, appId));
params.add(new OAuth.Parameter(OAUTH_TOKEN, props.getProperty(OAUTH_TOKEN)));
OAuthMessage response = client.invoke(accessor, "GET", url, params);
return response;
}
//------------------------------------------------------------------
public static Properties loadProps(String name) throws IOException {
Properties props = new Properties();
InputStream in = new FileInputStream(name);
try {
props.load(in);
} finally {
in.close();
}
return props;
}
public static void saveProps(String name, Properties props) throws IOException {
OutputStream out = new FileOutputStream(name);
try {
props.store(out, "");
} finally {
out.close();
}
}
public static void main(String[] args) throws Exception {
Properties props = loadProps(args[0]);
System.out.println(props);
OAuthTestClient test = new OAuthTestClient(props);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("\n> ");
String line = reader.readLine();
while (line != null) {
line = line.trim();
if ("request".equals(line)) {
String url = test.getAuthUrl();
System.out.println("Auth URL:\n"+url);
saveProps(args[0], props);
} else if ("access".equals(line)) {
test.access();
saveProps(args[0], props);
System.out.println("Get access token.");
} else {
OAuthMessage response = test.request(line);
System.out.println(response.readBodyAsString());
}
System.out.print("\n> ");
line = reader.readLine();
}
}
}
通常の OAuth クライアントなので GAE としての特別な部分は以下の3行だけ。
public static final String GET_REQ_TOKEN = "/_ah/OAuthGetRequestToken";
public static final String GET_AUTH_TOKEN = "/_ah/OAuthAuthorizeToken";
public static final String GET_ACCESS_TOKEN = "/_ah/OAuthGetAccessToken";
使い方は最初に目的サーバの app-id と OAuth secret を持つ
プロパティファイルを作成する。
app_id=
consumer_secret=XXXXXXXXXXXXXXXXXXXX
oauth.props を引数に渡して org.kotemaru.test.OAuthTestClient
を起動するとプロンプトが出るのでコマンドを入力。
$ java -classpath $CP org.kotemaru.test.OAuthTestClient oauth.props
> access
Get access token.
アクセストークンは oauth.props に書き戻される。
oauth-client.zip 参考にしたページ:
http://blog.smartnetwork.co.jp/staff/node/53
OAuthService oauthService = OAuthServiceFactory.getOAuthService(); User user = oauthService.getCurrentUser();これだけで OAuth 認証されたユーザが取得できる。 問題はクライアント側で OAuth の 3-legged プロトコルを ちゃんと実装しなければいけない。 とっかかりはこの辺りから...
- http://code.google.com/intl/ja/appengine/docs/java/oauth/
- http://code.google.com/intl/ja/apis/accounts/docs/OAuth_ref.html
- http://code.google.com/p/oauth/
サーバ側の準備
まず最初に必要なのが GAE サーバの登録。- https://www.google.com/accounts/ManageDomains
- https://wsjs-gae.appspot.com/test/oauth.ssjs:
クライアントの実装
先に見付けたサイトからライブラリの jar を落す。- http://code.google.com/p/oauth/downloads/list
svn co http://oauth.googlecode.com/svn/code/java/サンプルコード jmeter/example/command-line を参考にクライアント側 のコードを書いてみる。
- OAuthTestClient.java:
- oauth.props:
- トークン取得:
> request Auth URL: https://www.google.com/accounts/OAuthAuthorizeToken?application_name=wsjs-gae&oauth_token=4%2FtdHeN0V_wnRNUBbLJYpgiU0GRUUJ&oauth_consumer_key=wsjs-gae.appspot.com&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1302937883&oauth_nonce=1302937883290438000&oauth_version=1.0&oauth_signature=XXXXXXXXXXXXXXXXXXX%3D&scope=appengine&hd=defaultこのURLをブラウザで開いて登録ユーザでログインしアクセス許可を行う。
- アクセストークンの取得:
- 目的URLにアクセス:
> https://wsjs-gae.appspot.com/test/oauth.ssjs user:xxxxx@gmail.com admin:trueちゃんと認証できました。 なんだかえらい大変でした。 趣味で使う分には固定パスワードで十分だと思います。 業務用だと管理ツールで必須とか言われるかもねー。 コード一式(Eclipseプロジェクト):
oauth-client.zip 参考にしたページ:
http://blog.smartnetwork.co.jp/staff/node/53
この投稿へのコメント
コメント・フォーム