void

          Java安全有效的實(shí)現(xiàn)兩星期內(nèi)自動登陸功能

          http://stonejava.javaeye.com/blog/255136

          http://www.javaeye.com/topic/130309

          http://i18n.javaeye.com/blog/130849

          =========================================

          現(xiàn)在很多網(wǎng)站都有為用戶保存登陸信息(即保存Cookie)的功能,當(dāng)用戶下一次進(jìn)入網(wǎng)站時,可以幫助用戶自動登陸,使網(wǎng)站顯得更加友好。筆者通過 研究ACEGI項(xiàng)目的自動登陸源碼,編寫了一個安全有效的實(shí)現(xiàn)兩星期自動登陸功能的JAVA工具類,下面是具體的實(shí)現(xiàn)流程和實(shí)現(xiàn)代碼。

            先說一下流程:

            1.保存用戶信息階段:

            當(dāng)用戶登陸網(wǎng)站時,在登陸頁面填寫完用戶名和密碼后,如果用戶在提交時還選擇了“兩星期內(nèi)自動登陸”復(fù)選框,那么在后臺程序中驗(yàn)證用戶名和密碼 全都正確后,還要為用戶保存這些信息,以便用戶下一次可以直接進(jìn)入網(wǎng)站;如果用戶沒有勾選“兩星期內(nèi)自動登陸”復(fù)選框,則不必為用戶保存信息,那么用戶在 下一次登陸網(wǎng)站時仍需要填寫用戶名和密碼。

            在保存用戶信息階段,主要的工作是對用戶的信息進(jìn)行加密并保存到客戶端。加密用戶的信息是較為繁瑣的,大致上可分為以下幾個步聚:

             ① 得到用戶名、經(jīng)MD5加密后的用戶密碼、cookie有效時間(本文設(shè)置的是兩星期,可根據(jù)自己需要修改)
          ② 自定義的一個webKey,這個Key是我們?yōu)樽约旱木W(wǎng)站定義的一個字符串常量,這個可根據(jù)自己需要隨意設(shè)置
          ③ 將上兩步得到的四個值得新連接成一個新的字符串,再進(jìn)行MD5加密,這樣就得到了一個MD5明文字符串
          ④ 將用戶名、cookie有效時間、MD5明文字符串使用“:”間隔連接起來,再對這個連接后的新字符串進(jìn)行Base64編碼
          ⑤ 設(shè)置一個cookieName,將cookieName和上一步產(chǎn)生的Base64編碼寫入到客戶端。

            2.讀取用戶信息:

            其實(shí)弄明白了保存原理,讀取及校驗(yàn)原理就很容易做了。讀取和檢驗(yàn)可以分為下面幾個步驟:

             ① 根據(jù)設(shè)置的cookieName,得到cookieValue,如果值為空,就不幫用戶進(jìn)行自動登陸;否則執(zhí)行讀取方法
          ② 將cookieValue進(jìn)行Base64解碼,將取得的字符串以split(“:”)進(jìn)行拆分,得到一個String數(shù)組cookieValues(此操作與保存階段的第4步正好相反),這一步將得到三個值:

          cookieValues[0] ---- 用戶名
          cookieValues[1] ---- cookie有效時間
          cookieValues[2] ---- MD5明文字符串

             ③ 判斷cookieValues的長度是否為3,如果不為3則進(jìn)行錯誤處理。
          ④ 如果長度等于3,取出第二個,即cookieValues[1],此時將會得到有效時間(long型),將有效時間與服務(wù)器系統(tǒng)當(dāng)前時間比較,如果小于當(dāng)前時間,則說明cookie過期,進(jìn)行錯誤處理。
          ⑤ 如果cookie沒有過期,就取cookieValues[0],這樣就可以得到用戶名了,然后去數(shù)據(jù)庫按用戶名查找用戶。
          ⑥ 如果上一步返回為空,進(jìn)行錯誤處理。如果不為空,那么將會得到一個已經(jīng)封裝好用戶信息的User實(shí)例對象user
          ⑦ 取出實(shí)例對象user的用戶名、密碼、cookie有效時間(即cookieValues[1])、webKey,然后將四個值連接起來,然后進(jìn)行MD5加密,這樣做也會得到一個MD5明文字符串(此操作與保存階段的第3步類似)
          ⑧ 將上一步得到MD5明文與cookieValues[2]進(jìn)行equals比較,如果是false,進(jìn)行錯誤處理;如果是true,則將user對象添加到session中,幫助用戶完成自動登陸

            完整的代碼,用途請參見注釋

          CookieUtil.java

          處理cookie的工具類,包括讀取,保存,清除三個主要方法。

          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時的網(wǎng)站自定碼
          private final static String webKey = “itcast”;
          //設(shè)置cookie有效期是兩個星期,根據(jù)需要自定義
          private final static long cookieMaxAge = 60 * 60 * 24 * 7 * 2;
          //保存Cookie到客戶端--------------------------------------------------------------------------------------------------------
          //在CheckLogonServlet.java中被調(diào)用
          //傳遞進(jìn)來的user對象中封裝了在登陸時填寫的用戶名與密碼
          public static void saveCookie(User user, HttpServletResponse response) {

          //cookie的有效期
          long validTime = System.currentTimeMillis() + (cookieMaxAge * 1000);
          //MD5加密用戶詳細(xì)信息
          String cookieValueWithMd5 =getMD5(user.getUserName() + ":" + user.getPassword()+ ":" + validTime + ":" + webKey);
          //將要被保存的完整的Cookie值
          String cookieValue = user.getUserName() + ":" + validTime + ":" + cookieValueWithMd5;
          //再一次對Cookie的值進(jìn)行BASE64編碼
          String cookieValueBase64 = new String(Base64.encode(cookieValue.getBytes()));
          //開始保存Cookie
          Cookie cookie = new Cookie(cookieDomainName, cookieValueBase64);

          //存兩年(這個值應(yīng)該大于或等于validTime)
          cookie.setMaxAge(60 * 60 * 24 * 365 * 2);
          //cookie有效路徑是網(wǎng)站根目錄
          cookie.setPath("/");
          //向客戶端寫入
          response.addCookie(cookie);

          }

          //讀取Cookie,自動完成登陸操作--------------------------------------------------------------------------------------------

          //在Filter程序中調(diào)用該方法,見AutoLogonFilter.java
          public static void readCookieAndLogon(HttpServletRequest request, HttpServletResponse response,
          FilterChain chain) throws IOException, ServletException,UnsupportedEncodingException{
          //根據(jù)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不為空,才執(zhí)行下面的代碼

          //先得到的CookieValue進(jìn)行Base64解碼

          String cookieValueAfterDecode = new String (Base64.decode(cookieValue),"utf-8");

          //對解碼后的值進(jìn)行分拆,得到一個數(shù)組,如果數(shù)組長度不為3,就是非法登陸
          String cookieValues[] = cookieValueAfterDecode.split(":");
          if(cookieValues.length!=3){
          response.setContentType("text/html; charset=utf-8");
          PrintWriter out = response.getWriter();
          out.println("你正在用非正常方式進(jìn)入本站...");
          out.close();
          return;
          }
          //判斷是否在有效期內(nèi),過期就刪除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已經(jīng)失效,請重新登陸</a>");
          out.close();
          return;
          }

          //取出cookie中的用戶名,并到數(shù)據(jù)庫中檢查這個用戶名,
          String username = cookieValues[0];
          //根據(jù)用戶名到數(shù)據(jù)庫中檢查用戶是否存在
          UserDAO ud = DaoImplFactory.getInstance();

          User user = ud.selectUserByUsername(username);

          //如果user返回不為空,就取出密碼,使用用戶名+密碼+有效時間+ webSiteKey進(jìn)行MD5加密
          if(user!=null){
          String md5ValueInCookie = cookieValues[2];
          String md5ValueFromUser =getMD5(user.getUserName() + ":" + user.getPassword()+ ":" + validTimeInCookie + ":" + webKey);
          //將結(jié)果與Cookie中的MD5碼相比較,如果相同,寫入Session,自動登陸成功,并繼續(xù)用戶請求
          if(md5ValueFromUser.equals(md5ValueInCookie)){
          HttpSession session = request.getSession(true);
          session.setAttribute("user", user);
          chain.doFilter(request, response);
          }
          }else{
          //返回為空執(zhí)行
          response.setContentType("text/html; charset=utf-8");
          PrintWriter out = response.getWriter();
          out.println("cookie驗(yàn)證錯誤!");
          out.close();
          return;
          }
          }
          //用戶注銷時,清除Cookie,在需要時可隨時調(diào)用------------------------------------------------------------
          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;
          }

          //將傳遞進(jìn)來的字節(jié)數(shù)組轉(zhuǎn)換成十六進(jìn)制的字符串形式并返回

          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工具類各方法的調(diào)用演示:

          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中設(shè)置過濾規(guī)則,本文對過濾規(guī)則不作介紹,此程序主要作用是檢查用戶在上一次登陸時是否保存了Cookie,如果保存了,就處理Cookie信息,并幫助用戶自動登陸

          本程序主要調(diào)用了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中的設(shè)置相同

          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不為空,說明已經(jīng)登陸,則繼續(xù)執(zhí)行用戶的請求.下面的就不處理了

          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為空,也繼續(xù)執(zhí)行用戶請求
          if(cookieValue==null){
          chain.doFilter(request,response);
          return;
          }

          //cookieValue不為空執(zhí)行下面的方法,調(diào)用CookieUtil.java中的readCookieAndLogon方法
          try{
          CookieUtil.readCookieAndLogon(cookieValue, request, response, chain);
          }catch(Exception e){
          e.printStackTrace();
          }
          }

          public void init(FilterConfig arg0) throws ServletException {
          }
          }

          CheckLogonServlet.java

          驗(yàn)證用戶登陸信息的Servlet,此程序調(diào)用了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,這里調(diào)用了CookieUtil.java中的saveCookie方法,將上面的user對象作為參數(shù)傳遞
          if ("on".equals(remeberMe)) {
          CookieUtil.saveCookie(user, response);
          }
          //在Session中保存用戶信息,并轉(zhuǎn)向用戶的個人信息頁面
          session.setAttribute("user", user);
          request.getRequestDispatcher("User/userInfo.jsp").forward(request,response);
          }
          }
          UserDAO.java與DaoImplFactory.java屬于持久層相關(guān)的程序,這里就不貼出來了,讀者可根據(jù)自己需要選擇不同的持久層框架,在本程序中只要實(shí)現(xiàn)查詢用戶的功能就可以了

          posted on 2009-06-26 10:22 void 閱讀(496) 評論(0)  編輯  收藏 所屬分類: Java

          主站蜘蛛池模板: 鲁山县| 云和县| 北川| 佛坪县| 贵阳市| 满洲里市| 遂昌县| 武穴市| 宁明县| 县级市| 隆昌县| 吉水县| 通城县| 宜阳县| 双牌县| 礼泉县| 武隆县| 辽阳县| 平乡县| 阳朔县| 铁岭县| 习水县| 平遥县| 山丹县| 蛟河市| 雷波县| 清原| 太康县| 仪陇县| 肇州县| 许昌县| 永昌县| 汉阴县| 两当县| 镇巴县| 攀枝花市| 大渡口区| 九龙坡区| 保康县| 聊城市| 靖宇县|