`
rayn115
  • 浏览: 68832 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

    博客分类:
  • 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
            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("");你的Cookie已经失效,请重新登陆
                     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 e){
                      e.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
                            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","");用户名不存在,请先注册
                     request.getRequestDispatcher("index.jsp").forward(request, response);
                     return;
              }
              if(!password.equals(user.getPassword())){
                     request.setAttribute("checkPasswordError","");密码输入错误,请重新输入
                     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://i18n.iteye.com/blog/130306)

分享到:
评论

相关推荐

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

    教你用Java安全有效的实现两星期内自动登陆功能-Session

    别墅智能化安防系统设计方案.doc

    三、系统设计 本系统设计以EAVS数字网络报警视频服务器为前端控制器,系统功能强大,具有更高 级别的险情判断和处理能力,利用先进的网络通讯技术、电气自动化技术、计算机技术 以及无线电技术将安全防范与家居生活...

    asp.net知识库

    HttpModule 实现 ASP.Net (*.aspx) 中文简繁体的自动转换,不用修改原有的任何代码,直接部署即可! 服务器自定义开发二之客户端脚本回发 Web开发: 使用URL重写WEB主题切换 如何在Asp.Net1.1中实现页面模板(所谓的...

    网上会展的未来发展趋势

    展会前:建设展会的互联网商务平台,发布展会信息,有效利用网络优势进行展会推广、展会招商、展位预定、服务合作、服务预定、参展商信息发布、网上观众预定、网上调研等,建立包含多功能的大型数据库,采用三层结构...

    delphi 开发经验技巧宝典源码

    0206 如何在DBGrid中实现复制、粘贴功能 137 0207 在DBGrid中将选中的多行删除 137 0208 在DataGrid中如何使标题文字居中 138 0209 如何把ListBox中的内容拖曳到另一个ListBox中 139 0210 把DBGrid中的数据...

    delphi 开发经验技巧宝典源码06

    0206 如何在DBGrid中实现复制、粘贴功能 137 0207 在DBGrid中将选中的多行删除 137 0208 在DataGrid中如何使标题文字居中 138 0209 如何把ListBox中的内容拖曳到另一个ListBox中 139 0210 把DBGrid中的数据...

    Tcl_TK编程权威指南pdf

    虽然这种模式仍然是有效的,但结果表明用Tcl来编写整个应用程序也是可能的。这是因为Tcl/Tk的shell程序wish提供了对其他程序、文件系统和网络套接字的存取功能,同时还能够创建图形用户界面。不管怎样,现在发现...

    C#编程经验技巧宝典

    2 &lt;br&gt;0003 设置程序代码行序号 3 &lt;br&gt;0004 开发环境全屏显示 3 &lt;br&gt;0005 设置窗口的自动隐藏功能 3 &lt;br&gt;0006 根据需要创建所需解决方案 4 &lt;br&gt;0007 如何使用“验证的目标架构”功能 4 ...

    入门学习Linux常用必会60个命令实例详解doc/txt

    虚拟控制台的切换可以通过按下Alt键和一个功能键来实现,通常使用F1-F6 。 例如,用户登录后,按一下“Alt+ F2”键,用户就可以看到上面出现的“login:”提示符,说明用户看到了第二个虚拟控制台。然后只需按“Alt+...

    SuperNotepad

    信息加密算法安全高效,可任意深度加密; 4.右键集合了大多数常用功能,不常用的在"工具"中; 5.主要的特色功能︰ 00) 语音朗读文本 (注:[自定义欢迎词]中可更改欢迎语音; XP下初始只有一个缺省语音引擎,为...

    Oracle9i的init.ora参数中文说明

    该参数的值可以是包含在双引号内的任何有效的日期格式掩码。例如: ''MMM/DD/YYYY''。 值范围: 任何有效的日期格式掩码, 但不得超过一个固定长度。 默认值: 派生 nls_timestamp_tz_format: 说明: 与 NLS_TIME_TZ_...

    C语言FAQ 常见问题列表

    o 3.4 在 C 语言中实现抽象数据类型什么方法最好? o 3.5 在 C 中是否有模拟继承等面向对象程序设计特性的好方法? o 3.6 我遇到这样声明结构的代码: struct name { int namelen; char namestr[1];}; 然后又使用...

    你必须知道的495个C语言问题

    3.7 是否可以安全地认为,一旦&&和||左边的表达式已经决定了整个表达式的结果,则右边的表达式不会被求值? 3.8 为什么表达式printf("%d%d",f1(),f2());先调用了f2?我觉得逗号表达式应该确保从左到右的求值顺序...

    freemarker总结

    ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x&gt; ${x_index + 1}.${x}, 星期四"&gt;&lt;#break&gt;&lt;/#if&gt; &lt;p&gt;We have these animals: &lt;tr&gt;&lt;th&gt;Name&lt;th&gt;Price &lt;tr&gt;&lt;td&gt;${...

Global site tag (gtag.js) - Google Analytics