admin管理员组文章数量:1567305
# ecologicalhome项目的登录注册
基于SpringBoot框架实现的web登录注册项目,
使用框架或技术:
后端 SpringBoot、mybatis、mysql、shiro 等
前端 jquery Thymeleaf 基于jQ的Ajax等
1. 效果图演示
1. 登录:
1. 主页登录注册入口
![主页登录,注册入口](vx_images/388972113238996.png)
2. 点击登录来到登录页面![登录页面](vx_images/186772413226863.png)
3. 登录页面实现ajax查询用户名是否存在,js用户名,密码格式限制,点击验证码实现刷新对登录按钮进行限制,只有用户名,密码符合要求才可以点击登录,shiro实现登录控制,记住我,点击免费注册跳转注册页
用户名控制![用户名控制](vx_images/581433213247029.png)
密码控制![密码控制](vx_images/285183313239698.png)
ajax验证用户名 失去光标执行验证![用户名验证实现异步刷新](vx_images/76563513236253.png)
点击刷新验证码 验证码局部刷新![刷新前](vx_images/503383813232007.png)![刷新后](vx_images/63533913249887.png)
2. 注册:
1. 主页点击注册,或者登录页点击免费注册![注册页](vx_images/471845213247491.png)
2. 注册页Ajax实现判断注册邮箱是否被使用,js对密码的控制,验证确认密码是否一致,输入都符合要求时解除对注册按钮的控制,ajax发送邮箱验证码
邮箱控制![邮箱控制](vx_images/206230014244993.png)
密码控制![密码控制](vx_images/58600114226234.png)
再次输入密码控制![再次输入密码控制](vx_images/554000114248674.png)
点击发送邮箱验证码![aaa](vx_images/186030615237356.png)![验证码](vx_images/368050615230490.png)
2. 登录部分核心代码
1. 目录结构:
![目录结构](vx_images/517100915221020.png)
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>
```
3. loginController
```
/**
* 去登录页面
* @return 登陆页面
*/
@RequestMapping("/toLogin")
public String toLogin(){
return "member/login";
}
```
4. 准备图片验证码工具类
工具类网上有众多优秀的资源
本篇博客验证码获取工具类来源于:
https://blog.csdn/rickiyeat/article/details/55050440
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;
}
}
```
5. 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());
}
```
6. login.html
提前准备jquery,和jquery.cookie两个js文件
jquery-3.6.0.js
jquery.cookie.js
```
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf">
<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" οnsubmit="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="" οnkeyup="checkUserName2()"/>
<!-- autofocus="" 自动聚焦光标-->
</label>
<label>
<input type="password" name="password" id="password" placeholder="密码:" required="" οnkeyup="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>
```
7. 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);
}
})
```
8. 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;
}
```
9. 点击登录,执行登录方法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";
}
}
```
10. 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);
}
}
```
11. 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);
}
```
12. LoginMapper.xml
```
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis//DTD Mapper 3.0//EN"
"http://mybatis/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>
```
13. 登录中的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();
}
}
```
14. 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(), "");
}
}
```
3. 注册部分核心代码
1. registerController
```
/**
* toRegister 去注册页面
* @return
*/
@RequestMapping("/toRegister")
public String toRegister(){
return "member/reg";
}
```
2. 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" οnsubmit="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="" οnkeyup="checkEmail()">
</label></p>
<p><label>
<input type="password" name="password" id="password" placeholder="请输入密码:" οnkeyup="checkPassword()">
</label></p>
<p><label>
<input type="password" id="againPassword" placeholder="请确认密码:" οnkeyup="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>
```
3. 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);
}
})
```
4. ajax执行发送验证码的方法
在这之前需要引入email的相关jar包
```
<!-- https://mvnrepository/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
username: 264xxxx933@qq
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;
}
```
5. RegisterService和RegisterMapper类中只有一个插入数据方法不在描述
### 总结:
#### 第一次写博客,多多包涵
### 祝大家学有所成,快快上岸
### 关注我不迷路
本文标签: 项目SpringBootweb
版权声明:本文标题:springBoot web项目的登录注册 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1726721477a1081876.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论