验证码、MD5加密
验证码
生成验证码帮助类
点击查看代码VerifyCodeServlet.java
package com.situ.web.servlet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 验证码生成程序
*
*/
@WebServlet("/verifyCode")
public class VerifyCodeServlet extends HttpServlet {
private static final long serialVersionUID = 2376992603034716655L;
private final Font mFont = new Font("Arial Black", Font.PLAIN, 15); // 设置字体
private final int lineWidth = 2; // 干扰线的长度=1.414*lineWidth
private final int width = 88; // 定义图形大小
private final int height = 25; // 定义图形大小
private final int count = 200;
/**
* 描述:
*
* @param fc
* 描述:
* @param bc
* 描述:
*
* @return 描述:
*/
private Color getRandColor(int fc, int bc) { // 取得给定范围随机颜色
final Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
final int r = fc + random.nextInt(bc - fc);
final int g = fc + random.nextInt(bc - fc);
final int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
// 处理post
@Override
public void doPost(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
/**
* 描述:
*
* @param request
* 描述:
* @param response
* 描述:
*
* @throws ServletException
* 描述:
* @throws IOException
* 描述:
*/
@Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
response.reset();
// 设置页面不缓存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/gif");
// 在内存中创建图象
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
final Graphics2D g = (Graphics2D) image.getGraphics();
// 生成随机类
final Random random = new Random();
// 设定背景色
g.setColor(getRandColor(200, 250)); // ---1
g.fillRect(0, 0, width, height);
// 设定字体
g.setFont(mFont);
// 画边框
g.setColor(getRandColor(0, 20)); // ---2
// 距离
g.drawRect(0, 0, width - 1, height - 1);
// 随机产生干扰线,使图象中的认证码不易被其它程序探测到
for (int i = 0; i < count; i++) {
g.setColor(getRandColor(150, 200)); // ---3
final int x = random.nextInt(width - lineWidth - 1) + 1; // 保证画在边框之内
final int y = random.nextInt(height - lineWidth - 1) + 1;
final int xl = random.nextInt(lineWidth);
final int yl = random.nextInt(lineWidth);
g.drawLine(x, y, x + xl, y + yl);
}
// 取随机产生的认证码(4位数字)
String sRand = "";
for (int i = 0; i < 4; i++) {
final String rand = String.valueOf(random.nextInt(10));
sRand += rand;
// 将认证码显示到图象中,调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.setColor(new Color(20 + random.nextInt(130), 20 + random.nextInt(130), 20 + random.nextInt(130))); // --4--50-100
// 第一个参数是要画上去的字符串 后面两个参数是针对 (0,0) x轴和y轴
g.drawString(rand, (13 * i) + 10, 20);
}
// 把后台生成的验证码放到Session
HttpSession session = request.getSession();
session.setAttribute("codeInSession", sRand);
// 图象生效
// 它的作用是销毁程序中指定的图形界面资源,如果在使用了graphics获得windows一些图形资源,而不进行关闭的话,由于后期多人使用就会造成内存溢出的情况的,导致程序卡死。
g.dispose();
OutputStream os = response.getOutputStream();
// 输出图象到页面
ImageIO.write(image, "PNG", os);
os.flush();
os.close();
}
}
前端
前端展示
点击查看代码
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ include file="header.jsp"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录</title>
<script type="text/javascript">
let alert_msg = '${alert_msg}';
if (alert_msg != null && alert_msg.trim() != '') {
window.alert(alert_msg);
}
</script>
<style>
* {
padding: 0;
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
letter-spacing: .05em;
}
html {
height: 100%;
}
body {
height: 100%;
background: url("${path}/static/img/1.jpg") no-repeat;
background-size: 100% 100%;
}
.container {
height: 100%;
/*background-image: linear-gradient(to right, #fbc2eb, #a6c1ee); 设置渐变色 */
padding: 0;
margin: 0;
}
.login-wrapper {
background-color: #fff;
width: 300px;
height: 500px;
position: relative;
padding: 0 50px;
border-radius: 15px;
/* 设置框的居中位置也可以采用flex布局方式 */
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.login-wrapper .header {
font-size: 30px;
font-weight: bold;
text-align: center;
line-height: 120px;
}
.login-wrapper .form-wrapper .input-item {
display: block;
width: 100%;
margin-bottom: 20px;
border: 0;
padding: 10px;
border-bottom: 1px solid rgb(128, 125, 125);
font-size: 15px;
outline: none;
}
.login-wrapper .form-wrapper .input-item ::placeholder {
text-transform: uppercase;
}
.login-wrapper .form-wrapper .btn {
text-align: center;
padding: 10px;
margin-top: 40px;
width: 100%;
background-image: linear-gradient(to right, #a6c1ee, #fbc2eb);
color: #fff;
}
.login-wrapper .msg {
text-align: center;
line-height: 80px;
}
.login-wrapper .msg a {
/* 消除下划线 */
text-decoration: none;
text-decoration-color: unset;
color: #a6c1ee;
}
</style>
</head>
前端逻辑
<body>
<div class="container">
<div class="login-wrapper">
<div class="header">Login</div>
<div class="form-wrapper">
<form id="formId" method="post" onsubmit="return check()">
<input type="text" name="name" id="name" placeholder="登录账号" class="input-item">
<input type="password" name="password" id="password" placeholder="登录密码" class="input-item">
<input type="text" name="code" id="validationCode" placeholder="请输入验证码" class="input-item">
<img id="img_validation_code" src="${path}/auth" onclick="refresh()" />
<input class="btn" type="button" onclick="submitForm()" value="登录"/>
</form>
</div>
<div class="msg">
Don't have account?<a href='register.jsp' style="font-weight: bold;">Sign up</a>
</div>
</div>
</div>
</body>
<script type="text/javascript">
function submitForm(){
$.post(
'${path}/login?method=login',
$('#formId').serialize(),//{'name':'zhansagn','age':23,'gender':'男'}
function (jsonResult){
console.log(jsonResult)
if (jsonResult.ok){
mylayer.okUrl('登录成功','${path}/index.jsp')
}else{
mylayer.errorMsg(jsonResult.msg)
}
}
)
}
</script>
</html>
后端业务逻辑
private void login(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("UserServlet.login");
String name = req.getParameter("name");
String password = req.getParameter("password");
String code = req.getParameter("code");
//先判断验证码是否正确
HttpSession session = req.getSession();
String codeInSession = (String) session.getAttribute("codeInSession");
//验证码错误,返回错误信息
if(StringUtils.isEmpty(code)||!codeInSession.equalsIgnoreCase(code)){
JSONUtil.obj2Json(JSONResult.error("验证码为空或错误"),resp);
return;
}
//验证码正确,验证用户名和 密码是否正确
User user=userService.login(name,password);
if (user!=null) {
JSONUtil.obj2Json(JSONResult.ok("登录成功"), resp);
} else {
JSONUtil.obj2Json(JSONResult.error("用户名或密码错误"), resp);
}
}
MD5加密
MD5特性:
MD5不管你加密多少的东西,哪怕是10M文件都能加密为32位的字符串。
并且神奇的是 :数学上能保证,哪怕你只改变了1个文字,加密后的字符串都会有很大变化。
MD5常见用途:
- 加密密码
- 对比文件。(保证客户端下载的文件和服务器文件是一个,防止下载假的文件。)
- 网盘的文件秒传
为什么要使用MD5加密密码:
- 脱库:是吧一个网站的数据库拖回来,就是把数据导出保存。
- 撞库:是用拖回来的用户数据取尝试登陆别的网站,撞上了就撞上了,撞不上就拉倒。
加密解密的原则:一个加密算法就是让全世界都知道了也无法破解,这才是牛的算法。
MD5加密是不能够被破解的。
所以找回密码功能:
- 如果这个网站使用的是MD5加密的,
那么这个密码是根本找不回来,找回密码是做成重置密码。 - 如果某个网站真能找回来,
说明这个网站肯定是明文保存的。
MD5是不能够被破解的,如果说MD5能够被“破解”:有另外的一个明文字符串加密之后也是这个值,但是这个概率小2的128次方。
王小云并不是完成了MD5的破解,而是加速了MD5值碰撞速度。
MD5是无法根据MD5值反推原始字符串的,但是有一定的概率有另一个不同的字符串,也能生成同样的MD5值,用户不需要输入原来密码也能登录系统。
王小云做的其实就是大大减少了找到这样一个加密后MD5值一样的字符串的时间。
MD5加密帮助类
点击查看代码MD5Util.java
package study.utils;
import java.security.MessageDigest;
/**
* MD5工具类
*/
public class MD5Util {
//加盐
public static final String MD5_SALT = "sdut";
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* 返回大写MD5
*
* @param origin
* @param charsetname
* @return
*/
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname)) {
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} else {
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
}
} catch (Exception exception) {
}
return resultString.toUpperCase();
}
public static String MD5Encode(String origin) {
return MD5Encode(origin, "utf-8");
}
private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
}
MD5使用
添加用户时加密
@Override
public Boolean add(User user) {
//添加用户时以密文形式添加到数据库
user.setPassword(MD5Util.MD5Encode(user.getPassword()+MD5Util.MD5_SALT));
int count=userDao.add(user);
return count==1;
}
修改用户时加密
····
登录时加密
@Override
public User login(String name, String password) {
//登录时加密密码
return userDao.selectByNameAndPassword(name,MD5Util.MD5Encode(password+MD5Util.MD5_SALT));
}