JavaライブラリでOAuth認証
Tweet
みなさん、こんにちは。 7月にウノウに入社しました細川です。
私は、これまでいろいろなオープンソースの恩恵にあずかってきましたが、こちらから貢献をしたことは、ほとんどありませんでした。この記事がお役に立てれば幸いです。
ウノウ入社前にはJavaを主に使っていましたので、今回は、JavaのOAuthライブラリを使う方法について書いてみたいと思います。
OAuthとは
OAuthは、セキュアな認証手段として多く使われてきています。 twitterもweb APIの認証手段として採用しています。 OAuthを使った認証を行うことで、ユーザーはアカウントやパスワードを知られることなく、第三者サービスにAPIの使用を許可することができます。また、その認証は第三者サービスに関係なく取り消すことができます。
OAuth Community http://oauth.net/
OAuthコミュニティが各種プラットフォーム向けのライブラリを公開していますので、今回は、Javaのライブラリをビルドして使用します。
Code - OAuth http://oauth.net/code/
Repository http://oauth.googlecode.com/svn/code/
OAuth Library をビルドする
Eclipseを使ったビルドを紹介します。
新規プロジェクトを生成し、ショートカットメニューから「インポート」を選択します
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String destUrl = request.getParameter("dest");
OAuthClient client = new OAuthClient( new URLConnectionClient());
OAuthServiceProvider provider = new OAuthServiceProvider( REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL);
OAuthConsumer consumer = new OAuthConsumer( CALL_BACK_URL,
CONSUMER_KEY, COMSUMER_SECRET, provider);
OAuthAccessor accessor = new OAuthAccessor( consumer);
String redirectTo = null;
try{
try {
//get request token first from Twitter.com
HashMap params = new HashMap();
params.put( "oauth_callback",
OAuth.addParameters(accessor.consumer.callbackURL,
"dest", destUrl));
//get request token first from Twitter.com
client.getRequestToken(accessor, null, params.entrySet());
} catch (OAuthException e) {
throw new OperationFailedException( "It failed to authenticate Twitter account", e);
} catch (URISyntaxException e) {
throw new OperationFailedException( "It failed to authenticate Twitter account", e);
}
//build redirect path to twitter authentication page
redirectTo = OAuth.addParameters(
accessor.consumer.serviceProvider.userAuthorizationURL,//auth URL(twitter.com)
"oauth_token", accessor.requestToken//
);
} catch (IOException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
}
response.sendRedirect( redirectTo);
}
最初の部分ですが、ここでは、OAuthClientをもっとも単純なURLConnectionクラスを使うように初期化しています。
OAuthClient client = new OAuthClient( new URLConnectionClient());Jakarta Commons HttpClientのv3やv4を使用することもできます。その場合にはそれぞれnet.oauth.client.HttpClient3、net.oauth.client.HttpClient4クラスを使って初期化します。しかし、Google App Engine for Java環境では、スレッドが使えない関係で、URLConnectionClientしか動作しません。
OAuthServiceProvider provider = new OAuthServiceProvider( REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL); OAuthConsumer consumer = new OAuthConsumer( CALL_BACK_URL, CONSUMER_KEY, COMSUMER_SECRET, provider); OAuthAccessor accessor = new OAuthAccessor( consumer);アプリケーション登録で手に入れたパラメータを使って各クラスを初期化しています。
//get request token first from Twitter.com HashMapリクエストトークンは認証をリクエストする際に必要になります。params = new HashMap (); params.put( "oauth_callback", OAuth.addParameters(accessor.consumer.callbackURL, "dest", destUrl)); //get request token first from Twitter.com client.getRequestToken(accessor, null, params.entrySet());
//build redirect path to twitter authentication page redirectTo = OAuth.addParameters( accessor.consumer.serviceProvider.userAuthorizationURL,//auth URL(twitter.com) "oauth_token", accessor.requestToken// );
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String destUrl = request.getParameter( "dest");
String requestToken = request.getParameter(OAuth.OAUTH_TOKEN);
if( requestToken == null){
//rejected by USER
response.sendRedirect( destUrl);
return;
}
String verifire = request.getParameter(OAuth.OAUTH_VERIFIER);
if( verifire == null){
//rejected by USER
response.sendRedirect( destUrl);
return;
}
OAuthClient client = new OAuthClient( new URLConnectionClient());
OAuthServiceProvider provider = new OAuthServiceProvider( REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL);
OAuthConsumer consumer = new OAuthConsumer( CALL_BACK_URL,
CONSUMER_KEY, COMSUMER_SECRET, provider);
OAuthAccessor accessor = new OAuthAccessor( consumer);
accessor.requestToken = requestToken;
try {
HashMap params = new HashMap();
params.put( OAuth.OAUTH_VERIFIER, verifire);
//get access token and secret from twitter.com
client.getAccessToken(accessor, null, params.entrySet());
} catch (OAuthException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
} catch (URISyntaxException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
}
//Retrieve user's information
OAuthMessage oMessage = null;
try {
oMessage = accessor.newRequestMessage("GET", "http://api.twitter.com/1/account/verify_credentials.json", null);
} catch (OAuthException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
} catch (URISyntaxException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
}
OAuthResponseMessage rMessage = client.access(oMessage, ParameterStyle.AUTHORIZATION_HEADER);
int status = rMessage.getHttpResponse().getStatusCode();
if( status == HttpResponseMessage.STATUS_OK){
String jsonStr = rMessage.readBodyAsString();
JSONObject jObj = JSONObject.fromObject(jsonStr);
String userId = jObj.optString("id");
accessor.setProperty( "id", userId);
}else{
throw new RuntimeException( "It failed to authenticate Twitter account STATUS CODE:"+status);
}
//Store access token and secret to Cookie
TwitterAPIUtil.storeTokenToCookie(accessor, response);
response.sendRedirect( destUrl);
}
まず、認証によりTwitter(プロバイダ)から付け加えられたパラメータを取得しています。
String requestToken = request.getParameter(OAuth.OAUTH_TOKEN);
if( requestToken == null){
//rejected by USER
response.sendRedirect( destUrl);
return;
}
String verifire = request.getParameter(OAuth.OAUTH_VERIFIER);
if( verifire == null){
//rejected by USER
response.sendRedirect( destUrl);
return;
}
これらがセットされていなかった場合、ユーザーが認証を拒否したと考えられますので、あらかじめコールバックURLに追加したパラメータに積んでおいた飛び先URLにリダイレクトさせます。 accessor.requestToken = requestToken;
try {
HashMap params = new HashMap();
params.put( OAuth.OAUTH_VERIFIER, verifire);
//get access token and secret from twitter.com
client.getAccessToken(accessor, null, params.entrySet());
} catch (OAuthException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
} catch (URISyntaxException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
}
アクセストークンとトークンシークレットはAPIをコールするために必要になります。
うまく取得できた場合、すでにOAuthAccessorオブジェクトにアクセストークンとトークンシークレットは格納されていますので、Twitter APIを呼び出すことができます。
//Retrieve user's information
OAuthMessage oMessage = null;
try {
oMessage = accessor.newRequestMessage("GET", "http://api.twitter.com/1/account/verify_credentials.json", null);
} catch (OAuthException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
} catch (URISyntaxException e) {
throw new RuntimeException( "It failed to authenticate Twitter account", e);
}
OAuthResponseMessage rMessage = client.access(oMessage, ParameterStyle.AUTHORIZATION_HEADER);
int status = rMessage.getHttpResponse().getStatusCode();
if( status == HttpResponseMessage.STATUS_OK){
String jsonStr = rMessage.readBodyAsString();
JSONObject jObj = JSONObject.fromObject(jsonStr);
String userId = jObj.optString("id");
accessor.setProperty( "id", userId);
}else{
throw new RuntimeException( "It failed to authenticate Twitter account STATUS CODE:"+status);
}
ここでは、認証済みユーザー自身のユーザー情報を返す account/verify_credentials APIを呼び出しています。レスポンスのJSONを扱うのには、Json-lib ライブラリを使用しています。//Store access token and secret to Cookie TwitterAPIUtil.storeTokenToCookie(accessor, response);このメソッドの中身は以下のようになっています。
public static void storeTokenToCookie( OAuthAccessor accessor, HttpServletResponse response, int maxAge){
Cookie cookie = new Cookie( accessor.consumer.consumerKey+"_requesttoken", accessor.requestToken);
cookie.setPath( "/" );
cookie.setMaxAge( maxAge);
response.addCookie( cookie);
cookie = new Cookie( accessor.consumer.consumerKey+"_accesstoken", accessor.accessToken);
cookie.setPath( "/" );
cookie.setMaxAge( maxAge);
response.addCookie( cookie);
cookie = new Cookie( accessor.consumer.consumerKey+"_secret", accessor.tokenSecret);
cookie.setPath( "/" );
cookie.setMaxAge( maxAge);
response.addCookie( cookie);
String userId = (String)accessor.getProperty("id");
if( userId == null){
userId = "";
}
cookie = new Cookie( accessor.consumer.consumerKey+"_id", userId);
cookie.setPath( "/" );
cookie.setMaxAge( maxAge);
response.addCookie( cookie);
}
.........
OAuthClient client = new OAuthClient( new URLConnectionClient());
OAuthServiceProvider provider = new OAuthServiceProvider( REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL);
OAuthConsumer consumer = new OAuthConsumer( CALL_BACK_URL,
CONSUMER_KEY, COMSUMER_SECRET, provider);
OAuthAccessor accessor = new OAuthAccessor( consumer);
//try to retrieve token
Cookie[] cookies = request.getCookies();
for( int i = 0; i < cookies.length; ++i){
Cookie cookie = cookies[i];
if( cookie.getName().equals( accessor.consumer.consumerKey+"_accesstoken")){
accessor.accessToken = cookie.getValue();
}else if( cookie.getName().equals( accessor.consumer.consumerKey+"_requesttoken")){
accessor.requestToken = cookie.getValue();
}else if( cookie.getName().equals( accessor.consumer.consumerKey+"_secret")){
accessor.tokenSecret = cookie.getValue();
}else if( cookie.getName().equals( accessor.consumer.consumerKey+"_id")){
accessor.setProperty( "id", cookie.getValue());
}
}
.........
非常に駆け足になってしまいましたが、どのような印象を受けましたか?ライブラリを使用することで、かなりOAuthへの敷居が下がったと感じた方もおられるのではないでしょうか?

コメント
GAE/JでのOAuthライブラリの利用方法について悩んでいたので非常に参考になりました
ところで、「チェックアウトできましたら、次にプロジェクトの設定を行います」のくだりのサムネイルのリンク先が違うようですよ
投稿者: 迷える子羊 | 2010年9月 7日 17:17
↑http://labs.unoh.net/assets_c/2010/08/build_path_source-137.html
投稿者: 匿名 | 2011年1月14日 15:36