このあいだコミケを機にAndroid端末を手に入れたのでどうせならアプリでも作ったれと思ってTwitterクライアントを作ったので備忘録がてら。(実機テストよりエミュレーターのほうが楽だった。)

 正直なところiOSはTheWorld、AndroidはMoonStrikeで用が足りてしまうので使い道がないのが難点。

 盛大にハマったのはOAuth認証くらいだが細々とハマったところも後から書いていく。


(ソース1)

private RequestToken req;
private OAuthAuthorization oauau;

private String ck;
private String cs;    //...ConsumerKey/Secretは事前に取得しておく
private String at;
private String ats;
final String CALLBACK="hoge";

public void User_Oauth(){
	oauau=new OAuthAuthorization(new ConfigurationBuilder()
		.setOAuthConsumerKey(ck)
		.setOAuthConsumerSecret(cs)
		.build());
	oauau.setOAuthAccessToken(null);
	String url="";
	
	try {
		GetAuthURL g=new GetAuthURL(auau);
		g.execute(CALLBACK);
		req=g.get();
		url=req.getAuthorizationURL();
	}catch(Exception e){
		url=e.toString();
	}
	
	WebView wb=(WebView)findViewById(R.id.webview);
	wb.setVisibility(View.VISIBLE);
	wb.setWebViewClient(new WebViewClient(){
		String verifier="";
		
		public void onPageFinished(WebView view,String url){
			super.onPageFinished(view,url);
			
			if(url!=null&&url.split("\\?")[0].endsWith(CALLBACK)){
				String[] params=url.split("\\?")[1].split("&");
				
				if(params[0].startsWith("oauth_verifier")){
					verifier=params[0].split("=")[1];
				}else if(params[1].startsWith("oauth_verifier")){
					verifier=params[1].split("=")[1];
				}
				
				getAccessToken(verifier);
				view.setVisibility(GONE);
			}
		}
	});
	wb.loadUrl(url);
}

public void getAccessToken(String verifier){
	try {
		GetAccessTokenAsync g=new GetAccessTokenAsync(req,oauau);
		g.execute(verifier);
		String[]keys=g.get();
		at=keys[0];
		ats=keys[1];
	}catch(Exception e){
		//例外処理
	}
}

 まずOAuthAuthorizationの引数に、ConsumerKey(CK)とConsumerSecret(CS)をセットしたConfigurationBuilderを渡してインスタンス化する(ここではoauau)...(14行目)。

 この時点ではAccessTokenは取得していないのでoauauのAccessTokenにnullをセットする(しないと後でエラーになる)...(15行目)。

 次に認証ページのURLを取得するのだが、メインスレッドで取得しようとするとNetworkOnMainThreadExceptionなる例外が返ってくる。要するにメインスレッドで通信するなワーカースレッドでやれ、ということなのでAsyncTaskを継承したクラスを自前で用意しなくてはならない。

(ソース2)

public class GetAuthURL extends AsyncTask<String,Void,RequestToken> {
	private OAuthAuthorization oauau;
	
	public GetAuthURL(OAuthAuthorization oauau){
		this.oauau=oauau;
	}
	
	protected RequestToken doInBackground(String... callback){
		try{
			RequestToken req=oauau.getOAuthRequestToken(callback[0]);
			return req;
		}catch(Exception e){
			return null;
		}
	}
}

 AsyncTaskを継承するときdoInBackgroundメソッドに処理を書き込む。引数は可変長だ。

 ここではコンストラクタにoauauを渡し、それを使ってgetOAuthRequestTokenでRequestTokenを返している...(11行目)。

 ここでCALLBACKは任意の文字列である(後で使う)。

 返ってきたRequestTokenを使ってgetAuthorizationURL()で認証URLをめでたくゲット...(ソース1の22行目)。次はWebViewでアプリに使用許可を出す。

 WebViewClientのonPageFinished(ページ読み込みが終わると呼び出される)をオーバーライドして、認証後にリダイレクトするURLにさっきのCALLBACKが含まれていれば認証成功というように判定する。

 実際のURLは

https://api.twitter.com/oauth/(CALLBACK)?oauth_token=(...)&oauth_verifier=(...)

という形で返ってくるので、まずURLを?で区切る(正規表現の記号なので"?"は\\でエスケープさせる)。

 そして前半部分がCALLBACKで終わっているとき、後半部分を&で区切ってoauth_verifierを取得する...(ソース1の41行目)。

 ここまで来ればあとはAccessToken取得を残すのみである。これもAsyncTaskを使わないと例外を投げられるので適当にクラスを作る。

(ソース3)

public class GetAccessTokenAsync extends AsyncTask<String,Void,String[]>{
	private RequestToken req;
	private OAuthAuthorization oauau;
	
	public GetAccessTokenAsync(RequestToken req, OAuthAuthorization auau){
		this.req=req;
		this.oauau=oauau;
	}
	
	protected String[] doInBackground(String... verifier){
		try{
			AccessToken ac=oauau.getOAuthAccessToken(req,verifier[0]);
			return new  String[]{ac.getToken(),ac.getTokenSecret()};
		}catch(Exception e){
			return new String[]{e.toString()};
		}
	}
}

 oauauにRequestTokenとverifierを渡してAccessTokenを生成し、AccessTokenとAccessTokenSecretが取得できる...(13行目)。

 これでCK、CS、AccessToken、AccessTokenSecretがすべて揃ったので晴れてOAuth認証成功となる。

 

 一連の流れとしては

  • CKとCSからoauauを生成
  • oauauでRequestTokenを生成
  • RequestTokenから認証URLを生成し、権限を承認
  • リダイレクトのURLからVerifierを取得
  • RequestTokenとVerifierからAccessTokenを生成
  • CK、CS、AccessToken、AccessTokenSecretを使って権限を使用

という形になる。メンドクセエ…

 ともかくこれだけのステップを踏まないとセキュリティは守れないんですね(白目)。


おわり

【Android】Twitter4jでOAuth認証をアプリ内で完結させる

投稿ナビゲーション


コメントを残す

メールアドレスが公開されることはありません。