安全有效的实现两星期内自动登陆功能

现在很多网站都有为用户保存登陆信息(即保存 Cookie )的功能,当用户下一次进入网站时,可以帮助用户自动登陆,使网站显得更加友好。笔者通过研究 ACEGI 项目的自动登陆源码,编写了一个安全有效的实现两星期自动登陆功能的 JAVA 工具类, 。下面是具体的实现流程和实现代码。

先说一下流程:

1.   保存用户信息阶段:

当 用户登陆网站时,在登陆页面填写完用户名和密码后,如果用户在提交时还选择了“两星期内自动登陆”复选框,那么在后台程序中验证用户名和密码全都正确后, 还要为用户保存这些信息,以便用户下一次可以直接进入网站;如果用户没有勾选“两星期内自动登陆”复选框,则不必为用户保存信息,那么用户在下一次登陆网 站时仍需要填写用户名和密码。

在保存用户信息阶段,主要的工作是对用户的信息进行加密并保存到客户端。加密用户的信息是较为繁琐的,大致上可分为以下几个步聚:

得到用户名、经 MD5 加密后的用户密码、 cookie 有效时间 ( 本文设置的是两星期,可根据自己需要修改 )

自定义的一个 webKey ,这个 Key 是我们为自己的网站定义的一个字符串常量,这个可根据自己需要随意设置

将上两步得到的四个值得新连接成一个新的字符串,再进行 MD5 加密,这样就得到了一个 MD5 明文字符串

将用户名、 cookie 有效时间、 MD5 明文字符串使用“:”间隔连接起来,再对这个连接后的新字符串进行 Base64 编码

设置一个 cookieName, cookieName 和上一步产生的 Base64 编码写入到客户端。

             

2.   读取用户信息:

其实弄明白了保存原理,读取及校验原理就很容易做了。读取和检验可以分为下面几个步骤:

根据设置的 cookieName ,得到 cookieValue ,如果值为空,就不帮用户进行自动登陆;否则执行读取方法

cookieValue 进行 Base64 解码,将取得的字符串以 split(“:”) 进行拆分,得到一个 String 数组 cookieValues (此操作与保存阶段的第 4 步正好相反),这一步将得到三个值:

       cookieValues[0] ---- 用户名

       cookieValues[1] ---- cookie 有效时间

       cookieValues[2] ---- MD5 明文字符串

判断 cookieValues 的长度是否为 3 ,如果不为 3 则进行错误处理。

如果长度等于 3 ,取出第二个 , cookieValues[1] ,此时将会得到有效时间( long 型),将有效时间与服务器系统当前时间比较,如果小于当前时间,则说明 cookie 过期,进行错误处理。

如果 cookie 没有过期,就取 cookieValues[0] ,这样就可以得到用户名了,然后去数据库按用户名查找用户。

如果上一步返回为空,进行错误处理。如果不为空,那么将会得到一个已经封装好用户信息的 User 实例对象 user

取出实例对象 user 的用户名、密码、 cookie 有效时间(即 cookieValues[1] )、 webKey ,然后将四个值连接起来,然后进行 MD5 加密,这样做也会得到一个 MD5 明文字符串(此操作与保存阶段的第 3 步类似)

将上一步得到 MD5 明文与 cookieValues[2] 进行 equals 比较,如果是 false ,进行错误处理;如果是 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 时的网站自定码

       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 中的 MD 5 相比较 , 如果相同 , 写入 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 组合字符串的 MD 5 的字符串 ----------------------------------------------------------------------------

              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

验证用户登陆信息的 Servlet ,此程序调用了 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 属于持久层相关的程序,这里就不贴出来了,读者可根据自己需要选择不同的持久层框架,在本程序中只要实现查询用户的功能就可以了

 

 

来源:http://blog.csdn.net/lyhapple/archive/2007/10/09/1817308.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值