验证码、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));
    }
posted @ 2022-12-20 12:33  kingwzun  阅读(146)  评论(0编辑  收藏  举报