最近遊びでJava EEを使ったアプリを作ろうとしているのですが、世間のWeb系サービスでよく目にするTwitterでログインする機能みたいなの欲しいなぁと。
Twitter4JはOAuth周りもサポートされてるので、仕組みを把握することを兼ねてちょっとお試しで作ってみることに。
とりあえずOAuthの1.0から理解せんと(^^;ということで、以下サイト辺りが図解が丁寧でわかりやすかったです。
OAuthプロトコルの中身をざっくり解説してみるよ ( ꒪⌓꒪) ゆるよろ日記
OAuth 2.0でWebサービスの利用方法はどう変わるか @IT
また、JSPとTwitter4Jでの例は既にあって、そもそもTwitter4Jの公式サイトにあるコード例ページからリンクがあるように、@yusukeさんのGitHubにシンプルな例があったりします。
Twitterアカウントによるサインインのサンプル Twitter4JのGitHub
JavaでTwitterをOAuth認証して使えるTwitter4Jとは @IT
ということで、自分はJSFで試してみることにしました。
できたサンプルはこんな感じ。
とりあえずTwitterでログインボタンを設けたログインページ表示して
ボタンを押すとアプリから認証を求められて
とりあえず自分の普段利用してるアカウントで認証すると…
認証されて、取得されたユーザ名でメッセージ出せてる!
あらかじめログインした状態だと、認証の画面は以下のような感じで、ID/Passの入力が省かれます
世間的には大したことじゃないんだろうけど…なんか嬉し楽し(^^)!
サンプルはJDK8とGlassFish4.0.1b4で作りました。
以下、サンプルで作ったコードです。
ログインページ(twitterLogin.xhtml)
<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Twitterでログインするお試し</title>
</h:head>
<h:body>
<h:form>
<h:commandButton value="Twitterでログイン" actionListener="#{twitterLoginBean.twitterLogin()}" />
</h:form>
</h:body>
</html>
管理Bean
package com.kikutaro.twitterlogin;
import java.io.IOException;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import twitter4j.TwitterException;
import twitter4j.auth.RequestToken;
@Named
@RequestScoped
public class TwitterLoginBean{
@Inject
private TwitterSession twitterSession;
@PostConstruct
public void init(){
if(twitterSession != null){
twitterSession.getTwitter().setOAuthConsumer("hogehoge", "foofoo");
}
}
public void twitterLogin() throws TwitterException, IOException{
RequestToken requestToken = twitterSession.getTwitter().getOAuthRequestToken("http://localhost:8080/TwitterLogin/faces/callback.xhtml");
twitterSession.setRequestToken(requestToken);
ExternalContext externalContet = FacesContext.getCurrentInstance().getExternalContext();
externalContet.redirect(requestToken.getAuthenticationURL());
}
}
Session保持用
package com.kikutaro.twitterlogin;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import lombok.Getter;
import lombok.Setter;
import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.auth.RequestToken;
@Named
@SessionScoped
public class TwitterSession implements Serializable{
@Getter
private Twitter twitter;
@Getter @Setter
private RequestToken requestToken;
@PostConstruct
public void init(){
twitter = new TwitterFactory().getInstance();
}
}
Callbackでリダイレクトされるページ
<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Twitterからリダイレクトされてくる</title>
</h:head>
<h:body>
<f:metadata>
<f:viewParam name="oauth_token" value="#{callBackBean.oauthToken}" />
<f:viewParam name="oauth_verifier" value="#{callBackBean.oauthVerifier}" />
<f:viewAction action="#{callBackBean.preRender()}" />
</f:metadata>
<h:outputLabel value="#{callBackBean.loginUserName}" />さん、ログインに成功しました。
</h:body>
</html>
上記ページの管理Bean
package com.kikutaro.twitterlogin;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import lombok.Getter;
import lombok.Setter;
import twitter4j.TwitterException;
import twitter4j.User;
import twitter4j.auth.AccessToken;
@Named
@RequestScoped
public class CallBackBean {
@Inject
private TwitterSession twitterBean;
@Getter @Setter
private String oauthToken;
@Getter @Setter
private String oauthVerifier;
@Getter
private String loginUserName;
public void preRender(){
try {
AccessToken accessToken = twitterBean.getTwitter().getOAuthAccessToken(twitterBean.getRequestToken(), oauthVerifier);
twitterBean.getTwitter().setOAuthAccessToken(accessToken);
User user = twitterBean.getTwitter().showUser(accessToken.getUserId());
loginUserName = user.getName();
} catch (TwitterException ex) {
Logger.getLogger(CallBackBean.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
パラメータの受け取りは「JSF 2.0のGETパラメータの受け取り」と「JSF 2.2ではf:eventのpreRenderViewではなくf:viewActionを使う?」を参考にViewActionを利用してみました。
NameSpaceは「http://xmlns.jcp.org/jsf/core」で動いたので、改修されてるっぽいですね。
作っていて1つハマったのは以下サイトにもあるCallbackでリダイレクトできない現象でした。
TwitterのOAuthでCallbackさせるときの注意点
アプリケーションを登録する際の以下画面でCallback URLを指定する必要があるんですね。
上記ブログにもあるように、適当な値でも大丈夫なので何らか入れることで、ちゃんと情報が取得できるようになりました。
本気で実装するなら、もっと色々工夫しないとダメだなー。と思いつつ、とりあえず、動いたので地味に感動(^^;