在信息学中,密码也叫口令,要实现登录验证,每次网络传输的口令都不一致,故称为一次性口令机制。
这里实现简单的一次性口令机制:(SpringMVC实现)
思路:用验证码代替时间戳,将密码(口令)通过md5算法加密,再将验证码加在后面,然后再用md5算法加密,在网络传输过程中以密文的形式传输到后台管理。后台数据库保存的是用md5算法加密的密码,将该密文加上保存在session范围内的验证码用md5算法加密,得到的密文与请求中的口令对比,如配对,则验证成功,否则,验证失败。
MD5加密:
EncryptionByMD5.java
package com.dw.controller;
public class EncryptionByMD5 {
public static String getMD5(byte[] source) {
String s = null;
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f' };// 用来将字节转换成16进制表示的字符
try {
java.security.MessageDigest md = java.security.MessageDigest
.getInstance("MD5");
md.update(source);
byte tmp[] = md.digest();// MD5 的计算结果是一个 128 位的长整数,
// 用字节表示就是 16 个字节
char str[] = new char[16 * 2];// 每个字节用 16 进制表示的话,使用两个字符, 所以表示成 16
// 进制需要 32 个字符
int k = 0;// 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) {// 从第一个字节开始,对 MD5 的每一个字节// 转换成 16
// 进制字符的转换
byte byte0 = tmp[i];// 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf];// 取字节中高 4 位的数字转换,// >>>
// 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf];// 取字节中低 4 位的数字转换
}
s = new String(str);// 换后的结果转换为字符串
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return s;
}
public static void main(String[] args){
String test=EncryptionByMD5.getMD5("abc123".getBytes());
//test = e99a18c428cb38d5f260853678922e03
System.out.println(test);
}
}
验证码实现:RandomValidateCode.java:
package com.dw.controller;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 验证码生成类
*/
public class RandomValidateCode {
public static final String RANDOMCODEKEY= "RANDOMVALIDATECODEKEY";//放到session中的key
//private String randString = "0123456789";//随机产生只有数字的字符串 private String
//private String randString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生只有字母的字符串
private String randString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生数字与字母组合的字符串
private int width = 95;// 图片宽
private int height = 25;// 图片高
private int lineSize = 40;// 干扰线数量
private int stringNum = 4;// 随机产生字符数量
private Random random = new Random();
/*
* 获得字体
*/
private Font getFont() {
return new Font("Fixedsys", Font.CENTER_BASELINE, 18);
}
/*
* 获得颜色
*/
private Color getRandColor(int fc, int bc) {
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
int r = fc + random.nextInt(bc - fc - 16);
int g = fc + random.nextInt(bc - fc - 14);
int b = fc + random.nextInt(bc - fc - 18);
return new Color(r, g, b);
}
/**
* 生成随机图片
*/
public void getRandcode(HttpServletRequest request,
HttpServletResponse response) {
HttpSession session = request.getSession();
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();// 产生Image对象的Graphics对象,该对象可以在图像上进行各种绘制操作
g.fillRect(0, 0, width, height);
g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));
g.setColor(getRandColor(110, 133));
// 绘制干扰线
for (int i = 0; i <= lineSize; i++) {
drowLine(g);
}
// 绘制随机字符
String randomString = "";
for (int i = 1; i <= stringNum; i++) {
randomString = drowString(g, randomString, i);
}
//将生成的随机字符串保存到session中,而jsp界面通过session.getAttribute("RANDOMCODEKEY"),
//获得生成的验证码,然后跟用户输入的进行比较
session.removeAttribute(RANDOMCODEKEY);
session.setAttribute(RANDOMCODEKEY, randomString);
g.dispose();
try {
// 将内存中的图片通过流动形式输出到客户端
ImageIO.write(image, "JPEG", response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 绘制字符串
*/
private String drowString(Graphics g, String randomString, int i) {
g.setFont(getFont());
g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
.nextInt(121)));
String rand = String.valueOf(getRandomString(random.nextInt(randString
.length())));
randomString += rand;
g.translate(random.nextInt(3), random.nextInt(3));
g.drawString(rand, 13 * i, 16);
return randomString;
}
/*
* 绘制干扰线
*/
private void drowLine(Graphics g) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(13);
int yl = random.nextInt(15);
g.drawLine(x, y, x + xl, y + yl);
}
/*
* 获取随机的字符
*/
public String getRandomString(int num) {
return String.valueOf(randString.charAt(num));
}
}
Controller.java:
package com.dw.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/validateCode")
public class YanZhengMaController {
@RequestMapping("/getVerify")
public void getVerify(HttpServletRequest request, HttpServletResponse response){
response.setContentType("image/jpeg");//设置相应类型,告诉浏览器输出的内容为图片
response.setHeader("Pragma", "No-cache");//设置响应头信息,告诉浏览器不要缓存此内容
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expire", 0);
RandomValidateCode randomValidateCode = new RandomValidateCode();
try {
randomValidateCode.getRandcode(request, response);//输出验证码图片方法
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 登录页面校验验证码
*/
@RequestMapping("/checkVerify")
@ResponseBody
public String checkVerify(String inputStr, HttpSession session){
//从session中获取随机数
String random = (String) session.getAttribute("RANDOMVALIDATECODEKEY");
if(random.equals(inputStr)){
return "T";//验证码正确
}else{
return "F";//验证码错误
}
}
/*
* 口令、口令+验证码加密
*/
@RequestMapping("/jiami")
@ResponseBody
public String jiaMi(String inputStr, HttpSession session){
EncryptionByMD5 md5 = new EncryptionByMD5();
String jiami = md5.getMD5(inputStr.getBytes());
return jiami;
}
/*
* 验证成功输出界面
*/
@SuppressWarnings("static-access")
@RequestMapping("/checkOk")
public String checkOk(HttpServletRequest request,HttpSession session) {
String password = request.getParameter("password");
String username = request.getParameter("username");
String user = "admin";
//pwd = abc123 + 验证码
String pwd = "e99a18c428cb38d5f260853678922e03" + (String) session.getAttribute("RANDOMVALIDATECODEKEY");
EncryptionByMD5 encryptionByMD5 = new EncryptionByMD5();
pwd = encryptionByMD5.getMD5(pwd.getBytes());
// System.out.println("pwd:"+pwd);
// System.out.println("password:"+password);
if(!user.equals(username) || !pwd.equals(password)){
return "redirect:error.jsp";
}
return "redirect:ok.jsp";
}
}
jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>登陆页面</title>
<meta name="viewport"
content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script language="javascript" src="js/jquery-3.1.1.min.js"></script>
<script type="text/javascript">
$(document.body).ready(function () {
//首次获取验证码
$("#imgVerify").attr("src","/SpringMVC01/validateCode/getVerify.do?"+Math.random());
});
//获取验证码
function getVerify(obj,str){
obj.src = str+"/validateCode/getVerify.do?"+Math.random();
}
//验证码出错- 刷新验证码
function getVerify2(str){
$("#imgVerify").attr("src",str+"/validateCode/getVerify.do?"+Math.random());
}
//校验验证码
function checkSum(str){
var inputStr = $(".check_input").val();
if(inputStr!=null && inputStr!=""){
inputStr = inputStr.toUpperCase();//将输入的字母全部转换成大写
$.ajax({
url : str+"/validateCode/checkVerify.do",
data: {inputStr:inputStr},
success : function(datas) {
if(datas == "T"){
//$("#form1").submit();//提交表单
jiami(str);
}else{
getVerify2(str); //出错时更换验证码
$(".check_input").val("");
$(".warn_text").text("验证码输入错误!");
$(".login_form_warn").css("display","block");
}
}
});
}else{
getVerify2(str); //出错时更换验证码
$(".warn_text").text("请输入验证码");
$(".login_form_warn").css("display","block");
}
}
function jiami(str){
var pwd = document.getElementById("password").value;
var inputStr = $(".check_input").val().toUpperCase();
$.ajax({
url:str+"/validateCode/jiami.do",
data: {inputStr:pwd},
success:function(datas){
//$("#form1").submit();//提交表单
var klAndYzm = datas + inputStr;
$.ajax({
url:str+"/validateCode/jiami.do",
data: {inputStr:klAndYzm},
success:function(datas){
document.getElementById("password").value = datas;
$("#form1").submit();//提交表单
}
});
}
});
}
</script>
</head>
<body>
<div class="login_form">
<form action="${pageContext.request.contextPath }/validateCode/checkOk.do" method='post' id="form1">
<input type="text" name="username" id="username" placeholder="请输入用户名">
<input type="password" name="password" id="password" placeholder="请输入密码">
</form>
<input class="check_input" type="text" placeholder="请输入验证码" >
<img id="imgVerify" src="" alt="点击更换验证码" width="112" height="36" οnclick="getVerify(this,'${pageContext.request.contextPath}');"><!--首次获取验证码图片,也可在此将src设为访问路径/getVerify-->
<div class="login_form_warn_lable"></div>
<div class="login_form_warn">
<div style="font-size: 10px;color: red;"> <span class="warn_text"></span> </div>
</div>
<div class="login_form_submit">
<input class="btn_submit" οnclick="checkSum('${pageContext.request.contextPath}');" type="submit" value="登录"/>
</div>
</div>
</body>
</html>