読者です 読者をやめる 読者になる 読者になる

プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやる

openid4javaを試すなど

Java

ファイルの配置

EclipseTomcatプロジェクト作って、以下のように配置します。

OpenIDTest
│  index.jsp
│  index2.jsp
│
├─src
│      LoginWithGoogle.java
│
└─WEB-INF
    │  web.xml
    │
    ├─classes
    │      LoginWithGoogle.class
    │
    └─lib
            commons-codec-1.3.jar
            commons-logging-1.03.jar
            guice-2.0.jar
            httpclient-4.0.jar
            httpcore-4.0.1.jar
            nekohtml-1.9.14.jar
            openid4java-0.9.6.jar
            xercesImpl-2.8.1.jar

web.xml

/openidっていうパスに、LoginWithGoogleっていうクラスのServletを紐付けてるだけの、
超簡単web.xmlです。

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   version="2.5"> 
    <servlet>
        <servlet-name>LoginWithGoogle</servlet-name>
        <servlet-class>LoginWithGoogle</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>LoginWithGoogle</servlet-name>
        <url-pattern>/openid</url-pattern>
    </servlet-mapping>
</web-app>

index.jsp

OpenIDでログインするのに、サーブレットを呼び出します。
is_request=trueの意味は後述。

<a href = "/OpenIDTest/openid?is_request=true">login with Google</a>

index2.jsp

OpenIDでログイン成功したときに表示するページ。
特に何の変哲もありません。

Hello, World!

LoginWithGoogle.java

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.ParameterList;

public class LoginWithGoogle extends HttpServlet {
	public ConsumerManager manager;
	private String returnURL = "http://localhost:8080/OpenIDTest/openid";

	public void init() {
		this.manager = new ConsumerManager();
	}

	public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		if ("true".equals(req.getParameter("is_request"))) {
			requestOpenId(req, res);
		} else {
			responseOpenId(req, res);
		}
	}

	public void requestOpenId(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		try {
		    // perform discovery on the user-supplied identifier
		    List discoveries = manager.discover("https://www.google.com/accounts/o8/id");
			//List discoveries = manager.discover("http://yahoo.co.jp/");
	
		    // attempt to associate with the OpenID provider
		    // and retrieve one service endpoint for authentication
		    DiscoveryInformation discovered = manager.associate(discoveries);
	
		    // store the discovery information in the user's session for later use
		    // leave out for stateless operation / if there is no session
		    HttpSession session = req.getSession(false);
		    session.setAttribute("discovered", discovered);
	
		    // obtain a AuthRequest message to be sent to the OpenID provider
		    AuthRequest authReq = manager.authenticate(discovered, returnURL);

		    res.sendRedirect(authReq.getDestinationUrl(true));
		} catch (Exception e) {
			throw new ServletException(e);
		}
	}

	public void responseOpenId(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		try {
			// extract the parameters from the authentication response
			// (which comes in as a HTTP request from the OpenID provider)
			ParameterList openidResp = new ParameterList(req.getParameterMap());

			// retrieve the previously stored discovery information
			HttpSession session = req.getSession(false);
			DiscoveryInformation discovered = (DiscoveryInformation) session
					.getAttribute("discovered");

			// extract the receiving URL from the HTTP request
			StringBuffer receivingURL = req.getRequestURL();
			String queryString = req.getQueryString();
			if (queryString != null && queryString.length() > 0)
				receivingURL.append("?").append(req.getQueryString());

			// verify the response
			VerificationResult verification = manager.verify(
					receivingURL.toString(), openidResp, discovered);

			// examine the verification result and extract the verified
			// identifier
			Identifier verified = verification.getVerifiedId();

			if (verified != null) {
				// success, use the verified identifier to identify the user
				req.getRequestDispatcher("/index2.jsp").forward(req, res);
			} else {
				// OpenID authentication failed
				req.getRequestDispatcher("/index.jsp").forward(req, res);
			}
		} catch (Exception e) {
			throw new ServletException(e);
		}
	}
}
init

ConsumerManagerを初期化しています。

doGet

is_requestパラメータがtrueの場合は、OpenID認証リクエストを行い、
そうでない場合は、OpenIDプロバイダ(GoogleとかYahoo)からの戻りのリクエストを処理します。


これらを同じサーブレットに押し込めたのは、
openid4javaの制約なのか、なんなのか、
ConsumerManagerは1インスタンスにすべきという制約のためです。
(参考)Direct signature verification failed. - OpenID4Java | Google グループ

requestOpenID

SampleConsumer - openid4javaのパクリ。


変えたのは、userSuppliedStringのところ。
ここを変えることで、Googleだったり、Yahoo Japanだったり、その他もろもろの
OpenIDに対応することができるらしい。


ちなみに、http://www.hatena.ne.jp/taka_2/を指定したら、
戻りのところでパラメータが足りないとか怒られて動きませんでした。
OpenID2.0対応じゃないから?

responseOpenId

SampleConsumer - openid4javaのパクリ。

起動

  • http://localhost:8080/OpenIDTest/ にアクセスする
  • login with Googleのリンクをクリックする
  • (ログイン済みでなければ)ログイン画面が表示されるので、ログインする
  • (初回のみ or 今回のみ許可なら次回も)GoogleOpenID認証の画面になるので許可する
  • 認証OKなら、戻りのリクエストが行われ、index2.jspが表示されるはず

課題

  • ログインできなかったりする。やり直したらできたりする。
  • 例えばGoogleOpenIDを使った場合、Googleのメールアドレスとかは戻り側で取得できるの?
  • Google Appsは、また違う方法でやるようだ。