SSOクライアントの作成(2)ログインプロセス内でのSSO認証

2008/11/21:この記事にあるOpenSSOはかなり古いものです。最近のものを使う際の参考にはならないかもしれませんのでご注意ください。

以下、ドメインは「example.com」、SSO用のCookieの名前は「ssotoken」とする。
なお、ここで示す実装はSSOクライアントとして共通というわけではなく、当方の要件に合わせたものであるため、この実装を適用する際には事前の検討が必要である。


まず、ログインプロセスが始まる段階でCookieを無効にする。
これは、ログインを試みた段階でそのWebクライアント(SSOクライアントではなく、ブラウザがあるホストのこと)をSSOセッションと切り離すため。
これがないと、そのアプリケーションに対する認証に失敗してもSSOセッションにより「他のアプリケーションでログインが成功している」とみなされて、失敗前に有効だったユーザでログインできてしまう。

public static final String SSOTOKEN_NAME = "ssotoken";
public static final String DOMAIN_NAME = "example.com";
... 略 ...
	// 
	// FIXME:requestとしてHttpServletRequestを取得
	// 
	Cookie cookie = new Cookie(SSOTOKEN_NAME, "");
	cookie.setDomain(DOMAIN_NAME);
	response.addCookie(cookie);
... 略 ...


次に、SSOクライアント自体のログインプロセスを実行する。
ここは各SSOクライアントごとの実装。
ただし、認証を全面的にSSOに任せる場合はこの部分は不要(というかSSOを導入するならSSO一本で認証する方が妥当な気がする)。


SSOクライアント自体の認証をパスした場合、SSOのログインプロセスを実行する。
ここは上記参考5かopenssosamplesのLogin.classと同じような実装になる。

// 
// FIXME:LOGとしてログ用インスタンスを取得
// 
public static final String USER_ID = "USER_ID";
public static final String PASSWORD = "PASSWORD";
public static final String SSOTOKEN_NAME = "ssotoken";
public static final String DOMAIN_NAME = "example.com";
public static final String SET_COOKIE_REQUEST = "SET_COOKIE_REQUEST";
public void login() {
	// SSOクライアントの認証
	... 略 ...
	// 
	// FIXME:requestとしてHttpServletRequestを取得
	// 
	// SSO認証
	try {
		request.setAttribute(USER_ID, id);
		request.setAttribute(PASSWORD, password);
		if (!ssologin()) {
			// SSO認証に失敗。
			//
			// FIXME:ここでSSOクライアント自体のログアウトを行う。
			//
			throw new Exception("error");
		}
	} catch(AuthLoginException e) {
		LOG.error("AuthLogin problem", e);
		throw new Exception("error");
	} catch(UnsupportedCallbackException e) {
		LOG.error("Callback problem", e);
		throw new Exception("error");
	}
	... 略 ...
}
public boolean ssologin()
throws UnsupportedCallbackException, AuthLoginException {
	AuthContext lc = getAuthContext();
	boolean succeed = false;
	Callback callbacks = null;
	// get information requested from module
	while (lc.hasMoreRequirements()) {
		callbacks = lc.getRequirements();
		if (callbacks != null) {
			addLoginCallbackMessage(callbacks);
			lc.submitRequirements(callbacks);
		}
	}
	if (lc.getStatus() == AuthContext.Status.SUCCESS) {
		LOG.info("Login succeeded.");
		succeed = true;

		try {
			setSSOTokenIdToCookie(lc);
		} catch (Exception e) {
			LOG.info("Could not get SSOToken.");
			succeed = false;
		}

	} else if (lc.getStatus() == AuthContext.Status.FAILED) {
		LOG.error("Login failed.");
	} else {
		LOG.error("Unknown status: " + lc.getStatus());
	}
	return succeed;
}
public void addLoginCallbackMessage(Callback callbacks)
throws UnsupportedCallbackException {
	int i = 0;
	try {
		for (i = 0; i < callbacks.length; i++) {
			if (callbacks[i] instanceof TextOutputCallback) {
				// N/A
			} else if (callbacks[i] instanceof NameCallback) {
				handleNameCallback*1;
	}
}
public void handleNameCallback(NameCallback nc)
throws IOException {
	// 
	// FIXME:requestとしてHttpServletRequestを取得
	// 
	String name = (String) request.getAttribute(USER_ID);
	nc.setName(name);
}
public void handlePasswordCallback(PasswordCallback pc)
throws IOException {
	// 
	// FIXME:requestとしてHttpServletRequestを取得
	// 
	String passwd = (String) request.getAttribute(PASSWORD);
	pc.setPassword(passwd.toCharArray());
}
public void setSSOTokenIdToCookie(AuthContext lc)
throws Exception {
	// 
	// FIXME:responseとしてHttpServletResponseを取得
	// FIXME:requestとしてHttpServletRequestを取得
	// 
	SSOToken token = lc.getSSOToken();
	String tokenId = token.getTokenID().toString();
	Cookie cookie = new Cookie(SSOTOKEN_NAME, tokenId);
	cookie.setDomain(DOMAIN_NAME);
	response.addCookie(cookie);
	// この処理を実行したリクエスト内にはCookieが無いので
	// request属性としてSSOが有効であることを示す
	request.setAttribute(SET_COOKIE_REQUEST, tokenId);
}

今回、SSOのログインプロセスが失敗した場合はSSOクライアント側もログアウトしてセッションを無効にしているが、要件によっては「SSOセッションは無効だが当該Webアプリケーションは利用可能」というケースがあるかもしれない。

※注:CookieのDomainやPathは適宜設定するように。

*1:NameCallback)callbacks[i]); } else if (callbacks[i] instanceof PasswordCallback) { handlePasswordCallback((PasswordCallback)callbacks[i]); } else { throw new UnsupportedCallbackException(callbacks[i]); } } } catch (IOException e) { LOG.error("Login Failed", e); throw new UnsupportedCallbackException(callbacks[i],e.getMessage(