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つの主要な方法を含む.
 
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は永続層関連のプログラムに属しているが、ここでは貼らない.読者は自分の必要に応じて異なる永続層フレームワークを選択することができ、本プログラムではユーザーを検索する機能を実現すればよい.