• ecologicalhome项目的登录注册
  • [EMPTY]
  • 总结:
  • 第一次写博客,多多包涵
  • 祝大家学有所成,快快上岸
  • 关注我不迷路

ecologicalhome项目的登录注册

基于SpringBoot框架实现的web登录注册项目,
使用框架或技术:
后端 SpringBoot、mybatis、mysql、shiro 等
前端 jquery Thymeleaf 基于jQ的Ajax等

  1. 效果图演示
  1. 登录:
  1. 主页登录注册入口
  2. 点击登录来到登录页面
  3. 登录页面实现ajax查询用户名是否存在,js用户名,密码格式限制,点击验证码实现刷新对登录按钮进行限制,只有用户名,密码符合要求才可以点击登录,shiro实现登录控制,记住我,点击免费注册跳转注册页
    用户名控制
    密码控制
    ajax验证用户名 失去光标执行验证
    点击刷新验证码 验证码局部刷新
  1. 注册:
  1. 主页点击注册,或者登录页点击免费注册
  2. 注册页Ajax实现判断注册邮箱是否被使用,js对密码的控制,验证确认密码是否一致,输入都符合要求时解除对注册按钮的控制,ajax发送邮箱验证码
    邮箱控制
    密码控制
    再次输入密码控制
    点击发送邮箱验证码
  1. 登录部分核心代码
  1. 目录结构:
  2. index.html
<p class="fl" th:if="${loginUser==null}">
    <a th:href="@{/toLogin}" id="login">登录</a>
    <a th:href="@{/toRegister}" id="reg">注册</a>
</p>
<p class="fl" th:if="${loginUser!=null}">
    <a th:href="@{/logout}" id="logout">注销</a>
</p>
  1. loginController
/**
    * 去登录页面
    * @return 登陆页面
    */
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "member/login";
    }
  1. 准备图片验证码工具类
    工具类网上有众多优秀的资源

ValidateCode

package com.cheng.utils;

import java.util.Random;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;

/**
* 验证码生成器类,可生成数字、大写、小写字母及三者混合类型的验证码。 支持自定义验证码字符数量; 支持自定义验证码图片的大小; 支持自定义需排除的特殊字符;
* 支持自定义干扰线的数量; 支持自定义验证码图文颜色
*/
public class ValidateCode {

    /**
    * 验证码类型为仅数字 0~9
    */
    public static final int TYPE_NUM_ONLY = 0;

    /**
    * 验证码类型为仅字母,即大写、小写字母混合
    */
    public static final int TYPE_LETTER_ONLY = 1;

    /**
    * 验证码类型为数字、大写字母、小写字母混合
    */
    public static final int TYPE_ALL_MIXED = 2;

    /**
    * 验证码类型为数字、大写字母混合
    */
    public static final int TYPE_NUM_UPPER = 3;

    /**
    * 验证码类型为数字、小写字母混合
    */
    public static final int TYPE_NUM_LOWER = 4;

    /**
    * 验证码类型为仅大写字母
    */
    public static final int TYPE_UPPER_ONLY = 5;

    /**
    * 验证码类型为仅小写字母
    */
    public static final int TYPE_LOWER_ONLY = 6;

    private ValidateCode() {

    }

    /**
    * 生成验证码字符串
    *
    * @param type
    *            验证码类型,参见本类的静态属性
    * @param length
    *            验证码长度,大于0的整数
    * @param exChars
    *            需排除的特殊字符(仅对数字、字母混合型验证码有效,无需排除则为null)
    * @return 验证码字符串
    */
    public static String generateTextCode(int type, int length, String exChars) {

        if (length <= 0)
            return "";

        StringBuffer code = new StringBuffer();
        int i = 0;
        Random r = new Random();

        switch (type) {

            // 仅数字
            case TYPE_NUM_ONLY:
                while (i < length) {
                    int t = r.nextInt(10);
                    if (exChars == null || exChars.indexOf(t + "") < 0) {// 排除特殊字符
                        code.append(t);
                        i++;
                    }
                }
                break;

            // 仅字母(即大写字母、小写字母混合)
            case TYPE_LETTER_ONLY:
                while (i < length) {
                    int t = r.nextInt(123);
                    if ((t >= 97 || (t >= 65 && t <= 90)) && (exChars == null || exChars.indexOf((char) t) < 0)) {
                        code.append((char) t);
                        i++;
                    }
                }
                break;

            // 数字、大写字母、小写字母混合
            case TYPE_ALL_MIXED:
                while (i < length) {
                    int t = r.nextInt(123);
                    if ((t >= 97 || (t >= 65 && t <= 90) || (t >= 48 && t <= 57))
                            && (exChars == null || exChars.indexOf((char) t) < 0)) {
                        code.append((char) t);
                        i++;
                    }
                }
                break;

            // 数字、大写字母混合
            case TYPE_NUM_UPPER:
                while (i < length) {
                    int t = r.nextInt(91);
                    if ((t >= 65 || (t >= 48 && t <= 57)) && (exChars == null || exChars.indexOf((char) t) < 0)) {
                        code.append((char) t);
                        i++;
                    }
                }
                break;

            // 数字、小写字母混合
            case TYPE_NUM_LOWER:
                while (i < length) {
                    int t = r.nextInt(123);
                    if ((t >= 97 || (t >= 48 && t <= 57)) && (exChars == null || exChars.indexOf((char) t) < 0)) {
                        code.append((char) t);
                        i++;
                    }
                }
                break;

            // 仅大写字母
            case TYPE_UPPER_ONLY:
                while (i < length) {
                    int t = r.nextInt(91);
                    if ((t >= 65) && (exChars == null || exChars.indexOf((char) t) < 0)) {
                        code.append((char) t);
                        i++;
                    }
                }
                break;

            // 仅小写字母
            case TYPE_LOWER_ONLY:
                while (i < length) {
                    int t = r.nextInt(123);
                    if ((t >= 97) && (exChars == null || exChars.indexOf((char) t) < 0)) {
                        code.append((char) t);
                        i++;
                    }
                }
                break;

        }

        return code.toString();
    }

    /**
    * 已有验证码,生成验证码图片
    *
    * @param textCode
    *            文本验证码
    * @param width
    *            图片宽度
    * @param height
    *            图片高度
    * @param interLine
    *            图片中干扰线的条数
    * @param randomLocation
    *            每个字符的高低位置是否随机
    * @param backColor
    *            图片颜色,若为null,则采用随机颜色
    * @param foreColor
    *            字体颜色,若为null,则采用随机颜色
    * @param lineColor
    *            干扰线颜色,若为null,则采用随机颜色
    * @return 图片缓存对象
    */
    public static BufferedImage generateImageCode(String textCode, int width, int height, int interLine,
                                                boolean randomLocation, Color backColor, Color foreColor, Color lineColor) {

        BufferedImage bim = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = bim.getGraphics();

        // 画背景图
        g.setColor(backColor == null ? getRandomColor() : backColor);
        g.fillRect(0, 0, width, height);

        // 画干扰线
        Random r = new Random();
        if (interLine > 0) {

            int x = 0, y = 0, x1 = width, y1 = 0;
            for (int i = 0; i < interLine; i++) {
                g.setColor(lineColor == null ? getRandomColor() : lineColor);
                y = r.nextInt(height);
                y1 = r.nextInt(height);

                g.drawLine(x, y, x1, y1);
            }
        }

        // 写验证码

        // g.setColor(getRandomColor());
        // g.setColor(isSimpleColor?Color.BLACK:Color.WHITE);

        // 字体大小为图片高度的80%
        int fsize = (int) (height * 0.8);
        int fx = height - fsize;
        int fy = fsize;

        g.setFont(new Font("Default", Font.PLAIN, fsize));

        // 写验证码字符
        for (int i = 0; i < textCode.length(); i++) {
            fy = randomLocation ? (int) ((Math.random() * 0.3 + 0.6) * height) : fy;// 每个字符高低是否随机
            g.setColor(foreColor == null ? getRandomColor() : foreColor);
            g.drawString(textCode.charAt(i) + "", fx, fy);
            fx += fsize * 0.9;
        }

        g.dispose();

        return bim;
    }

    /**
    * 生成图片验证码
    *
    * @param type
    *            验证码类型,参见本类的静态属性
    * @param length
    *            验证码字符长度,大于0的整数
    * @param exChars
    *            需排除的特殊字符
    * @param width
    *            图片宽度
    * @param height
    *            图片高度
    * @param interLine
    *            图片中干扰线的条数
    * @param randomLocation
    *            每个字符的高低位置是否随机
    * @param backColor
    *            图片颜色,若为null,则采用随机颜色
    * @param foreColor
    *            字体颜色,若为null,则采用随机颜色
    * @param lineColor
    *            干扰线颜色,若为null,则采用随机颜色
    * @return 图片缓存对象
    */
    public static BufferedImage generateImageCode(int type, int length, String exChars, int width, int height,
                                                int interLine, boolean randomLocation, Color backColor, Color foreColor, Color lineColor) {

        String textCode = generateTextCode(type, length, exChars);
        BufferedImage bim = generateImageCode(textCode, width, height, interLine, randomLocation, backColor, foreColor,
                lineColor);

        return bim;
    }

    /**
    * 产生随机颜色
    *
    * @return
    */
    private static Color getRandomColor() {
        Random r = new Random();
        Color c = new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255));
        return c;
    }

}
  1. LoginController
/**
    * 生成验证码图片
    *          * 验证码类型为仅数字 0~9 0
    *          * 验证码类型为仅字母,即大写、小写字母混合1
    *          * 验证码类型为数字、大写字母、小写字母混合2
    *          * 验证码类型为数字、大写字母混合3
    *          * 验证码类型为数字、小写字母混合4
    *          * 验证码类型为仅大写字母5
    *          * 验证码类型为仅小写字母6
    * @param response 传递生成数据
    * @throws IOException 抛出io异常
    */
    @RequestMapping("/validateCode")
    public void validateCode(HttpServletResponse response) throws IOException {
        String code = ValidateCode.generateTextCode(4, 4, null);
        System.out.println("生成的验证码"+code);
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.removeAttribute("validateCode");
        session.setAttribute("validateCode",code);
        BufferedImage bufferedImage = ValidateCode.generateImageCode(code, 120, 40, 7, true, null, null, null);
        ImageIO.write(bufferedImage, "JPEG", response.getOutputStream());
    }
  1. login.html

提前准备jquery,和jquery.cookie两个js文件
jquery-3.6.0.js
jquery.cookie.js

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
	    <meta charset="UTF-8">
	    <title>登录</title>
	    <link rel="stylesheet" type="text/css" th:href="@{member/css/public.css}"/>
	    <link rel="stylesheet" type="text/css" th:href="@{member/css/login.css}"/>
	    <script type="text/javascript" th:src="@{member/js/jquery-3.6.0.js}"></script>
	    <script type="text/javascript" th:src="@{js/jquery.cookie.js}"></script>
	    <script type="text/javascript" th:src="@{member/js/login.js}"></script>
    </head>
    <body>
	    <!-------------------login-------------------------->
	    <div class="login">
		    <form th:action="@{/login}" method="post" onsubmit="return checkForm()">
			    <h1><a th:href="@{/index}"><img th:src="@{member/img/temp/logo.png}" alt=""></a></h1>
			    <div class="msg-warn hide"><b>公共场所切勿自动登录,以防账号丢失</b></div>
			    <label>
				    <input type="text" name="userName" id="userName"  placeholder="昵称/邮箱/手机号:" required="" onkeyup="checkUserName2()"/>
<!--					autofocus="" 自动聚焦光标-->
			    </label>
			    <label>
				    <input type="password" name="password" id="password"  placeholder="密码:" required="" onkeyup="checkPassword()"/>
			    </label>
			    <div>
				    <label>
					    <input style="width: 100px;" type="text" name="verificationCode"  placeholder="验证码:" required="" />
				    </label>
				    <a href="javascript:changeVerificationCode()" rel="external nofollow" >
					    <div style="margin-right: 87px; margin-top: 10px;float: right"><img id="verificationCode" th:src="@{/validateCode}" alt=""></div>
				    </a>
			    </div>
			    <p style="color:#ff0000; font-size: 17px;" th:text="${msg}" id="msg" th:if="${not #strings.isEmpty(msg)}" ></p>
			    <p style="font-size: 17px;" id="info"></p>
<!--				<div>-->
				    <div class="checkboxRememberAndLogin">
					    <label>
						    <input type="hidden" name="rememberMe" value="0">
						    <input style="width: 17px;height: 17px;" type="checkbox"  name="rememberMe" id="rememberMe" value="1"/>
					    </label> 记住我
				    </div>
			    <p><input type="submit" name="" value="登	录" ></p>
			    <p class="txt"><a class="" th:href="@{/toRegister}">免费注册</a><a href="forget.html">忘记密码</a></p>
		    </form>
	    </div>
    </body>
</html>
  1. login.js
var usernameObj;
var passwordObj;
var emailObj;
var againPasswordObj;

var usernameMsg;
var passwordMsg;
var emailMsg;
var againPasswordMsg;


// 页面加载之后, 获取页面中的对象
window.onload = function() {
    usernameObj = document.getElementById("userName");
    passwordObj = document.getElementById("password");
    emailObj = document.getElementById("email");
    againPasswordObj = document.getElementById("againPassword");

    usernameMsg = document.getElementById("info");
    passwordMsg = document.getElementById("info");
    emailMsg = document.getElementById("info")
    againPasswordMsg = document.getElementById("info");



};
//login的表单验证
function checkForm() {			// 验证整个表单
    var bUsername = checkUserName2();
    var bPassword = checkPassword();
    return bUsername && bPassword;	// return false后, 事件将被取消
}

// register的表单验证
function checkFormRegister() {			// 验证整个表单
    var bCheckEmail = checkEmail();
    var bPassword = checkPassword();
    var bAgainPassword = checkAgainPassword()
    return bCheckEmail && bPassword &&bAgainPassword;	// return false后, 事件将被取消
}


//验证邮箱
function checkEmail() {			// 验证邮箱
    var regex = /^[\w-]+@([\w-]+\.)+[a-zA-Z]{2,4}$/;
    var value =emailObj.value;
    var msg = "";
    if (!value)
        msg = "邮箱必须填写:";
    else if (!regex.test(value))
        msg = "邮箱格式不合法:";
    emailMsg.innerHTML = msg;
    emailObj.style.color = msg === "" ? "black" : "red";
    emailMsg.style.color = msg === "" ? "black" : "red";
    return msg === "";
}

function checkUsername() {		// 验证用户名
    var regex = /^[a-zA-Z_]\w{0,9}$/;	// 字母数字下划线1到10位, 不能是数字开头
    var value = usernameObj.value;// 获取usernameObj中的文本
    var msg = "";						// 最后的提示消息, 默认为空
    if (!value)							// 如果用户名没填, 填了就是一个字符串可以当作true, 没填的话不论null或者""都是false
        msg = "用户名必须填写:";			// 改变提示消息
    else if (!regex.test(value))		// 如果用户名不能匹配正则表达式规则
        msg = "用户名不合法:";			// 改变提示消息
    usernameMsg.innerHTML = msg;		// 将提示消息放入SPAN
    usernameObj.parentNode.parentNode.style.color = msg === "" ? "black" : "red";	// 根据消息结果改变tr的颜色
    return msg === "";					// 如果提示消息为空则代表没出错, 返回true
}

//Login验证用户名/邮箱/手机号的输入是否合理
function checkUserName2() {		// 验证输入是否为昵称,邮箱,电话
    var regexName = /^[\u4e00-\u9fa5a-zA-Z0-9]{2,8}$/;	//^[\u4e00-\u9fa5a-zA-Z0-9]{6,12}$汉字,字母,数字
    var regexEmail = /^[\w-]+@([\w-]+\.)+[a-zA-Z]{2,4}$/;
    var regexPhone = /^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$/ //手机号/^(13[0-9]|14[5|7]|15[0-9]|18[0-9]|17[0-9])d{8}$/;

    var value = usernameObj.value;// 获取usernameObj中的文本
    var flag = 0
    var msg = "";						// 最后的提示消息, 默认为空
    // if (!value)							// 如果用户名没填, 填了就是一个字符串可以当作true, 没填的话不论null或者""都是false
    //     msg = "昵称/邮箱/电话,必须填写:";			// 改变提示消息
    if(regexName.test(value)){
        msg = "用户名格式正确";     //"用户名为2-8个字符"
        flag = 1;

    }
    else if (regexEmail.test(value)){
        // 如果邮箱能匹配正则表达式规则
        msg ="邮箱格式正确";			// 改变提示消息 "邮箱格式为X@X.X"
        flag = 1;
    }
    else if (regexPhone.test(value)){
        msg = "电话格式正确";
        flag = 1;
        //"电话不符合要求\n以13[0-9],14[579],15[0-3,5-9],16[6],\n17[0135678],18[0-9],19[89]开头11位的电话号码"

    }
    else {
        msg = "用户名为2-8个字符 邮箱格式为X@X.X 电话格式13[0-9],14[579],15[0-3,5-9],16[6],17[0135678],18[0-9],19[89]开头11位数字"
        flag = 0;
    }
    usernameMsg.innerHTML = msg;		// 将提示消息放入SPAN
    usernameObj.style.color = flag === 1 ? "green" : "red";	// 根据消息结果改变tr的颜色parentNode获取父类,childNodes获取子类标签
    usernameMsg.style.color = flag === 1 ? "green" : "red";
    return flag !== 0;					// 如果提示消息为空则代表没出错, 返回true
}

// 验证密码
function checkPassword() {
    var regex = /^[^\u4e00-\u9fa5 ]{6,16}$/;//不含中文空格
    // 任意字符, 6到16位/^.{6,16}$/  字母特殊字符数字/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[~@#$%*-+=:,\\?\[\]{}]).{6,16}$/
    var value = passwordObj.value;
    var msg = "";
    if (!value)
        msg = "密码必须填写:";
    else if (!regex.test(value))
        msg = "密码为6-16位任意字符";
    passwordMsg.innerHTML = msg;
    passwordMsg.style.color = msg === "" ? "green" : "red";
    return msg === "";
}

// 验证确认密码
function checkAgainPassword() {
    var regex = /^[^\u4e00-\u9fa5 ]{6,16}$/;//不含中文空格
    var passwordValue = passwordObj.value;
    var againPassword = againPasswordObj.value;
    var msg = "";

    if(!againPassword){
        msg = "确认密码必须填写";
    }
    else if (!regex.test(passwordValue))
        msg = "密码为6-16位任意字符";
    else if (passwordValue !== againPassword){
        msg = "密码必须保持一致";
    }
    againPasswordMsg.innerHTML = msg;
    againPasswordMsg.style.color = msg === "" ? "black" : "red";
    againPasswordObj.style.color = msg === "" ? "black" : "red";
    return msg === "";
}


//判断用户名是否正确,(验证存在ajax)
function checkUser() {
    var name = $("#userName").val();
    $.ajax({
        type : "post",
        url : "/userNameAjax?userName="+name,
        data : name,
        // dataType:"String",
        success : function(data) {
            // var node = document.getElementById("info");
            // node.firstChild.nodeValue = data;
            document.getElementById("info").innerHTML = data;
            document.getElementById("info").style.color= "red";
        }
        ,
        error:function(data){
            alert("连接失败");
        }
    });
}
$(document).ready(function() {
    $("#userName").blur(function() {
        checkUser();
    });
})

//判断注册邮箱是否被使用,(ajax)
function checkRegEmail() {
    var email = $("#email").val();
    $.ajax({
        type : "post",
        url : "/emailAjax?email="+email,
        data : email,
        success : function(data) {
            document.getElementById("info").innerHTML = data;
            document.getElementById("info").style.color= "red";
        }
        ,
        error:function(data){
            alert("连接失败");
        }
    });
}
$(document).ready(function() {
    $("#email").blur(function() {
        checkRegEmail();
    });
})

//点击刷新验证码图片
function changeVerificationCode() {
    document.getElementById("verificationCode").src="/validateCode?flag="+Math.random();
}

//点击发送邮箱验证码
function changeEmailCode() {
    var email = $("#email").val();
    var msg = "";
    if (!email)
        msg = "请先填写邮箱!";
    else{
        $.ajax({
            type : "post",
            url : "/emailCode?email="+email,
            data : email,
            success : function(data) {
                document.getElementById("info").innerHTML = data;
                document.getElementById("info").style.color= "green";
            }
            ,
            error:function(data){
                document.getElementById("info").innerHTML = "发送失败,请重试或检查网络状态";
                document.getElementById("info").style.color= "red";
            }
        });
    }

        // $("verificationCode").attr('src',"/validateCode?flag="+Math.random());
        // document.getElementById("emailCodeTime").src="/emailCode?email="+email;
        // window.location.href="/emailCode?email="+email;

    emailMsg.innerHTML = msg;
    emailMsg.style.color = msg === "" ? "black" : "red";
}

//计时器
$(function() {
    var emailCodeTime = $("#emailCodeTime");
    var countdown = 60;//倒计时总时间,为了演示效果,设为5秒,一般都是60s
    $(function() {
        emailCodeTime.click(setTime);
    })
    $("#changeEmailCode").click(function() {
        changeEmailCode();
    })

    function setTime() {
        if (countdown === 0) {
            emailCodeTime.attr("disabled", false);
            emailCodeTime.html("获取验证码");
            emailCodeTime.removeClass("disabled");
            countdown = 60;
            return;
        } else {
            emailCodeTime.addClass("disabled");
            emailCodeTime.attr("disabled", true);
            emailCodeTime.html("重新发送(" + countdown + ")");
            countdown--;
        }
        setTimeout(setTime, 1000);
    }
})
  1. ajax所需要的方法判断用户名&判断邮箱

LoginController

/**
    * userNameAjax 登录时后使用ajax验证用户名,邮箱,电话是否存在
    * @param userName 接收用户名,邮箱,电话参数
    * @return 返回反馈信息
    */
    @RequestMapping("/userNameAjax")
    @ResponseBody
    public String userNameAjax(@RequestParam("userName")String userName ){
        User user=loginService.validateLoginUserName(userName);
        if(user==null){
            user= loginService.validateLoginEmail(userName);
            if(user==null){
                user=loginService.validateLoginTelephone(userName);
            }
        }
        System.out.println("user---->"+user);
        System.out.println(userName);
        if(user==null){
            return "输入的昵称/邮箱/电话,不存在";
        }
        return null;
    }
  1. 点击登录,执行登录方法login
@PostMapping("/login")
    public String login(@RequestParam("userName") String userName,
                        @RequestParam("password") String password,
                        @RequestParam("verificationCode") String verificationCode,
                        @RequestParam("rememberMe") String rememberMe,
                        ModelMap modelMap){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        if(verificationCode==null){
            modelMap.addAttribute("msg","验证码为空!");
            return "member/login";
        }
        if(rememberMe.equals("1")) {
            token.setRememberMe(true);
        }
        Session session = subject.getSession();
        String validateCode =(String) session.getAttribute("validateCode");
//        System.out.println("获取session的验证码"+validateCode);
//        System.out.println("登录填写的验证码"+verificationCode);
        if(!verificationCode.equals(validateCode)){
            modelMap.addAttribute("msg","验证码错误!");
            return "member/login";
        }
        try {
            subject.login(token);//执行登录的方法
            return "redirect:/index";
        } catch (UnknownAccountException e) {
            modelMap.addAttribute("msg","账号错误!");
            return "member/login";
        } catch (IncorrectCredentialsException e) {
            modelMap.addAttribute("msg", "密码错误!");
            return "member/login";
        }
    }
  1. LoginServiceImpl实现类方法
@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    LoginMapper loginMapper;

    @Override
    public User validateLogin1(String loginValue, String password) {
        return loginMapper.validateLogin1(loginValue,password);
    }

    @Override
    public User validateLoginUserName(String loginValue) {
        return loginMapper.validateLoginUserName(loginValue);
    }

    @Override
    public User validateLoginEmail(String loginValue) {
        return loginMapper.validateLoginEmail(loginValue);
    }

    @Override
    public User validateLoginTelephone(String loginValue) {
        return loginMapper.validateLoginTelephone(loginValue);
    }
}
  1. LoginMapper中的方法定义
@Mapper
public interface LoginMapper {
    User validateLogin1(@Param("VI") String loginValue,@Param("password")String password);
    User validateLoginUserName(@Param("VI") String loginValue);//验证信息verificationInformation
    User validateLoginEmail(@Param("VI") String loginValue);
    User validateLoginTelephone(@Param("VI") String loginValue);
}
  1. LoginMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.cheng.member.mapper.LoginMapper">

    <select id="validateLogin1" resultType="User" parameterType="String">
        select * from ecologicalhome.user where userName=#{VI} and password=#{password};
    </select>

    <select id="validateLoginUserName" resultType="User" parameterType="String">
        select * from ecologicalhome.user
        where userName=#{VI};
    </select>
    <select id="validateLoginEmail" resultType="User" parameterType="String">
        select * from ecologicalhome.user
        where email=#{VI};
    </select>
    <select id="validateLoginTelephone" resultType="User" parameterType="String">
        select * from ecologicalhome.user
        where telephone=#{VI};
    </select>


</mapper>
  1. 登录中的Shiro登录认证
@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean 3.
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean =new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);


        Map<String,String> filterMap = new LinkedHashMap<>();

//        授权
        filterMap.put("/user/addUser","perms[user:addUser]");
        filterMap.put("/user/updateUser","perms[user:updateUser]");
        filterMap.put("/user/*","authc");

        bean.setFilterChainDefinitionMap(filterMap);

        //设置登录的请求
        bean.setLoginUrl("/toLogin");

        //未授权的请求
        bean.setUnauthorizedUrl("/noauth");
        return bean;
    }
    @Bean
    public SimpleCookie rememberMeCookie(){
        // 这个参数是 cookie 的名称,叫什么都行,我这块取名 rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        // setcookie 的 httponly 属性如果设为 true 的话,会增加对 xss 防护的安全系数,
        // 只能通过http访问,javascript无法访问,防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        // 记住我 cookie 生效时间30天 ,单位是秒
        simpleCookie.setMaxAge(7*24*60*60);
        return simpleCookie;
    }
    /**
    * cookie管理对象;记住我功能,rememberMe管理器
    * @return
    */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

    /**
    * FormAuthenticationFilter 过滤器 过滤记住我
    * @return
    */
    @Bean
    public FormAuthenticationFilter formAuthenticationFilter(){
        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
        // 对应 rememberMeCookie() 方法中的 name
        formAuthenticationFilter.setRememberMeParam("rememberMe");
        return formAuthenticationFilter;
    }

    //DefaultWebSecurityManager 2.
    @Bean(name = "defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//        需要关联UserRealm
        securityManager.setRealm(userRealm);
        securityManager.setRememberMeManager(rememberMeManager());
        // 将 sessionManager 注入到 SecurityManager 中,否则不会生效
//        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }



    //创建realm 对象  自定义对象 1.
    @Bean(name = "userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }

//整合ShiroDialect :用来整合shiro和thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

}
  1. aa
//自定义的UserRealm extends
public class UserRealm extends AuthorizingRealm {
    User user;

    @Autowired
    LoginService loginService;


    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行啦 --------->>授权doGetAuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        info.addStringPermission("user:index");
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();
//        设置当前用户的权限
//        info.addStringPermission(currentUser.getPower());
        return info;
    }

    //    认证 authentication
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行啦 --------->>认证doGetAuthorizationInfo");

        //用户名,密码
        //链接真实的数据库
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        user=loginService.validateLoginUserName(userToken.getUsername());
        if(user==null){
            user= loginService.validateLoginEmail(userToken.getUsername());
            if(user==null){
                user=loginService.validateLoginTelephone(userToken.getUsername());
                if(user==null){
                    user=loginService.validateLoginTelephone(userToken.getUsername());
                }
            }
        }

        if(user==null){//没有这个人
            return null;//会抛出一个异常
        }
        else {
            if(String.valueOf(userToken.getPassword()).equals(user.getPassword())){
                Session session = subject.getSession();
                session.setAttribute("loginUser",user);
            }
        }
//        System.out.println(user);

        //        System.out.println("simpleAuthenticationInfo-----------"+simpleAuthenticationInfo);
        //密码认证 shiro帮我们做
        return new SimpleAuthenticationInfo(user, user.getPassword(), "");
    }
}
  1. 注册部分核心代码
  1. registerController
/**
 * toRegister 去注册页面
 * @return
 */
@RequestMapping("/toRegister")
public String toRegister(){
    return "member/reg";
}
  1. register.thml
<head>
	<meta charset="UTF-8">
	<title>注册</title>
	<link rel="stylesheet" type="text/css" th:href="@{member/css/public.css}"/>
	<link rel="stylesheet" type="text/css" th:href="@{member/css/login.css}"/>
	<script type="text/javascript" th:src="@{member/js/jquery-3.6.0.js}"></script>
	<script type="text/javascript" th:src="@{member/js/login.js}"></script>
</head>
<body>
	<!-------------------reg-------------------------->
	<div class="reg">
		<form th:action="@{/register}" method="post" onsubmit="return checkFormRegister()">
			<h1><a th:href="@{/index}"><img th:src="@{member/img/temp/logo.png}"></a></h1>

			<p><label>
				<input type="email" name="email" id="email" placeholder="请输入邮箱:" required="" onkeyup="checkEmail()">
			</label></p>

			<p><label>
				<input type="password" name="password" id="password" placeholder="请输入密码:" onkeyup="checkPassword()">
			</label></p>

			<p><label>
				<input type="password" id="againPassword" placeholder="请确认密码:" onkeyup="checkAgainPassword()">
			</label></p>

			<p class="txtL txt">
				<div>
					<label>
						<input style="width: 100px;" type="text" name="emailCode"  placeholder="验证码:" required="" />
					</label>
					<a id="changeEmailCode">
						<button type="button" class="emailCode" id="emailCodeTime">获取验证码</button>
					</a>
				</div>
			</p>
			<p style="font-size: 17px;" id="info"></p>
			<p style="color:#ff0000;font-size: 17px;" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}" ></p>
			<p><input type="submit" name="" value="注册"></p>
			<p class="txtL txt">注册即同意该   <a href="#">使用条款和隐私策略</a></p>
			<p class="txt"><a th:href="@{/toLogin}"><span></span>已有账号登录</a></p>
			<a th:href="@{/}" class="off"><img th:src="@{member/img/temp/off.png}"></a>
		</form>
	</div>
</body>
  1. login.js文件在登录页面已经展示,现在展示部分核心代码
//点击发送邮箱验证码
function changeEmailCode() {
    var email = $("#email").val();
    var msg = "";
    if (!email)
        msg = "请先填写邮箱!";
    else{
        $.ajax({
            type : "post",
            url : "/emailCode?email="+email,
            data : email,
            success : function(data) {
                document.getElementById("info").innerHTML = data;
                document.getElementById("info").style.color= "green";
            }
            ,
            error:function(data){
                document.getElementById("info").innerHTML = "发送失败,请重试或检查网络状态";
                document.getElementById("info").style.color= "red";
            }
        });
    }

        // $("verificationCode").attr('src',"/validateCode?flag="+Math.random());
        // document.getElementById("emailCodeTime").src="/emailCode?email="+email;
        // window.location.href="/emailCode?email="+email;

    emailMsg.innerHTML = msg;
    emailMsg.style.color = msg === "" ? "black" : "red";
}

//计时器
$(function() {
    var emailCodeTime = $("#emailCodeTime");
    var countdown = 60;//倒计时总时间,为了演示效果,设为5秒,一般都是60s
    $(function() {
        emailCodeTime.click(setTime);
    })
    $("#changeEmailCode").click(function() {
        changeEmailCode();
    })

function setTime() {
    if (countdown === 0) {
        emailCodeTime.attr("disabled", false);
        emailCodeTime.html("获取验证码");
        emailCodeTime.removeClass("disabled");
        countdown = 60;
        return;
    } else {
        emailCodeTime.addClass("disabled");
        emailCodeTime.attr("disabled", true);
        emailCodeTime.html("重新发送(" + countdown + ")");
        countdown--;
    }
    setTimeout(setTime, 1000);
    }
})
  1. ajax执行发送验证码的方法
    在这之前需要引入email的相关jar包
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
        <version>2.6.4</version>
    </dependency>

application.yml相关配置

spring:
  mail:
    host: smtp.qq.com
    username: 264xxxx933@qq.com
    password: ncslrxtbvrigebad
    properties:
    mail:
        stmp:
        ssl:
            enable: true

RegisterCotroller

/**
 * register 执行注册方法,判断验证是否正确
 * @param user 用户对象
 * @param emailCode 邮箱验证码
 * @param modelMap 向前端反馈信息
 * @return 注册成功重定向到去登录页,失败返回到注册页
 */
@PostMapping("/register")
public String register(User user,@RequestParam("emailCode")String emailCode, ModelMap modelMap){
    Subject subject = SecurityUtils.getSubject();
    Session session = subject.getSession();
    String validateCode =(String) session.getAttribute("validateCode");
    String emailName =(String) session.getAttribute("emailName");
    if(validateCode!=null&&emailName.equals(user.getEmail()) && validateCode.equals(emailCode)){
        do {
            user.setUserName("用户" + outputString.getNumberString(5));
        } while (loginService.validateLoginUserName(user.getUserName()) != null);
        user.setCountry("中国");
        user.setIntroduce("");
        user.setRole("member");
        user.setUserAvatar("member/img/userAvatar/default_avatar.png");
        user.setRegisterTime(new Timestamp(new Date().getTime()));
        int reg=registerService.insertUser(user);
        if(reg>0){
            return "redirect:/toLogin";
        }
        else {
            modelMap.addAttribute("msg","注册失败!");
            return "member/reg";
        }
    }else {
        modelMap.addAttribute("msg","验证码错误!");
        return "member/reg";
    }

}

/**
 * emailAjax 利用ajax判断邮箱是否已经被注册
 * @param email 接收邮箱号
 * @return 返回判断信息,未被使用返回空
 */
@RequestMapping("/emailAjax")
@ResponseBody
public String emailAjax(@RequestParam("email") String email){
    User user= loginService.validateLoginEmail(email);
    if(user!=null){
        System.out.println("这个邮箱被使用:"+email);
        return "邮箱已被使用,请登录";
    }
    return null;
}

//    获取配置中的邮件发送方
@Value("${spring.mail.username}")
private String from;

/**
 * 实现页面不刷新,发送邮箱验证码
 * @param email 指定验证码发送到的邮箱
 * @return 返回是否发送成功验证码,不成功返回null
 */
@RequestMapping("/emailCode")
@ResponseBody
public String emailCode(@RequestParam("email") String email) {

    User user = loginService.validateLoginEmail(email);
    System.out.println("emailCode------>user:"+user);
    if(user==null){
        String code = ValidateCode.generateTextCode(0, 6, null);
        System.out.println("注册邮箱生成的验证码"+code+"----"+new Timestamp(new Date().getTime()));
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.removeAttribute("validateCode");
        session.setAttribute("validateCode",code);
        session.removeAttribute("emailName");
        session.setAttribute("emailName",email);

        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("智能家居");
        message.setText("你获取的智能家居验证码为:"+code+"     验证码请在3分钟内使用,切勿向任何人泄露验证码");
        message.setTo(email);
        message.setFrom(from);
        mailSender.send(message);
        return "验证码已发送";
    }
    return null;
}
  1. RegisterService和RegisterMapper类中只有一个插入数据方法不在描述

总结:

第一次写博客,多多包涵