2週間以内の自動ログイン機能を安全かつ効率的に実現
http://blog.csdn.net/lyhapple/archive/2007/10/09/1817308.aspx
現在、多くのサイトにはユーザーがログイン情報(すなわちCookie)を保存する機能があり、ユーザーが次のサイトにアクセスすると、ユーザーが自動的にログインするのを助けることができ、サイトをより友好的に見せることができます.筆者はACEGIプロジェクトの自動登録ソースコードを研究することにより,2週間の自動登録機能を安全かつ効率的に実現するJAVAツールクラスを作成した.以下に、具体的な実装プロセスと実装コードを示す.
まずプロセスについてお話しします.
1. ユーザー情報の保存フェーズ:
ユーザーがウェブサイトにログインする時、ログインページにユーザー名とパスワードを記入した後、ユーザーが提出する時に「2週間以内に自動的にログインする」チェックボックスを選択した場合、バックグラウンドプログラムでユーザー名とパスワードがすべて正しいことを検証した後、ユーザーのためにこれらの情報を保存し、ユーザーが次回直接ウェブサイトに入ることができるようにする.ユーザーが「2週間以内に自動的にログイン」チェックボックスにチェックマークを付けていない場合は、ユーザーの情報を保存する必要はありません.ユーザーは、次のWebサイトにログインするときにユーザー名とパスワードを記入する必要があります.
ユーザ情報の保存段階では,主な作業はユーザの情報を暗号化してクライアントに保存することである.暗号化されたユーザの情報は煩雑であり、大きく以下のステップに分けられる.
① ユーザー名、MD 5で暗号化されたユーザーパスワード、クッキー有効時間を取得します(本明細書では2週間設定しており、必要に応じて変更できます)
② カスタマイズされたwebKey、このKeyは私たちが自分のウェブサイトのために定義した文字列定数で、これは自分の必要に応じて勝手に設定することができます
③ 前の2つのステップで得られた4つの値を新しい文字列に接続し、MD 5暗号化を行うと、MD 5の明文文字列が得られます.
④ ユーザ名、クッキー有効時間、MD 5明文文字列を":"間隔で接続し、この接続後の新しい文字列をBase 64符号化する
⑤ cookieNameを設定し、cookieNameと前のステップで生成したBase 64符号化をクライアントに書き込みます.
2. ユーザー情報を読み込みます.
保存原理がわかっていれば、読み取りや検証が簡単にできます.読み取りと検証は、次のステップに分けられます.
① 設定されたCookieNameに基づいて、cookieValueが得られ、値が空であれば、ユーザーの自動ログインを支援しません.そうでなければ読み取り方法を実行します
② CookieValueをBase 64復号化し、取得した文字列をsplit(":")で分割し、String配列cookieValues(この動作は保存フェーズの4ステップ目とは正反対)を得、このステップで3つの値を得る.
cookieValues[0] ---- ユーザー名
cookieValues[1]----cookie有効時間
CookieValues[2]----MD 5明文文字列
③ CookieValuesの長さが3であるか否かを判断し、3でない場合はエラー処理を行う.
④ 長さが3に等しい場合、2番目のcookieValues[1]を取り出すと、有効時間(long型)が得られ、有効時間をサーバシステムの現在時間と比較し、現在時間未満であればcookieが期限切れであり、エラー処理が行われることを示す.
⑤ cookieが期限切れになっていない場合は、cookieValues[0]を取得し、ユーザー名を取得し、データベースに行ってユーザー名でユーザーを検索します.
⑥ 前のステップが空に戻った場合は、エラー処理を行います.空でない場合、ユーザー情報がカプセル化されたUserインスタンスオブジェクトuserが得られます.
⑦ インスタンスオブジェクトuserのユーザ名、パスワード、クッキー有効時間(すなわちcookieValues[1])、webKeyを取り出し、4つの値を接続してMD 5暗号化を行うと、MD 5の明文文字列(保存フェーズの3ステップ目と同様)が得られます.
⑧ 前回得られたMD 5明文をcookieValues[2]とequals比較し、falseであればエラー処理を行う.trueの場合、userオブジェクトをセッションに追加し、ユーザーの自動ログインの完了を支援します.
完全なコード、用途はコメントを参照してください
CookieUtil.java
クッキーを処理するツールクラスは、読み取り、保存、クリアの3つの主要な方法を含む.
CookieUtilツールクラスの各メソッドの呼び出しのデモを次に示します.
User.java
ユーザー情報をカプセル化するJavaBeanオブジェクトモデル
package com.itcast.bean;
AutoLogonFilter.java
フィルタプログラムは、WEB-INF/web.xmlにフィルタルールを設定できます.本稿ではフィルタルールについて説明しません.このプログラムの主な役割は、ユーザーが前回のログイン時にCookieを保存したかどうかを確認し、保存したらCookie情報を処理し、ユーザーの自動ログインを支援することです.
このプログラムは主にCookieUtil.javaの読み取りと自動ログイン方法、すなわちreadCookieAndLogon方法を呼び出します.
CheckLogonServlet.java
CookieUtil.javaのsaveCookieメソッドを呼び出すユーザーログイン情報のサーブレットを確認します.
UserDAO.javaとDaoImplFactory.javaは永続層関連のプログラムに属しているが、ここでは貼らない.読者は自分の必要に応じて異なる永続層フレームワークを選択することができ、本プログラムではユーザーを検索する機能を実現すればよい.
現在、多くのサイトにはユーザーがログイン情報(すなわちCookie)を保存する機能があり、ユーザーが次のサイトにアクセスすると、ユーザーが自動的にログインするのを助けることができ、サイトをより友好的に見せることができます.筆者はACEGIプロジェクトの自動登録ソースコードを研究することにより,2週間の自動登録機能を安全かつ効率的に実現するJAVAツールクラスを作成した.以下に、具体的な実装プロセスと実装コードを示す.
まずプロセスについてお話しします.
1. ユーザー情報の保存フェーズ:
ユーザーがウェブサイトにログインする時、ログインページにユーザー名とパスワードを記入した後、ユーザーが提出する時に「2週間以内に自動的にログインする」チェックボックスを選択した場合、バックグラウンドプログラムでユーザー名とパスワードがすべて正しいことを検証した後、ユーザーのためにこれらの情報を保存し、ユーザーが次回直接ウェブサイトに入ることができるようにする.ユーザーが「2週間以内に自動的にログイン」チェックボックスにチェックマークを付けていない場合は、ユーザーの情報を保存する必要はありません.ユーザーは、次のWebサイトにログインするときにユーザー名とパスワードを記入する必要があります.
ユーザ情報の保存段階では,主な作業はユーザの情報を暗号化してクライアントに保存することである.暗号化されたユーザの情報は煩雑であり、大きく以下のステップに分けられる.
① ユーザー名、MD 5で暗号化されたユーザーパスワード、クッキー有効時間を取得します(本明細書では2週間設定しており、必要に応じて変更できます)
② カスタマイズされたwebKey、このKeyは私たちが自分のウェブサイトのために定義した文字列定数で、これは自分の必要に応じて勝手に設定することができます
③ 前の2つのステップで得られた4つの値を新しい文字列に接続し、MD 5暗号化を行うと、MD 5の明文文字列が得られます.
④ ユーザ名、クッキー有効時間、MD 5明文文字列を":"間隔で接続し、この接続後の新しい文字列をBase 64符号化する
⑤ cookieNameを設定し、cookieNameと前のステップで生成したBase 64符号化をクライアントに書き込みます.
2. ユーザー情報を読み込みます.
保存原理がわかっていれば、読み取りや検証が簡単にできます.読み取りと検証は、次のステップに分けられます.
① 設定されたCookieNameに基づいて、cookieValueが得られ、値が空であれば、ユーザーの自動ログインを支援しません.そうでなければ読み取り方法を実行します
② CookieValueをBase 64復号化し、取得した文字列をsplit(":")で分割し、String配列cookieValues(この動作は保存フェーズの4ステップ目とは正反対)を得、このステップで3つの値を得る.
cookieValues[0] ---- ユーザー名
cookieValues[1]----cookie有効時間
CookieValues[2]----MD 5明文文字列
③ CookieValuesの長さが3であるか否かを判断し、3でない場合はエラー処理を行う.
④ 長さが3に等しい場合、2番目のcookieValues[1]を取り出すと、有効時間(long型)が得られ、有効時間をサーバシステムの現在時間と比較し、現在時間未満であればcookieが期限切れであり、エラー処理が行われることを示す.
⑤ cookieが期限切れになっていない場合は、cookieValues[0]を取得し、ユーザー名を取得し、データベースに行ってユーザー名でユーザーを検索します.
⑥ 前のステップが空に戻った場合は、エラー処理を行います.空でない場合、ユーザー情報がカプセル化されたUserインスタンスオブジェクトuserが得られます.
⑦ インスタンスオブジェクトuserのユーザ名、パスワード、クッキー有効時間(すなわちcookieValues[1])、webKeyを取り出し、4つの値を接続してMD 5暗号化を行うと、MD 5の明文文字列(保存フェーズの3ステップ目と同様)が得られます.
⑧ 前回得られたMD 5明文をcookieValues[2]とequals比較し、falseであればエラー処理を行う.trueの場合、userオブジェクトをセッションに追加し、ユーザーの自動ログインの完了を支援します.
完全なコード、用途はコメントを参照してください
CookieUtil.java
クッキーを処理するツールクラスは、読み取り、保存、クリアの3つの主要な方法を含む.
package cn.itcast.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import cn.itcast.bean.User;
import cn.itcast.dao.UserDAO;
import cn.itcast.factory.DaoImplFactory;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
/*
* 2007.09.21 by lyhapple
* */
public class CookieUtil {
// cookie cookieName
private final static String cookieDomainName = “cn.itcast”;
// cookie
private final static String webKey = “itcast”;
// cookie ,
private final static long cookieMaxAge = 60 * 60 * 24 * 7 * 2;
// Cookie --------------------------------------------------------------------------------------------------------
// CheckLogonServlet.java
// user
public static void saveCookie(User user, HttpServletResponse response) {
//cookie
long validTime = System.currentTimeMillis() + (cookieMaxAge * 1000);
//MD5
String cookieValueWithMd5 =getMD5(user.getUserName() + ":" + user.getPassword()
+ ":" + validTime + ":" + webKey);
// Cookie
String cookieValue = user.getUserName() + ":" + validTime + ":" + cookieValueWithMd5;
// Cookie BASE64
String cookieValueBase64 = new String(Base64.encode(cookieValue.getBytes()));
// Cookie
Cookie cookie = new Cookie(cookieDomainName, cookieValueBase64);
// ( validTime)
cookie.setMaxAge(60 * 60 * 24 * 365 * 2);
//cookie
cookie.setPath("/");
//
response.addCookie(cookie);
}
// Cookie, --------------------------------------------------------------------------------------------
// Filter , AutoLogonFilter.java
public static void readCookieAndLogon(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException,UnsupportedEncodingException{
// cookieName cookieValue
Cookie cookies[] = request.getCookies();
String cookieValue = null;
if(cookies!=null){
for(int i=0;i<cookies.length;i++){
if (cookieDomainName.equals(cookies[i].getName())) {
cookieValue = cookies[i].getValue();
break;
}
}
}
// cookieValue , ,
if(cookieValue==null){
return;
}
// cookieValue ,
// CookieValue Base64
String cookieValueAfterDecode = new String (Base64.decode(cookieValue),"utf-8");
// , , 3,
String cookieValues[] = cookieValueAfterDecode.split(":");
if(cookieValues.length!=3){
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println(" ...");
out.close();
return;
}
// , Cookie
long validTimeInCookie = new Long(cookieValues[1]);
if(validTimeInCookie < System.currentTimeMillis()){
// Cookie
clearCookie(response);
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<a href=’logon.jsp’> Cookie , </a>");
out.close();
return;
}
// cookie , ,
String username = cookieValues[0];
//
UserDAO ud = DaoImplFactory.getInstance();
User user = ud.selectUserByUsername(username);
// user , , + + + webSiteKey MD5
if(user!=null){
String md5ValueInCookie = cookieValues[2];
String md5ValueFromUser =getMD5(user.getUserName() + ":" + user.getPassword()
+ ":" + validTimeInCookie + ":" + webKey);
// Cookie MD5 , , Session, ,
if(md5ValueFromUser.equals(md5ValueInCookie)){
HttpSession session = request.getSession(true);
session.setAttribute("user", user);
chain.doFilter(request, response);
}
}else{
//
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("cookie !");
out.close();
return;
}
}
// , Cookie, ------------------------------------------------------------
public static void clearCookie( HttpServletResponse response){
Cookie cookie = new Cookie(cookieDomainName, null);
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
}
// Cookie MD5 ----------------------------------------------------------------------------
public static String getMD5(String value) {
String result = null;
try{
byte[] valueByte = value.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(valueByte);
result = toHex(md.digest());
} catch (NoSuchAlgorithmException e2){
e1.printStackTrace();
}
return result;
}
//
private static String toHex(byte[] buffer){
StringBuffer sb = new StringBuffer(buffer.length * 2);
for (int i = 0; i < buffer.length; i++){
sb.append(Character.forDigit((buffer[i] & 0xf0) >> 4, 16));
sb.append(Character.forDigit(buffer[i] & 0x0f, 16));
}
return sb.toString();
}
}
CookieUtilツールクラスの各メソッドの呼び出しのデモを次に示します.
User.java
ユーザー情報をカプセル化するJavaBeanオブジェクトモデル
package com.itcast.bean;
public class User {
private int id;
private String userName;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
AutoLogonFilter.java
フィルタプログラムは、WEB-INF/web.xmlにフィルタルールを設定できます.本稿ではフィルタルールについて説明しません.このプログラムの主な役割は、ユーザーが前回のログイン時にCookieを保存したかどうかを確認し、保存したらCookie情報を処理し、ユーザーの自動ログインを支援することです.
このプログラムは主にCookieUtil.javaの読み取りと自動ログイン方法、すなわちreadCookieAndLogon方法を呼び出します.
package cn.itcast.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import cn.itcast bean.User;
import cn.itcast.util.CookieUtil;
public class AutoLogonFilter implements Filter {
public void destroy() {
}
// cookie cookieName, CookieUtil.java
private final static String cookieDomainName = “cn.itcast”;
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
HttpSession session = request.getSession(true);
User user = (User)session.getAttribute("user");
// user , , .
if(user!=null){
chain.doFilter(request,response);
return;
}
//user , , Cookie
Cookie cookies[] = request.getCookies();
String cookieValue = null;
if(cookies!=null){
for(int i=0;i<cookies.length;i++){
if (cookieDomainName.equals(cookies[i].getName())) {
cookieValue = cookies[i].getValue();
break;
}
}
}
// cookieValue ,
if(cookieValue==null){
chain.doFilter(request,response);
return;
}
//cookieValue , CookieUtil.java readCookieAndLogon
try{
CookieUtil.readCookieAndLogon(cookieValue, request, response, chain);
}catch(Exception e){
e.printStackTrace();
}
}
public void init(FilterConfig arg0) throws ServletException {
}
}
CheckLogonServlet.java
CookieUtil.javaのsaveCookieメソッドを呼び出すユーザーログイン情報のサーブレットを確認します.
package cn.itcast.servlet;
/*
* update 2007.09.23 by lyhapple
*
* */
import java.io.IOException;
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 cn.itcast.bean.User;
import cn.itcast.dao.UserDAO;
import cn.itcast.factory.DaoImplFactory;
import cn.itcast.util.CookieUtil;
public class CheckLogonServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username").trim();
String password = CookieUtil.getMD5(request.getParameter("password"));
String remeberMe = request.getParameter("remeberMe");
HttpSession session = request.getSession(false);
// UserDao checkUser ,
// User
UserDAO ud = DaoImplFactory.getInstance();
User user = ud.selectUserByUsername(username);
if (user == null) {
request.setAttribute("checkUserError","<a href='register.jsp'><font color=red> , </font></a>");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}
if(!password.equals(user.getPassword())){
request.setAttribute("checkPasswordError","<font color=red> , </font>");
request.getRequestDispatcher("index.jsp").forward(request, response);
return;
}
// Cookie, CookieUtil.java saveCookie , user
if ("on".equals(remeberMe)) {
CookieUtil.saveCookie(user, response);
}
// Session ,
session.setAttribute("user", user);
request.getRequestDispatcher("User/userInfo.jsp").forward(request,response);
}
}
UserDAO.javaとDaoImplFactory.javaは永続層関連のプログラムに属しているが、ここでは貼らない.読者は自分の必要に応じて異なる永続層フレームワークを選択することができ、本プログラムではユーザーを検索する機能を実現すればよい.