`
天之痕苏
  • 浏览: 18499 次
文章分类
社区版块
存档分类
最新评论

"忘记密码"功能过程及其实现细节

 
阅读更多

对于忘记密码功能,一般都是通过2种方式找回:一种是通过预留电话号码发送验证码找回,另一个是通过设定邮箱找回。对于具体的找回流程,参见:http://www.yixieshi.com/ucd/9207.html

这里结合目前做的项目,详细的说明一下密码找回的过程。这里用的是邮箱找回。根据一般的忘记密码的流程来说明。我采用的流程是:

  • 流程

【登录】 --> 【点击忘记密码】 --> 【输入个人邮箱和验证码】 --> 【系统发送邮箱验证】 --> 【用户在限定时间内登录邮箱,点击链接,进入重置密码页面】 --> 【重置密码完毕,点击进入登录界面】。

  • 登录页面

先给出我做的登录界面,其中包含"忘记密码"功能


一般登录界面都有“忘记密码”选项,这里不多说。

  • "忘记密码"页面

点击"忘记密码"选项后,这时,页面会跳向一个新页面,即“忘记密码”页面。首先给出"忘记密码"按钮对应的跳转代码片段:
<a href = "" style = "padding:0px 0px 0px 18px" onclick = "window.open('forgetPassword.jsp')">忘记密码?</a>  <!-- 打开新窗口 -->

在点击后,跳到新页面:


这里的界面设计是模仿的网页163邮箱的忘记密码,给出链接:http://reg.163.com/getpasswd/RetakePassword.jsp?from=mail163
该界面中,有几个关键点:一个是验证码的生成,一个是邮箱发送的功能,还一个就是点击确定后的反馈界面。主要包含这3个方面的内容,这3点以下将会详细介绍。

1、验证码生成功能

关键是jsp和servlet的交互。上面的链接中,主要使用了作者贴出的serlvet代码片段,即随机生成四位0~9的数字,然后生成一些干扰线,最后将生成的图片发送到jsp页面。
下面给出部分后台servlet代码片段:
 public void doGet(HttpServletRequest request, HttpServletResponse response)
                      throws ServletException, IOException
    {
        System.out.println("生成验证码");
        // 清空缓冲区
        response.reset();
 
        // 注意这里的MIME类型
        response.setContentType("image/png");
 
        // 设置页面不缓存
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
 
        // 创建一个图像,验证码显示的图片大小
        BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
 
        // 获取图形上下文
        Graphics g = image.getGraphics();
         
        // 设置背景
        g.setColor(getRandColor(200,250));
        g.fillRect(0, 0, width, height);
 
        for (int i = 0; i < 4; i++)
        {
            drawCode(g, i);
        }
        //添加干扰线
        drawNoise(g, 12);
 
        // 绘制边框
        //g.setColor(Color.gray);
        //g.drawRect(0, 0, width - 1, height - 1);
 
        // 将验证码内容保存进session中,用于验证用户输入是否正确时使用
        HttpSession session = request.getSession(true);
        session.removeAttribute("rand");
        session.setAttribute("rand", codeNumbers);
        // 重设字符串
        codeNumbers = "";
        // 利用ImageIO类的write方法对图像进行编码
        ServletOutputStream sos = response.getOutputStream();
        ImageIO.write(image, "PNG", sos);
        sos.close();
    }
从上面的代码可以看出,基本步骤是:生成4位0~9的随机数,然后设置数字的大小,颜色等;接着设置干扰线,根数,颜色等,然后创建出一个BufferedImage对象,就是验证码的背景图片大小、颜色等;最后将生成好的验证码传送到前台。
注意:这里要将验证码(即图片)传送到前台的jsp界面,需要三个方面的设置:
设置传送到前台的内容类型,可以是text/html,也可以是image/png。由于这里传送的是验证码,所以设置为后一种。
 // 注意这里的MIME类型
        response.setContentType("image/png");
在servlet中,还要考虑用什么方法对图像进行编码。
 // 利用ImageIO类的write方法对图像进行编码
        ServletOutputStream sos = response.getOutputStream();
        ImageIO.write(image, "PNG", sos);
        sos.close();

在jsp页面部分:
<img id="code" src="codeMakerServlet" title="看不清点击刷新验证码" style="cursor : pointer;"  onclick="return refreshcode()"/>
对应的js函数:
function refreshcode(){
	        document.getElementById("code").src="codeMakerServlet?a="+Math.random()+100;
	        return true;
	    }

其实就是每次点击,都运行一次对应的servlet,servlet在运行后,都会发送一个验证码图片到前台,也就是所谓的"刷新"。

2、填写内容检查

这里用到了一个js库:jquery.validate.min.js。这个只能判断输入的内容是否符合规则。
由于要输入用户的登录邮箱,规则正确只是第一步,还要查看输入的邮箱在数据库中是否存在,如果不存在,是不能允许发送密码重置邮件的。所以对邮箱正确性的检查,需要调用一次$.getJSON();进行验证;
对于验证码的验证,由于在验证码生成的servlet中,已经设置了
// 将验证码内容保存进session中,用于验证用户输入是否正确时使用
        HttpSession session = request.getSession(true);
        session.removeAttribute("rand");
        session.setAttribute("rand", codeNumbers);
所以这时候还要进行一次后台通讯,查看是否验证码输入正确。但是,有个问题我还没来得急了解:这个验证码的作用是什么?这个也是我在后期需要进行追加的内容。

3、邮件发送功能

参考文章:http://www.iteye.com/topic/352753
需要用到的jar包:mail.jar。
在上面的参考文章中,作者详细的给出了3段主要的代码,这里也不多说了,说实话,我还真没怎么看其内部的实现原理,只是把别人写好的代码,直接调用接口使用。
注意:作者在最后给出了几个经验,其中一个就是不能用新申请的邮箱,否则无法发送成功,我试了下,确实是这样,但是这个是不是所有的邮箱都是这样,我也没有试过。
另外,需要特别提出的是,发送内容的方式分为两种,一个是按照文本发送,一个是按照html发送。
先给出示例代码:
public static void main(String[] args){  
        //这个类主要是设置邮件  
     MailSenderInfo mailInfo = new MailSenderInfo();   
     mailInfo.setMailServerHost("smtp.163.com");   
     mailInfo.setMailServerPort("25");   
     mailInfo.setValidate(true);   
     mailInfo.setUserName("tianzhihensu@163.com");   
     mailInfo.setPassword("********");//您的邮箱密码   
     mailInfo.setFromAddress("tianzhihensu@163.com");   
     mailInfo.setToAddress("sacevmproject@163.com");   
     mailInfo.setSubject("设置邮箱标题,sfsfdfdff");   
     mailInfo.setContent("<a href = \"http://write.blog.csdn.net/postlist\">http://write.blog.csdn.net/postlist</a>");   
        //这个类主要来发送邮件  
     SimpleMailSender sms = new SimpleMailSender();  
         sms.sendTextMail(mailInfo);//发送文体格式   
         SimpleMailSender.sendHtmlMail(mailInfo);//发送html格式  
   }  

在代码的最后,有两种发送方式,一般都会采用第二种方式,如果要给对方邮箱发送一些超链接等,就需要用html格式发送,这样,<a href = "****"></a>就能自动识别成超链接。下图是用文本方式发送的上面代码中的内容:

从上图中,我们可以看出,对应的<a>标签是当做文本处理的,下图是用html格式发送的内容:
两种效果还是不同的,由于发送验证邮件需要用到链接,所以项目中采用html格式。

4、邮件中的链接

对于这个链接,还有一个重要的部分,那就是链接要链向哪里?需要哪些参数?这个链接怎么保证时效性(每个密码找回链接都是有时效性的)?
咱们首先看一下成熟的例子。学信网的密码找回链接如下:

现在能够确定的一点是:链接是指向某个servlet的,但是对应的参数就是一串没有规律的数字。这个key中隐含着什么样的信息?我认为包括两个部分:
一部分是用户的用户名等可以辨识用户的内容,否则在用户点击链接后无法得知用户是谁;
还一部分是邮件发送时的时间,因为只有参数中含有时间信息,才能使得该链接具有时效性(具体时效性可以是多 少,指向 的后台servlet中需要设定的部分)。
知道了以上的内容,还剩下最后一个问题,这个key值的乱码是怎么得来的?首先想到的就是加密。对的!就是加密得来的。否则以明文传送,不太安全。
至于这个加密的部分,就放在下面去讲。这里先来大体说一下加密:输入就是一串数,出来的就是一堆乱码,而且,这里的加密必须要是可逆的,否则点击链接后,不能还原成明文还是不顶用啊大笑。既然知道了输入就是一个字符串,那么可以把时间和用户名串起来,这样就形成了一个字符串,然后在后台再解析出来。

5、点击确定后的信息回馈

首先来看一下【学信网】是怎么回馈的:

用户点击"确定"按钮,当邮件发送成功后,会显示"邮件发送成功",然后下面跟出一些提示性的语言blablabla....但是,若由于某些原因,导致邮件发送失败时,也要给出相应的提醒。
模仿上述反馈界面,我用了如下界面:

内部实现原理如下:
在用户点击"确认"按钮后,后台首先获得当前的系统时间(按照ms计算),然后从前台得到了用户输入的登录邮箱,在两者之间加入一个"@"符连接起来为一个字符串(目的:便于在转变为明文的时候将两者区分开来,因为邮箱的第一个字母是不允许使用"@"符的,所以加入该符号不会引起歧义)。然后将该字符串加密成一串密文,这就是key值。前面再配上某个servlet的固定连接,就是完成了link链接;
调用邮箱发送接口,实现邮箱转发。本系统中使用的邮箱接口如下:
/**
	 * 发送信息到用户的登录邮箱
	 * 
	 * @param userEmail
	 * @return
	 */
	public Boolean sendMail(String userEmail) {
		Boolean flag = false;
		try {
			// 获取当前系统时间
			Date now = new Date();
			String currentTime = "" + now.getTime();
			
			String urlString = "http://localhost:8080/EVM/forgetPasswordAction?method=resetPassword&key=";
			CodeEncryption cEncryption = new CodeEncryption();
			String encryptedCode = cEncryption.encryptCode(currentTime + "@" + userEmail);
			String link = urlString + encryptedCode;
			String adminMailAddress = "admin@126.com";
			String contents = this.setContents(userEmail, link, adminMailAddress);
			//邮箱配置
			String serverHost = "smtp.163.com";
			String serverPort = "25";
			Boolean isValidate = true;
			String userName = "blablalbablabla@163.com";
			String password = "blablabla";
			String toMailAddress = userEmail;
			String subtitle = "科研数据管理平台注册账号密码找回 ";
			this.setMail(serverHost, serverPort, isValidate, userName, password, toMailAddress, subtitle, contents);
			
			flag = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return flag;
	}

上面的函数会返回一个布尔值,如果邮箱发送成功,则返回true,否则返回false。该函数是在service层,得到的布尔值将会返回到调用该函数的servlet层。这里面有个setMail函数,里面有很多参数,下面也给出对应的setMail函数:
/**
	 * 邮箱发送前的配置
	 * @param serverHost
	 * @param serverPort
	 * @param isValidate
	 * @param userName
	 * @param password
	 * @param toMailAddress
	 * @param subtitle
	 * @param contents
	 */
	public void setMail(String serverHost, String serverPort,
			Boolean isValidate, String userName, String password,
			String toMailAddress, String subtitle, String contents) {
		// 这个类主要是设置邮件
		MailSenderInfo mailInfo = new MailSenderInfo();
		mailInfo.setMailServerHost(serverHost);
		mailInfo.setMailServerPort(serverPort);
		mailInfo.setValidate(isValidate);
		mailInfo.setUserName(userName);
		mailInfo.setPassword(password);// 您的邮箱密码
		mailInfo.setFromAddress(userName);
		mailInfo.setToAddress(toMailAddress);
		mailInfo.setSubject(subtitle);
		mailInfo.setContent(contents);
		// 这个类主要来发送邮件
		SimpleMailSender sms = new SimpleMailSender();
	//	sms.sendTextMail(mailInfo);// 发送文体格式
		SimpleMailSender.sendHtmlMail(mailInfo);// 发送html格式
	}

若邮件发送失败,servlet会接收到false的信息。先来看一下对应的servlet层是怎么做的:
/**
	 * 密码忘记,发送信息到邮箱
	 * @param request
	 * @param response
	 */
	public void passwordForgotten(HttpServletRequest request, HttpServletResponse response) {
		String userEmail = request.getParameter("userEmailInput");
		ForgetPasswordService fPasswordService = new ForgetPasswordService();
		Boolean flag = fPasswordService.sendMail(userEmail);
		
		try {
			if (flag) {
				RequestDispatcher rDispatcher = request.getRequestDispatcher("fpEmailSended.jsp");
				String str1 = userEmail.substring(0, 3);
				String str2 = userEmail.substring(userEmail.indexOf("@"));
				String str3 = str1 + "***" + str2;
				request.setAttribute("userEmail", str3);
				request.setAttribute("flag", "true");
				rDispatcher.forward(request, response);
			} else {
				RequestDispatcher rDispatcher = request.getRequestDispatcher("fpEmailSended.jsp");
				String str1 = userEmail.substring(0, 3);
				String str2 = userEmail.substring(userEmail.indexOf("@"));
				String str3 = str1 + "***" + str2;
				request.setAttribute("userEmail", str3);
				request.setAttribute("flag", false);
				rDispatcher.forward(request, response);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

这里,flag出现了2个分支:在正确的情况下,给出"邮箱发送成功"的反馈界面,并由 RequestDispatcher指向一个新的jsp页面,即点击确认的那个页面和信息回馈的页面不是同一个页面。
若flag为true,则走第一个分支,结果如下:
flag为false,则走第二个分支,结果如下:
下面我们来看一下发送成功的情况,这样在邮箱285***@qq.com中就出现了一封邮件:


6、点击邮箱链接触发的操作

点击链接后,会进入到指定的servlet。从链接中可以看到,进入到的是一个名称叫forgetPasswordAction的servlet中。后面跟着两个参数:method----指定servlet触发什么操作,key----包含有发送时间(ms计算)和用户名信息,在servlet中会转化为明文。其中,"发送时间"用于判断用户点击链接的时候,该链接是否已经过时,"用户名"用于servlet跳转到jsp页面的前端显示。
下面看一下指定的servlet中的相关内容:
/**
	 * 用户在点击邮箱里的链接后,进入该函数
	 * @param request
	 * @param response
	 */
	public void resetPassword(HttpServletRequest request, HttpServletResponse response) {
		String key = request.getParameter("key");
		ForgetPasswordService fService = new ForgetPasswordService();
		List<String> datasList = new ArrayList<String>();
		datasList = fService.getExplicitCode(key);
		String time = datasList.get(0);
		String userEmail = datasList.get(1);
		Boolean timeFlag = fService.judgeOfTime(time);	//判断用户点击链接的时间是否过期
		String flag = "false";
		if (timeFlag) {
			flag = "true";
		}
		List<String> resultList = new ArrayList<String>();
		resultList.add(flag);
		resultList.add(userEmail);
		
		try {
			RequestDispatcher rDispatcher = request.getRequestDispatcher("resetPassword.jsp");
			request.setAttribute("datasList", resultList);
			rDispatcher.forward(request, response);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

7、重置密码页面

首先判断时间是否过期,并将标记发送到jsp中。下面给出resetPassword.jsp中的部分内容:
首先获得后台传过来的参数:
<%
		List<String> datasList = (List<String>) request.getAttribute("datasList");
		String flag = datasList.get(0);	//链接是否过期
	//	flag = "false";
		String userEmail = datasList.get(1);	//用户登录邮箱
	%>
得到flag,将flag设置到隐藏的input中:
<input type = "hidden" id = "hiddenInput" name = "hiddenInput" value = "<%=flag %>" >
在本jsp页面中,有两个大的div,分别对应flag的不同标记。若flag的标记为true,则显示一个div的内容,隐藏另一个div,若flag为false,则反过来。
flag为true时显示的内容:
<form action="forgetPasswordAction" class="form-horizontal"
						role="form" method="POST" style="border: 1px dotted silver;"
						id="resetPasswordForm">
	<br>
	<div class="form-group">
		<div class = "col-lg-3 col-lg-offset-1 col-md-3 col-md-offset-1 col-sm-4 col-xs-4 text-right">
			<label>用户名</label>
		</div>
		<div class = "col-lg-8 col-md-8 col-sm-7 col-xs-8 text-left">
			<b style = "color:#FF0000;font-size:10px;">*</b>
			<label><%=userEmail %></label>
			<input type = "hidden" name = "userEmailInput" id = "userEmailInput"  value = "<%=userEmail %>">
		</div>
	</div>
						
	<div class="form-group">
		<div class = "col-lg-3 col-lg-offset-1 col-md-3 col-md-offset-1 col-sm-4 col-xs-4 text-right">
			<label>密     码</label>
		</div>
		<div class = "col-lg-8 col-md-8 col-sm-7 col-xs-8 text-left">
			<b style = "color:#FF0000;font-size:10px;">*</b>
			<input type = "password" name = "password1" id = "password1" value = "">
			<label>注:密码长度至少6位</label>
		</div>
	</div>
						
	<div class="form-group">
		<div class = "col-lg-3 col-lg-offset-1 col-md-3 col-md-offset-1 col-sm-4 col-xs-4 text-right">
			<label>确认密码</label>
		</div>
		<div class = "col-lg-8 col-md-8 col-sm-7 col-xs-8 text-left">
			<b style = "color:#FF0000;font-size:10px;">*</b>
			<input type = "password" name = "password2" id = "password2" value = "">
		</div>
	</div>
						
	<input type="hidden" name="method" value="resetPasswordComplete">
						
	<div class="form-group">
		<div class = "col-12 col-md-12 col-sm-12 col-xs-12 text-center">
			<button type = "submit" class = "btn btn-primary btn-sm" onclick = "return resetPasswordConfirm()">确定</button>
		</div>
	</div>
</form>


对应的页面如下:

若flag为false,即链接已过期,对应的html代码如下;
<div class = "col-lg-8 col-lg-offset-2 col-md-8 col-md-offset-2 col-sm-12 col-sm-12 text-left" id = "failureDivId">
	<i class="fa fa-warning fa-lg" style="color: #FF0000;"></i><b
		style="font-family: arial; color: #00A600; font-size: 18px;">重置密码链接已过期</b>
	<p></p>
	<p></p>
	<p style="font-family: arial; font-size: 14px;">
		没有收到重置密码邮件,您可以到邮件垃圾箱里找找。
		<br>或者点击:<a href = "/EVM/forgetPassword.jsp">【重新发送重置密码邮件】</a>。
	</p>
</div>

对应的页面如下:


jsp中的js控制代码:
	var flag = $("#hiddenInput").val();
	if(flag == "true") {
		$("#failureDivId").hide();
		$("#resetPasswordForm").show();
	} else {
		$("#resetPasswordForm").hide();
		$("#failureDivId").show();
	}


  • 重置界面

进入重置页面,用户页面已自动填充(根据邮箱链接中的key值可得到用户名),用户这时候可进行密码重置。这里需要说明的一点是:一般的找回密码流程,最后都会让用户重新设置密码,不会把原来用户忘记的密码发送给用户。
其实,在重置密码页面,主要需要说明的就是密码的加密过程。

1、加密算法

我这里用到的是3DES密码加密,相关链接可以直接搜索3DES百度百科,里面有示例代码。这里我贴出相关的代码片段:
byte[] encoded = encryptMode(keyBytes, szSrc.getBytes());
 System.out.println("加密后的字符串:" + new String(encoded));
 
 byte[] srcBytes = decryptMode(keyBytes, encoded);
 System.out.println("解密后的字符串:" + (new String(srcBytes)));

这里可以看出来,加密之后的密文是一个byte数组,难就难在这个数组上,虽然可以使用new String(),转化为字符串,但是这个字符串是乱码,我试过,无论使用什么编码格式,转化出来的字符串都是乱码,这是一个问题,另一个问题是,转化后的字符串是无法再回退到byte数组的,即这个过程是不可逆的。基于以上两点缺陷,不能将得到的密文byte数组直接转化为字符串。
为了解决这个问题,我想到了另外一种方式,很巧妙的化解了byte数组的显示问题,而且,此种方式,在一定程度上还算是对byte数组的再次加密,方法如下:
/**
	 * 将明文字符串加密成密文,然后以字符串的形式返回.
	 * 说明:在将字符串明文加密后,得到的是一个byte[]数组,这时候如果将byte[]数组转化为字符串,是乱码。解决方式如下:
	 * 步骤1:对于byte[]数组中的每个元素,因为范围都在-128~127之间,所以为了方便表达,统一加上128,都转化为0或者正数;
	 * 步骤2: 利用Integer.toBinaryString()函数,将每个元素转化为16进制,结果若为单个的,用g在右侧补成2位的;
	 * 步骤3:将所有的结果统一串成一个整个的字符串,作为结果返回。
	 * @param explicitPassword
	 * @return
	 */
	public String encryptCode(String explicitString) {
		Security.addProvider(new com.sun.crypto.provider.SunJCE());
		
		String implicitString = "";
		byte[] encoded = encryptMode(keyBytes, explicitString.getBytes());
		
		List<String> codeArrTemp = new ArrayList<String>();
		for (int i = 0; i < encoded.length; i++) {
			codeArrTemp.add(String.valueOf(Integer.toHexString((int) encoded[i] + 128)));
		}
		List<String> codeArr = new ArrayList<String>();
		for (int i = 0; i < codeArrTemp.size(); i++) {
			if (codeArrTemp.get(i).length() == 1) {
				String temp = codeArrTemp.get(i) + "g";
				codeArr.add(temp);
			} else {
				codeArr.add(codeArrTemp.get(i));
			}
		}
		
		for (int i = 0; i < codeArr.size(); i++) {
			implicitString += codeArr.get(i);
		}
		
		return implicitString;
	}
注释中,已经给出了我的转化步骤,下面再给出与上面加密配套的解密代码:
/**
	 * 对密文解密,返回明文
	 * @param implicitString
	 * @return
	 */
	public String deEncryptCode(String implicitString) {
		Security.addProvider(new com.sun.crypto.provider.SunJCE());
		
		String explicitString = null;
		byte[] encoded = null;
		//根据implicitString得到encoded
		List<String> codeArrTemp = new ArrayList<String>();
		for (int i = 0; i < implicitString.length(); i += 2) {
			codeArrTemp.add(implicitString.substring(i, i + 2));
		}
		List<String> codeArr = new ArrayList<String>();
		for (int i = 0; i < codeArrTemp.size(); i++) {
			if (codeArrTemp.get(i).contains("g")) {
				codeArr.add(codeArrTemp.get(i).substring(0, 1));
			} else {
				codeArr.add(codeArrTemp.get(i));
			}
		}
		encoded = new byte[codeArr.size()];
		for (int i = 0; i < codeArr.size(); i++) {
			encoded[i] = (byte) (Integer.parseInt(codeArr.get(i), 16) - 128);
		}
		
		byte[] srcBytes = decryptMode(keyBytes, encoded);
		explicitString = new String(srcBytes);
		return explicitString;
	}
以上,就完成了字符串的加/解密过程,上面的代码可以直接拿过来用。其中,以上两个函数中,用到了原来的加/解密接口,这里顺便也把代码贴出来:
3DES加密:
// DES,DESede,Blowfish
	// keybyte为加密密钥,长度为24字节
	// src为被加密的数据缓冲区(源)
	public static byte[] encryptMode(byte[] keybyte, byte[] src) {
		try {
			// 生成密钥
			SecretKey deskey = new SecretKeySpec(keybyte, Algorithm);
			// 加密
			Cipher c1 = Cipher.getInstance(Algorithm);
			c1.init(Cipher.ENCRYPT_MODE, deskey);
			return c1.doFinal(src);
		} catch (java.security.NoSuchAlgorithmException e1) {
			e1.printStackTrace();
		} catch (javax.crypto.NoSuchPaddingException e2) {
			e2.printStackTrace();
		} catch (java.lang.Exception e3) {
			e3.printStackTrace();
		}
		return null;
	}
3DES解密:
// keybyte为加密密钥,长度为24字节
	// src为加密后的缓冲区
	public static byte[] decryptMode(byte[] keybyte, byte[] src) {
		try {
			// 生成密钥
			SecretKey deskey = new SecretKeySpec(keybyte, Algorithm);
			// 解密
			Cipher c1 = Cipher.getInstance(Algorithm);
			c1.init(Cipher.DECRYPT_MODE, deskey);
			return c1.doFinal(src);
		} catch (java.security.NoSuchAlgorithmException e1) {
			e1.printStackTrace();
		} catch (javax.crypto.NoSuchPaddingException e2) {
			e2.printStackTrace();
		} catch (java.lang.Exception e3) {
			e3.printStackTrace();
		}
		return null;
	}
以上3段代码就是整个加密、解密的过程。在我们数据库中的密码加密,也是用到了以上的方法。

2、"重置密码"点击"确定"后的信息反馈页面

这一步类似于前面的发送邮箱的确认,也是分为两支:成功还是失败。这里再次贴出重置密码的页面:

在用户点击"确定"后,后台将密码加密,然后存入数据库,等操作完成后,后台的servlet跳转到一个新页面:resetPassSuccOrFail.jsp,即给出信息反馈,表示是重置成功还是失败。
这里的操作和前面都是类似的,也就不多说了。下面给出两张分支的页面:

当修改密码失败时:

至此,"忘记密码"功能已实现完毕。

分享到:
sdf
评论

相关推荐

    postfixadmin忘记密码后的修改密码方法详解

    彩虹表是一种预先计算好大量明文密码及其对应MD5哈希值的数据库,通过与待破解的哈希值比较,可以快速找到对应的明文密码。由于盐值的随机性,相同的密码即使在彩虹表中存在,也无法匹配,因为其散列值与生成时的...

    信息系统密码管理规定.docx

    ### 信息系统密码管理规定知识点详解 #### 一、引言 在当今数字化时代,信息安全已成为企业生存和发展的重要保障。...企业在实施过程中应注意细节操作,并结合实际情况灵活调整策略,以实现最佳的安全效果。

    《电子投票平台管理系统的设计与实现》.doc

    以下是系统的核心功能模块及其实现细节: 1. 功能选择模块:该模块为用户提供菜单式操作界面,根据用户输入的选项执行相应的功能。系统针对管理员和投票人设置不同的权限,分别在list1()和list2()函数中实现。 2. ...

    路由密码查看,登陆备份配置软件

    本文将详述“路由密码查看,登陆备份配置软件”的核心功能及其相关知识点,旨在帮助用户更好地理解和操作这类工具。 首先,标题中的“路由密码查看”指的是能够查看路由器登录密码的工具。通常,路由器的管理员密码...

    S7-1200 CPU的SD存储卡功能及其应用介绍

    文章详述了如何正确插入、使用及格式化存储卡,同时也解释了存储卡的不同工作模式——‘程序卡’与‘传送卡’的区别,以及如何利用存储卡重置忘记的密码和恢复出厂设置。 适合人群:熟悉自动化设备操作的技术人员和...

    linux 破解root密码

    Debian及其衍生版(如Ubuntu)中的root密码重置步骤与Red Hat/CentOS类似,但存在一些细节上的差异: 1. **启动到GRUB菜单:** - 重启计算机。 - 在GRUB启动菜单中选择内核并按`e`键编辑启动选项。 2. **进入...

    android用户登录界面设计

    在登录界面中,我们可能用它来实现“记住我”或“忘记密码”等功能,用户点击后可以触发相应的功能,如自动保存登录状态或跳转到重置密码页面。 在实现这些组件的交互时,我们需要使用到Android的布局文件(如...

    ASP.NET登陆模块设计

    - 忘记密码功能:提供找回密码的方法,通常通过电子邮件确认。 - 非法用户登录限制:防止恶意用户频繁尝试登录,比如设置登录失败次数上限。 2. **模块流程分析**: - **登录页面** (`login.aspx`):提供用户...

    网上商城注册登录模块需求分析(以京东商城为例)

    3. **忘记密码**:设置找回密码功能,通过绑定的手机号或邮箱发送重置链接或验证码。 4. **安全问题**:对于高价值账户,可能需要设置安全问题,增加账户安全性。 5. **登录失败处理**:提供清晰的错误提示,如密码...

    订单系统(Asp.net+SQL server 2008)

    ### 订单系统(Asp.net+SQL server 2008) #### 登录(管理员与普通用户登录...通过对上述各个模块的详细介绍,我们可以清晰地了解到每个部分的功能及其实现方式,这对于后续系统维护和功能扩展都具有重要的参考价值。

    Bosch拧紧枪-BS300CN使用说明

    - 用户密码复位功能可以在用户忘记密码时帮助其重新设置密码。 **8.7 地址访问授权** - 地址访问授权功能可以为不同用户设定不同的访问权限,确保敏感信息的安全。 #### 配置 **9.1 配置KE** - 配置KE是指为...

    网络安全相关的密码学、网络攻防、安全分析等项目举例与概要.docx

    - **《Applied Cryptography》**:Bruce Schneier 的这本经典之作深入探讨了密码学的实际应用,对于理解密码学原理及其在现实世界中的应用至关重要。 **在线社区** - **Reddit 的 r/netsec 和 r/cybersecurity 子...

    新型Smart管理系统的设计方法与原理,需要的拿走

    - **重置密码**:若用户忘记密码,可以通过邮箱或手机号找回密码。 ##### 2. 资源管理 - **添加资源**:管理员可以添加新的资源,如文档、图片等。 - **修改资源**:可编辑已存在的资源信息。 - **删除资源**:支持...

    需求规格说明书v1.0.11

    登录过程应支持记住密码和忘记密码的功能,同时提供安全的账户保护措施,如二次验证。 3. 非功能性需求 非功能性需求通常涉及系统的性能、安全性、可用性、可维护性等方面。例如,系统应能处理大量并发用户,保证...

    华为Quidway Eudemon 1000E 统一安全网关 配置指南 基础配置分册

    - **功能**:基于静态密码进行认证。 - **步骤**:使用“authentication-mode password”命令。 - **5.4.2 配置AAA验证** - **功能**:使用认证服务器进行认证。 - **步骤**:使用“authentication-mode aaa”...

    富士通 笔记本FMV 8200驱动

    2. **密码功能**: 详细讲解了BIOS中的密码功能,包括不同类型的密码(如管理员密码和用户密码)、如何设置、忘记密码后的处理方式以及密码的修改或删除等。 3. **BIOS消息列表**: 提供了一系列BIOS可能显示的信息...

    BBS论坛系统详细设计说明书.rar

    根据提供的文件信息,我们...通过以上内容,我们可以清楚地了解到BBS论坛系统的主要功能和技术实现细节。这些知识对于系统开发人员、产品经理以及其他相关人员来说都非常重要,有助于确保项目的顺利进行和最终的成功。

    MySQL基础与实例教程非笔试申请表.doc

    - **存储过程**:使用存储过程(`CREATE PROCEDURE`)来封装一系列复杂的SQL操作,可以有效地实现诸如选课、调课等功能。 - **游标和临时表**:利用游标(`CURSOR`)和临时表(`TEMPORARY TABLE`)来处理复杂的数据操作...

Global site tag (gtag.js) - Google Analytics