package org.yoshiori.hatena.auth;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.yoshiori.commons.security.MessageDigest;
import org.yoshiori.commons.security.MessageDigest.DigestAlgorithm;
import org.yoshiori.hatena.HatenaException;
/**
*
はてな認証を行うクラスです.
* はてな認証の詳細については「はてな認証API」こちらをご覧ください。
*
* 主な使い方としては
*
HatenaAuth auth = new HatenaAuth("api_key", "secret");
* String link = auth.getAuthURL();
* で、認証用のURLが取得できます。
* cert取得後、
* User user = auth.login(cert);
* でユーザー情報を取得できます。
*
* @author yoshiori
* @see はてな認証API
*/
public class HatenaAuth {
private String apiKey;
private String secret;
private MessageDigest md5;
private String encode = "UTF-8";
private static final String AUTH_URL = "http://auth.hatena.ne.jp/auth?";
private static final String LOGIN_URL = "http://auth.hatena.ne.jp/api/auth.xml?";
/**
* @param apiKey はてなから取得したAPIキー
* @param secret はてなから取得した秘密鍵
*/
public HatenaAuth(String apiKey, String secret) {
if(StringUtils.isEmpty(apiKey) || StringUtils.isEmpty(secret) ){
throw new NullPointerException("apikey or secret is null.");
}
this.apiKey = apiKey;
this.secret = secret;
md5 = new MessageDigest(DigestAlgorithm.MD5);
}
/**
* apiKeyとsecretを元に
* 認証用のURLが取得できます。
* @return 認証用URL
*/
public String getAuthURL() {
return getAuthURL(null);
}
/**
* apiKeyとsecretとparameterMapを元に
* 認証用のURLが取得できます。
* parameterMapについては
* javax.servlet.ServletRequest.getParameterMap()に
* 準じています。
* @param parameterMap 上記参照
* @return 認証用URL
* @see javax.servlet.ServletRequest.getParameterMap()
*/
public String getAuthURL(Map parameterMap) {
Map params = new TreeMap();
if(parameterMap != null){
params.putAll(parameterMap);
}
params.put("api_key", new String[] { apiKey });
StringBuilder sigBuilder = new StringBuilder(secret);
StringBuilder paramBuilder = new StringBuilder();
for (String key : params.keySet()) {
String[] parameterValues = params.get(key);
Arrays.sort(parameterValues);
for (String value : parameterValues) {
sigBuilder.append(key).append(value);
try {
paramBuilder.append("&")
.append(key)
.append("=")
.append(URLEncoder.encode(value, encode));
} catch (UnsupportedEncodingException e) {
throw new HatenaException(e);
}
}
}
String sig = md5.getHexString(sigBuilder.toString());
return AUTH_URL + "api_sig=" + sig + paramBuilder.toString();
}
/**
* certを元にユーザーデータを取得します。
* @param cert 認証ハンドラから取得したcert の値
* @return {@link User} はてなユーザーデータ
*/
public User login(String cert){
if (cert == null) {
throw new NullPointerException("cert is null");
}
StringBuilder sigBuilder = new StringBuilder(secret);
sigBuilder.append("api_key").append(apiKey);
sigBuilder.append("cert").append(cert);
String sig = md5.getHexString(sigBuilder.toString());
URL url = null;
try {
url = new URL(LOGIN_URL + "api_key=" + apiKey + "&cert=" + cert + "&api_sig=" + sig);
} catch (MalformedURLException e) {
throw new HatenaException(e);
}
SAXReader xmlReader = new SAXReader();
try {
return getUser(xmlReader.read(url));
} catch (DocumentException e) {
throw new HatenaException(e);
}
}
/**
* @param document
* @return
* @throws LoginException
*/
protected User getUser(Document document) {
User user = new User();
Element root = document.getRootElement();
if ("true".equals(root.elementText("has_error"))) {
throw new HatenaException(root.element("error").elementText("message"));
}
Element userElement = root.element("user");
user.setName(userElement.elementText("name"));
user.setImageURL(userElement.elementText("image_url"));
user.setThumbnailURL(userElement.elementText("thumbnail_url"));
return user;
}
/**
* @return
*/
public String getEncode() {
return encode;
}
/**
* @param encode パラメータの文字コード(デフォルトはUTF-8)
*/
public void setEncode(String encode) {
this.encode = encode;
this.md5.setEncoding(encode);
}
}