最近接手了之前的一个项目,在看里面登陆模块的时候,遇到了一堆问题。现在记录下来。

这个登陆模块的逻辑是这样的

1 首先在登陆之前,调用后台的UserLoginAction类的getRandomKey方法产生一个随机字符串。

2 在前台获得用户名的登陆密码后,首先是要md5对其加密,之后把加密的结果与之前的随机字符串合并,使用md5再次加密,并把最后的结果作为用户的密码传给后台。

3 后台获得前台的用户名后(用户名全局唯一),先找出这个用户的密码(数据库里的真实密码),先用md5加密,再与第一步产生的随机字符串合并,之后使用md5二次加密

4 比照第三步产生的密码与从前台获得的密码。如果相等,说明登陆成功。

OK,我们看代码

起始页面:

//index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ taglib prefix="s" uri="/struts-tags"%>
    <s:action name="index" namespace="/" executeResult="true" var="rd"/>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

</body>
</html>

index.jsp里面只有一个action请求。

看看struts.xml的配置情况

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <package name="core" namespace="/" extends="struts-default">

             <action name="index" class="userLoginAction"     method="getRandomKey">
                 <result name="success">login.jsp</result>
        </action>

        <action name="login" class="userLoginAction" method="login">
            <result name="success">main.jsp</result>
            <result name="input">login.jsp</result>
            <result name="error">login.jsp</result>
        </action>
    </package>
</struts>

我引入了Spirng来管理类。

看看UserLoginAction

注意要加上prototype

/**
 * @author dell
 *
 */
@Component
@Scope("prototype")
public class UserLoginAction extends BaseAction{

    /**
     * 接收JSP传来的账号与密码
     */
    private Users user;

    /**
     * 随机字符串作为加密密钥
     */
    private String randomString;

    /**
     * 生成随机字符串作为密钥
     */
    public String getRandomKey() {

        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        int length = 6;
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
    //    logger.info("randomString: " + sb.toString());
        this.randomString = sb.toString();
        return SUCCESS;
    }
}

我们再看看返回的login.jsp

这个文件引入了MD5.js。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>短波应急接入网管理系统</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta http-equiv="pragma" content="no-cache" />
        <meta http-equiv="cache-control" content="no-cache" />
        <meta http-equiv="expires" content="0" />
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3" />

        <link rel="shortcut icon" href="logo.ico" type="image/x-icon" />
        <link rel="stylesheet" type="text/css" href="css/common.css">

        <script type="text/javascript" src="easyui/jquery.min.js"></script>
        <script type="text/javascript" src="easyui/jquery.easyui.min.js"></script>

        <script type="text/javascript" src="js/md5.js"></script>

</head>
<body onload="document.getElementById('loginName').focus()">
<div id="page">
    <div class="t">
                    <center>
                        <span class="flderr">
                             <s:fielderror>
                                <s:param>error_msg</s:param>
                             </s:fielderror> </span>
                    </center>
    </div>
     <form action="login" id="login" method="post" class="ym_login_form" >
        <div class="ymlf_row">
            <span>用户编号:</span>
            <input type="text" id="loginName" name="user.userName" class="ymlf_name" />
        </div>
        <div class="ymlf_row">
            <span>用户密码:</span>

            <input type="password" id="loginPassword" name="user.passWord" class="ymlf_password" />
            <input type="hidden" id="randomString"  name="randomString" value=<s:property value="randomString"/> />
        //为什么要加上randomString?自己想一想
        </div>
        <div class="ymlf_btn_box">
            <img src="data:images/bt1.png" class="sub"  onclick="mysubmit()" />
             
            <img src="data:images/bt2.png" class="res"  onclick="myreset()"  />
        </div>

    </form>
</div>
</body>

        <script type="text/javascript">
        function myreset(){
             $('#login').form('clear');
        }
        function mysubmit(){
             var frm = document.getElementById("login");

            if(check(frm)==true)
            frm.submit();
        }
        function check(frm) {
            var lgnName = frm.loginName.value;
            var lgnPassword = frm.loginPassword.value;
            if(lgnName=='') {
                $('.flderr').html("请输入用户名");
                frm.loginName.focus();
                return false;
            }
            if(lgnPassword=='') {
                $('.flderr').html("请输入密码");
                frm.loginPassword.focus();
                return false;
            }

            var randomString = '<s:property value="randomString" />';
            lgnPassword = hex_md5(lgnPassword);
            lgnPassword = lgnPassword+randomString;
            lgnPassword = hex_md5(lgnPassword);
            $('#login').form('load',{
                'user.passWord':lgnPassword
            });

            return true;
        }

        document.onkeypress   =
            function(event) {
                var e = event||window.event;
                var ele = e.target||e.srcElement;
                var k = e.which||e.keyCode;
                if(k == 13 && ele.id == 'loginPassword')
                    mysubmit();
        }
</script>
</html>

提交的结果给了login那个aciton,最后调用的是UserLoginAction的login方法

         /**
     * 用户登录处理
     *
     * @return "center_login" or "dept_login" or ERROR
     */
    @SuppressWarnings("unchecked")
    public String login() {

        if (user == null || user.getUserName() == null
                || user.getPassWord() == null) {
            return ERROR;
        }

        // 从客户端传来的加密后的密码
        byte[] clientPassword = user.getPassWord().trim().getBytes();

        //逻辑就是这样 我自己写的dao就不给大家看了
        //根据前台传过来的用户名从数据库取得用户
                List<Users> resultList = (List<Users>) utilDAO.findListByProperty("Users", "userName",user.getUserName() ,"");

        if (resultList.isEmpty()) { // 该账号不存在
            this.addFieldError("error_msg", "该账号不存在");
             logger.info("该账号不存在!!");
            return ERROR;
        } else {
            user = resultList.get(0);

            // 对用户真实的密码作同样的加密处理

            String passwd = (String) user.getPassWord();
            System.out.println("passwd  真实的:"+passwd);
            MessageDigest md5 = null;
            byte[] serverPassword = null;
            try {
                md5 = MessageDigest.getInstance("MD5");
                md5.update(passwd.getBytes());
                passwd = new BigInteger(1, md5.digest()).toString(16);
                //md5(890617)后的之为09aaa5d27d99ad09a129a0734d52519b
                //字符串以0开头,用BigInteger会把开头的0截去,导致位数减少,所以需要判断并补0
                if (passwd.length() < 32) {
                    for (int i = 0; i <32- passwd.length(); i++) {
                        passwd = '0' + passwd;
                    }
                }
                passwd = passwd + this.randomString;            //从前台通过s:hidden传来的randomString
                System.out.println(randomString+"   aaaa"+"  "+this);
                md5.update(passwd.getBytes());
                serverPassword = (new BigInteger(1, md5.digest()).toString(16))
                        .getBytes();

            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }

            if (!MessageDigest.isEqual(clientPassword, serverPassword)) {
                this.addFieldError("error_msg", "用户名或密码错误");
                logger.info("用户名或密码错误");
                return ERROR;
            }
            this.getSession().put("user", user);
            logger.info("登陆成功");
            getLevel();
            return SUCCESS;
        }
    }

在改写这个功能的时候,遇到很多问题。对就这么一个小小的登陆,实在是没有想到会碰到那么多问题。

问题1

<s:action name="index" namespace="/" executeResult="true" var="rd"/> 这是让后台产生随机字符串的action调用。

   如果把它放在login.jsp里,那么xml的配置文件里是否需要写result?

   如果不写result,在前台取不到randomString。这个很容易理解,你只是在login.jsp里调用这个action,login.jsp里凭什么能取到action里面的值呢?

   如果写result,按照下面的形式

                <action name="index" class="userLoginAction"     method="getRandomKey">

                 <result name="success">login.jsp</result>

        </action>

   会出现action与jsp调用的死循环。为什么?大家自己想。

我没有办法,最后把让后台产生随机字符串的action调用放到了一个新的index.jsp里面,这个页面的唯一作用就是调用index这个action,返回的结果给login.jsp。

****************************************************

人蠢没办法。

前台直接获取调用action的值方法如下:

前台jsp调用action

//son1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<s:action name="index1" namespace="/" executeResult="false" var="rd"/>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%--  上面已经指定了acton的变量名为rd 这里直接用就是了 当然前面要加上#    --%>
<%--  另外atr1这个变量在下面的s:debug里是不存在的--%>
 <s:property value="#rd.atr1"/>

<s:debug>
</s:debug>

</body>
</html>

struts.xml

<package name="testchain" namespace="/" extends="struts-default">
       <action name="index1" class="chain.Action1">
       </action>
</package>
public class Action1 extends ActionSupport {
    /**
     *
     */
    private static final long serialVersionUID = 4114388780011017030L;
    private String atr1;
    private String atr2;

    public String execute() throws Exception {
        System.out.println("set in action1");
        atr1="dlf";
        return SUCCESS;    

    }
    //省略getset方法
 }

问题2

UserLoginAction应该是单例还是多例。

从技术上来说,下面的就是多例

@Component

@Scope("prototype")

public class UserLoginAction extends BaseAction{

去掉 @Scope("prototype")之后就是单例。

如果是单例的话,用户名与密码在高并发下会发生覆盖问题。所以我们得采用多例。任何一个请求都对应唯一的一个action。

那么又出了一个新问题

问题3

请求index这个action,后台产生了随机字符串,我们可以在login.jsp里访问随机字符串,但是

<action name="login" class="userLoginAction" method="login">

            <result name="success">main.jsp</result>

            <result name="input">login.jsp</result>

            <result name="error">login.jsp</result>

        </action>    

这个action对应的对象是一个全新的对象,它的getRandomKey()方法还没有调用过。它里面的随机字符串还是空值呢。

最后的解决方法是给login.jsp上加上这行代码

<input type="hidden" id="randomString"  name="randomString" value=<s:property value="randomString"/> />

它是怎么解决问题的?自己想一想。

使用MD5加密的登陆demo的更多相关文章

  1. md5加密用户登陆遇到的问题及解决办法

    有个项目的登陆模块使用到了cas,应需求要求,用户名和密码传输时使用了md5加密模式,加密的密码可以直接保存在数据库,但是加密的用户名则必须解密出来才行,于是后台的java代码中便写了针对用户名的解密 ...

  2. Java三行代码搞定MD5加密,测试5c短信网关的demo

    看到之前项目中,关于MD5加密的足足写了一个辅助类. 其实在Java中大部分都帮你实现好了,完成MD5加密,主要就三行代码: /** * 对字符串md5加密 * * @param str * @ret ...

  3. Java 自带MD5加密 Demo

    package demo; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; pub ...

  4. JAVAEE——SSH项目实战05:用户注册、登陆校验拦截器、员工拜访客户功能和MD5加密

    作者: kent鹏 转载请注明出处: http://www.cnblogs.com/xieyupeng/p/7170519.html 一.用户注册   显示错误信息到页面上的另一种方法: public ...

  5. Java和JS MD5加密-附盐值加密demo

    JAVA和JS的MD5加密 经过测试:字母和数据好使,中文不好使. 源码如下: ** * 类MD5Util.java的实现描述: * */public class MD5Util { // 获得MD5 ...

  6. iOS,一行代码进行RSA、DES 、AES、MD5加密、解密

    本文为投稿文章,作者:Flying_Einstein(简书) 加密的Demo,欢迎下载 JAVA端的加密解密,读者可以看我同事的这篇文章:http://www.jianshu.com/p/98569e ...

  7. iOS MD5加密

    1.MD5加密 Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.该算法的文件号为RFC 1321 ...

  8. IOS中把字符串加密/IOS中怎么样MD5加密/IOS中NSString分类的实现

    看完过后,你会学到: 1学习IOS开发中的分类实现, 2以及类方法的书写, 3以及字符串的MD5加密/解密. ---------------------------wolfhous---------- ...

  9. MD5加密相关

    demo效果

随机推荐

  1. 每CPU变量

    最好的同步技术是把设计不需要同步的临界资源放在首位,这是一种思维方法,因为每一种显式的同步原语都有不容忽视的性能开销.最简单也是最重要的同步技术包括把内核变量或数据结构声明为每CPU变量(per-cp ...

  2. pdflush进程详解

    一.简介     由于页高速缓存的缓存作用,写操作实际上会被延迟.当页高速缓存中的数据比后台存储的数据更新时,那么该数据就被称做脏数据.在内存中累积起来的脏页最终必须被写回磁盘.在以下两种情况发生时, ...

  3. 微信小程序基础之表单Form的使用

    表单Form的应用很广泛,我们可以利用form设计登录注册,也可以设计一种答题问卷的形式,今天主要讲一下form的使用 form表单,将组件内输入的"switch","i ...

  4. Openresty 数据共享API.Data Sharing within an Nginx Worker

    摘要自:https://github.com/openresty/lua-nginx-module/#data-sharing-within-an-nginx-worker 每nginx worker ...

  5. 用scheme重写Python的三大函数map reduce 和filter

    重写过程中,发现这种做法能加深对递归的理解,而且reduce还体现了函数式编程是如何通过参数传递来实现命令式编程中的状态改变的. (define (imap f x . y) (if (null? y ...

  6. FORM开发实现动态LOV

    方法一.设置lov的记录组 IF :mat_trx.trx_action_id = 1 THEN set_lov_property('lot_number',group_name,'lot_issue ...

  7. Java学习之二维数组定义与内存分配详解

    二维数组:就是元素为一维数组的一个数组. 格式1: 数据类型[][] 数组名 = new 数据类型[m][n]; m:表示这个二维数组有多少个一维数组. n:表示每一个一维数组的元素有多少个. 注意: ...

  8. 看见的力量 – (II) 影响地图

    本文转自台湾的李智桦老师的博客,原文地址 Impact Mapping 真是令人惊艳的可视化工具.等你看完这篇文章,你会爱上它的. 典故 继2011年6月Example of specificatio ...

  9. EPnP算法

    EPnP算法 相机坐标系用\(F^c\),世界坐标系用\(F^w\)表示,任何一点可以用四个控制点\(p_i^w\)表示 \begin{equation} p_i^w=\sum_{j=1}^4\alp ...

  10. 最详细的制作正式版10.11 OS X El Capitan 安装U盘的方法

    原帖地址:http://bbs.feng.com/read-htm-tid-10036487.html 一.准备工作: 1.准备一个 8GB 或以上容量的 U 盘,确保里面的数据已经妥善备份好(该过程 ...