待办清单项目12:完善注册和登录功能
5/8/2025
280 阅读
封装WebUtils工具类
1.打开idea后端项目,在utils包内创建一个WebUtils类文件
粘贴如下的代码:
package com.youngshu.todolist.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.youngshu.todolist.common.Result;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
public class WebUtils {
private static ObjectMapper objectMapper;
// 初始化ObjectMapper
static{
objectMapper=new ObjectMapper();
// 设置JSON和Object转换时的时间日期格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
// 从请求中获取JSON串并转换为Object
public static <T> T readJson(HttpServletRequest request, Class<T> clazz) {
T t = null;
BufferedReader reader = null;
try {
reader = request.getReader();
StringBuffer buffer = new StringBuffer();
String line = null;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
t = objectMapper.readValue(buffer.toString(), clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
return t;
}
// 将Result对象转换成JSON串并放入响应对象
public static void writeJson(HttpServletResponse response, Result result) {
response.setContentType("application/json;charset=UTF-8");
try {
String json = objectMapper.writeValueAsString(result);
response.getWriter().write(json);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
2.接下来用刚封装的类来简化代码,双击SysUserController文件,把验证用户名是否存在方面里面的代码改成下面这样
3.启动服务器,访问注册页面查看检验用户名是否可用的功能是否还可用使用
完成注册页的功能
1.用vscode打开前端项目,在命令行中运行 npm install axios
指令来按住axios
2.在src文件夹中新建utils文件夹,然后在utils文件中新建一个request.js文件
request.js文件的代码如下:
import axios from 'axios'
// 创建instance实例
const instance = axios.create({
baseURL: 'http://localhost:8080/'
})
// 添加请求拦截
instance.interceptors.request.use(
// 设置请求头配置信息
config => {
// 处理指定的请求头
return config
},
// 设置请求错误处理函数
error => {
return Promise.reject(error)
}
)
// 添加响应拦截器
instance.interceptors.response.use(
// 设置响应正确时的处理函数
response => {
return response
},
// 设置响应异常时的处理函数
error => {
return Promise.reject(error)
}
)
// 默认导出
export default instance
3.修改注册组件的代码,把Regist.vue文件中的代码改成下面这样
<script setup>
import { ref, reactive } from 'vue'
import request from '../utils/request'
let registUser = reactive({
username: "",
userpassword: ""
})
let usernameMsg = ref('')
let userPwdMsg = ref('')
let reUserPwdMsg = ref('')
let reUserPwd = ref('')
async function checkUsername() {
let usernameReg =/^[A-Za-z](?=.*[A-Za-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{7,}$/;//用户名必须以英文字母开头,必须包含英文字母、数字和特殊字符三种字符,长度不少于8位
if (!usernameReg.test(registUser.username)) {
usernameMsg.value = "格式有误,用户名必须以英文字母开头,必须包含英文字母、数字和特殊字符三种字符,长度不少于8位"
return false
}
// 继续校验用户名是否被占用
let { data } = await request.post(`TodoList/user/checkUsername?username=${registUser.username}`)
if (data.code!== 200) {
usernameMsg.value = "用户名已被占用"
return false
}
usernameMsg.value = ""
return true
}
async function checkPassword(){
let userPwdReg = /^(?:(?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[^A-Za-z0-9])|(?=.*\d)(?=.*[^A-Za-z0-9])).{6,}$/; // 密码至少包含字母、数字、特殊字符中的两种,长度至少6位
if(!userPwdReg.test(registUser.userpassword)){
userPwdMsg.value="密码至少包含字母、数字、特殊字符中的两种,长度至少6位"
return false
}
userPwdMsg.value=""
return true
}
async function checkReUserPwd() {
let userPwdReg = /^(?:(?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[^A-Za-z0-9])|(?=.*\d)(?=.*[^A-Za-z0-9])).{6,}$/; // 密码至少包含字母、数字、特殊字符中的两种,长度至少6位
if (!userPwdReg.test(reUserPwd.value)) {
reUserPwdMsg.value = "密码至少包含字母、数字、特殊字符中的两种,长度至少6位";
return false;
}
if (registUser.userpassword!== reUserPwd.value) {
reUserPwdMsg.value = "两次密码不一致";
return false;
}
reUserPwdMsg.value = "";
return true;
}
async function regist() {
// 校验所有的输入框是否合法
let flag1 = await checkUsername()
let flag2 = await checkPassword()
let flag3 = await checkReUserPwd()
console.log(flag1)
console.log(flag2)
console.log(flag3)
if (flag1 && flag2 && flag3) {
request.post("TodoList/user/regist", registUser)
} else {
alert("校验不通过,请求再次检查数据")
}
}
</script>
<template>
<div class="register-wrapper">
<div class="register-card">
<div class="register-header">
<!-- 使用不同的 SVG 图标 -->
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user-plus"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="23" y1="11" x2="17" y2="11"></line></svg>
<h2>创建账户</h2>
<p>加入我们,开始管理您的待办事项!</p>
</div>
<form @submit.prevent="handleRegister" class="register-form">
<!-- 用户名输入组 -->
<div class="input-group">
<label for="username">用户名</label>
<input
type="text"
id="username"
v-model="registUser.username"
placeholder="用户名"
required
@blur="checkUsername()"
/>
<p v-if="usernameMsg" id="username-error" class="error-message">{{ usernameMsg }}</p>
</div>
<!-- 密码输入组 -->
<div class="input-group">
<label for="password">密码</label>
<input
type="password"
id="password"
v-model="registUser.userpassword"
placeholder="请输入密码"
required
@blur="checkPassword()"
/>
<p v-if="userPwdMsg" id="password-error" class="error-message">{{ userPwdMsg }}</p>
</div>
<!-- 确认密码输入组 -->
<div class="input-group">
<label for="confirmPassword">确认密码</label>
<input
type="password"
id="confirmPassword"
v-model="reUserPwd"
placeholder="请再次输入密码"
required
@blur="checkReUserPwd()"
/>
<p v-if="reUserPwdMsg" id="confirm-password-error" class="error-message">{{ reUserPwdMsg }}</p>
</div>
<!-- 注册按钮 -->
<button type="submit" class="btn-register" @click="regist()">注 册</button>
</form>
<!-- 页脚链接 -->
<div class="register-footer">
<router-link to="/login">已有账号?返回登录</router-link>
</div>
</div>
</div>
</template>
<style scoped>
/* 整体布局和卡片样式 (与 Login.vue 类似) */
.register-wrapper {
/* min-height: 100vh; */ /* 移除 min-height */
flex-grow: 1; /* 让 wrapper 在 main-content 内填充可用空间 */
display: flex;
justify-content: center;
align-items: center;
/* background-color: #fff; */ /* 背景色由 main-content 或 body 控制,或者按需保留 */
padding: 2rem; /* 保留内边距 */
}
.register-card {
background: white;
padding: 2.5rem 3rem;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 420px;
text-align: center;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.register-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
}
/* 头部样式 */
.register-header {
margin-bottom: 2rem;
}
.register-header svg {
color: #4361ee; /* 主题色 */
margin-bottom: 0.5rem;
}
.register-header h2 {
font-size: 1.8rem;
color: #333;
margin-bottom: 0.5rem;
}
.register-header p {
color: #777;
font-size: 0.95rem;
}
/* 表单样式 */
.register-form {
display: flex;
flex-direction: column;
gap: 0.8rem; /* 调整输入框组之间的基础间距 */
}
/* 输入框组样式 */
.input-group {
text-align: left;
margin-bottom: 0.7rem; /* 为错误信息预留空间 */
position: relative; /* 使得错误信息可以定位 */
}
.input-group label {
display: block;
margin-bottom: 0.5rem;
color: #555;
font-weight: 500;
font-size: 0.9rem;
}
.input-group input {
width: 100%;
padding: 0.8rem 1rem;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 1rem;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
box-sizing: border-box;
}
.input-group input:focus {
outline: none;
border-color: #4361ee;
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1);
}
/* 错误状态的输入框 */
.input-group input.input-error {
border-color: #ef476f; /* 红色边框 */
}
.input-group input.input-error:focus {
border-color: #ef476f;
box-shadow: 0 0 0 3px rgba(239, 71, 111, 0.1); /* 红色光晕 */
}
/* 错误信息样式 */
.error-message {
color: #ef476f;
font-size: 0.8rem;
margin-top: 0.3rem;
min-height: 1.1rem; /* 占据固定高度防止跳动 */
/* position: absolute; */ /* 可以取消绝对定位,让其自然流动 */
/* bottom: -1.2rem; */
/* left: 0; */
width: 100%; /* 确保宽度 */
text-align: left;
}
/* 注册按钮样式 (与 Login.vue 类似,但类名不同) */
.btn-register {
padding: 0.9rem;
background: linear-gradient(90deg, #4361ee 0%, #3a53c0 100%);
color: white;
border: none;
border-radius: 6px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 1rem; /* 与上方元素的间距 */
letter-spacing: 1px;
}
.btn-register:hover {
background: linear-gradient(90deg, #3a53c0 0%, #4361ee 100%);
box-shadow: 0 4px 15px rgba(67, 97, 238, 0.3);
transform: translateY(-2px);
}
/* 页脚链接样式 (与 Login.vue 类似) */
.register-footer {
margin-top: 1.5rem;
font-size: 0.9rem;
}
.register-footer a {
color: #4361ee;
text-decoration: none;
transition: color 0.3s ease;
}
.register-footer a:hover {
color: #3451db;
text-decoration: underline;
}
</style>
4.在浏览器中访问前端页面,若输入有误,会有响应的提示,若输入正确,点了注册按钮后,在网络中可用看到我们在往服务器端发送注册请求,在负载里面携带了json格式的用户名和密码
5.打开IDEA后端项目,找到SysUserController中的regist方法,把代码改成下面这样:
// 1 接收客户端提交的json参数,并转换为User对象,获取信息
SysUser registUser = WebUtils.readJson(req, SysUser.class);
// 2 调用服务层方法,完成注册功能
//将参数放入一个SysUser对象中,在调用regist方法时传入
int rows = sysUserService.regist(registUser);
// 3 根据注册结果(成功 失败) 做页面跳转
Result result = Result.ok(null);
if (rows < 1) {
result = Result.build(null, ResultCodeEnum.USERNAME_USED);
}
WebUtils.writeJson(resp, result);
6.重新部署一下Tomcat服务器
7.回到前端项目中,再次找到注册组件Regist.vue,把代码改成下面这样
<script setup>
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import request from '../utils/request'
let router = useRouter()
let registUser = reactive({
username: "",
user_pwd: ""
})
let usernameMsg = ref('')
let userPwdMsg = ref('')
let reUserPwdMsg = ref('')
let reUserPwd = ref('')
async function checkUsername() {
let usernameReg =/^[A-Za-z](?=.*[A-Za-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{7,}$/;//用户名必须以英文字母开头,必须包含英文字母、数字和特殊字符三种字符,长度不少于8位
if (!usernameReg.test(registUser.username)) {
usernameMsg.value = "格式有误,用户名必须以英文字母开头,必须包含英文字母、数字和特殊字符三种字符,长度不少于8位"
return false
}
// 继续校验用户名是否被占用
let { data } = await request.post(`TodoList/user/checkUsername?username=${registUser.username}`)
if (data.code!== 200) {
usernameMsg.value = "用户名已被占用"
return false
}
usernameMsg.value = ""
return true
}
async function checkPassword(){
let userPwdReg = /^(?:(?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[^A-Za-z0-9])|(?=.*\d)(?=.*[^A-Za-z0-9])).{6,}$/; // 密码至少包含字母、数字、特殊字符中的两种,长度至少6位
if(!userPwdReg.test(registUser.user_pwd)){
userPwdMsg.value="密码至少包含字母、数字、特殊字符中的两种,长度至少6位"
return false
}
userPwdMsg.value=""
return true
}
async function checkReUserPwd() {
let userPwdReg = /^(?:(?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[^A-Za-z0-9])|(?=.*\d)(?=.*[^A-Za-z0-9])).{6,}$/; // 密码至少包含字母、数字、特殊字符中的两种,长度至少6位
if (!userPwdReg.test(reUserPwd.value)) {
reUserPwdMsg.value = "密码至少包含字母、数字、特殊字符中的两种,长度至少6位";
return false;
}
if (registUser.user_pwd!== reUserPwd.value) {
reUserPwdMsg.value = "两次密码不一致";
return false;
}
reUserPwdMsg.value = "";
return true;
}
async function regist() {
// 校验所有的输入框是否合法
let flag1 = await checkUsername()
let flag2 = await checkPassword()
let flag3 = await checkReUserPwd()
if (flag1 && flag2 && flag3) {
let {data} = await request.post("TodoList/user/regist", registUser)
if (data.code == 200) {
// 注册成功跳转 登录页
alert("注册成功,快去登录吧")
router.push("/login")
} else {
alert("抱歉,用户名被抢注了")
}
} else {
alert("校验不通过,请求再次检查数据")
}
}
</script>
<template>
<div class="register-wrapper">
<div class="register-card">
<div class="register-header">
<!-- 使用不同的 SVG 图标 -->
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user-plus"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="23" y1="11" x2="17" y2="11"></line></svg>
<h2>创建账户</h2>
<p>加入我们,开始管理您的待办事项!</p>
</div>
<form @submit.prevent="handleRegister" class="register-form">
<!-- 用户名输入组 -->
<div class="input-group">
<label for="username">用户名</label>
<input
type="text"
id="username"
v-model="registUser.username"
placeholder="用户名"
required
@blur="checkUsername()"
/>
<p v-if="usernameMsg" id="username-error" class="error-message">{{ usernameMsg }}</p>
</div>
<!-- 密码输入组 -->
<div class="input-group">
<label for="password">密码</label>
<input
type="password"
id="password"
v-model="registUser.user_pwd"
placeholder="请输入密码"
required
@blur="checkPassword()"
/>
<p v-if="userPwdMsg" id="password-error" class="error-message">{{ userPwdMsg }}</p>
</div>
<!-- 确认密码输入组 -->
<div class="input-group">
<label for="confirmPassword">确认密码</label>
<input
type="password"
id="confirmPassword"
v-model="reUserPwd"
placeholder="请再次输入密码"
required
@blur="checkReUserPwd()"
/>
<p v-if="reUserPwdMsg" id="confirm-password-error" class="error-message">{{ reUserPwdMsg }}</p>
</div>
<!-- 注册按钮 -->
<button type="submit" class="btn-register" @click="regist()">注 册</button>
</form>
<!-- 页脚链接 -->
<div class="register-footer">
<router-link to="/login">已有账号?返回登录</router-link>
</div>
</div>
</div>
</template>
<style scoped>
/* 整体布局和卡片样式 (与 Login.vue 类似) */
.register-wrapper {
/* min-height: 100vh; */ /* 移除 min-height */
flex-grow: 1; /* 让 wrapper 在 main-content 内填充可用空间 */
display: flex;
justify-content: center;
align-items: center;
/* background-color: #fff; */ /* 背景色由 main-content 或 body 控制,或者按需保留 */
padding: 2rem; /* 保留内边距 */
}
.register-card {
background: white;
padding: 2.5rem 3rem;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 420px;
text-align: center;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.register-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
}
/* 头部样式 */
.register-header {
margin-bottom: 2rem;
}
.register-header svg {
color: #4361ee; /* 主题色 */
margin-bottom: 0.5rem;
}
.register-header h2 {
font-size: 1.8rem;
color: #333;
margin-bottom: 0.5rem;
}
.register-header p {
color: #777;
font-size: 0.95rem;
}
/* 表单样式 */
.register-form {
display: flex;
flex-direction: column;
gap: 0.8rem; /* 调整输入框组之间的基础间距 */
}
/* 输入框组样式 */
.input-group {
text-align: left;
margin-bottom: 0.7rem; /* 为错误信息预留空间 */
position: relative; /* 使得错误信息可以定位 */
}
.input-group label {
display: block;
margin-bottom: 0.5rem;
color: #555;
font-weight: 500;
font-size: 0.9rem;
}
.input-group input {
width: 100%;
padding: 0.8rem 1rem;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 1rem;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
box-sizing: border-box;
}
.input-group input:focus {
outline: none;
border-color: #4361ee;
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1);
}
/* 错误状态的输入框 */
.input-group input.input-error {
border-color: #ef476f; /* 红色边框 */
}
.input-group input.input-error:focus {
border-color: #ef476f;
box-shadow: 0 0 0 3px rgba(239, 71, 111, 0.1); /* 红色光晕 */
}
/* 错误信息样式 */
.error-message {
color: #ef476f;
font-size: 0.8rem;
margin-top: 0.3rem;
min-height: 1.1rem; /* 占据固定高度防止跳动 */
/* position: absolute; */ /* 可以取消绝对定位,让其自然流动 */
/* bottom: -1.2rem; */
/* left: 0; */
width: 100%; /* 确保宽度 */
text-align: left;
}
/* 注册按钮样式 (与 Login.vue 类似,但类名不同) */
.btn-register {
padding: 0.9rem;
background: linear-gradient(90deg, #4361ee 0%, #3a53c0 100%);
color: white;
border: none;
border-radius: 6px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 1rem; /* 与上方元素的间距 */
letter-spacing: 1px;
}
.btn-register:hover {
background: linear-gradient(90deg, #3a53c0 0%, #4361ee 100%);
box-shadow: 0 4px 15px rgba(67, 97, 238, 0.3);
transform: translateY(-2px);
}
/* 页脚链接样式 (与 Login.vue 类似) */
.register-footer {
margin-top: 1.5rem;
font-size: 0.9rem;
}
.register-footer a {
color: #4361ee;
text-decoration: none;
transition: color 0.3s ease;
}
.register-footer a:hover {
color: #3451db;
text-decoration: underline;
}
</style>
8.在浏览器中尝试注册新用户,看是否能成功
完善登录页的功能
1.修改Login.vue文件
把代码改成下面这样:
<script setup>
import { ref, reactive } from 'vue'
import request from '../utils/request'
import { useRouter } from 'vue-router'
const router = useRouter()
let loginUser = reactive({
username: "",
user_pwd: ""
})
let usernameMsg = ref("")
let userPwdMsg = ref("")
function checkUsername(){
// 用正则表达式验证用户名
// 用户名必须以英文字母开头,必须包含英文字母、数字和特殊字符三种字符,长度不少于8位
const reg = /^[A-Za-z](?=.*[A-Za-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{7,}$/;
if (reg.test(loginUser.username)) {
usernameMsg.value = '';
return true;
} else {
usernameMsg.value = '用户名必须以英文字母开头,必须包含英文字母、数字和特殊字符三种字符,长度不少于8位';
return false;
}
};
function checkPassword(){
// 用正则表达式验证密码
const reg = /^(?:(?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[^A-Za-z0-9])|(?=.*\d)(?=.*[^A-Za-z0-9])).{6,}$/; // 密码至少包含字母、数字、特殊字符中的两种,长度至少6位
if (reg.test(loginUser.user_pwd)) {
userPwdMsg.value = '';
return true;
} else {
userPwdMsg.value = '// 密码至少包含字母、数字、特殊字符中的两种,长度至少6位';
return false;
}
};
async function login() {
var flag = checkUsername() && checkPassword();
if (!flag) {
return;
}
let {data} = await request.post('TodoList/user/login',loginUser);
if (data.code === 200) {
alert("登录成功");
router.push('/todolist');
} else if (data.code === 503) {
alert("密码错误");
} else if (data.code === 502) {
alert("用户名不存在");
}
else {
alert("未知错误,登录失败");
}
};
</script>
<template>
<div class="login-wrapper">
<div class="login-card">
<div class="login-header">
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-square"><polyline points="9 11 12 14 22 4"></polyline><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path></svg>
<h2>用户登录</h2>
<p>欢迎回来!请登录您的账户。</p>
</div>
<form @submit.prevent="handleLogin" class="login-form">
<div class="input-group">
<label for="username">用户名</label>
<input
type="text"
id="username"
v-model="loginUser.username"
@blur="checkUsername()"
placeholder="请输入用户名"
required
/>
<div v-if="usernameMsg" class="error-msg">{{ usernameMsg }} </div>
</div>
<div class="input-group">
<label for="password">密码</label>
<input
type="password"
id="password"
v-model="loginUser.user_pwd"
@blur = "checkPassword()"
placeholder="请输入密码"
required
/>
<div v-if="userPwdMsg" class="error-msg">{{ userPwdMsg }} </div>
</div>
<button type="submit" class="btn-login" @click="login()">登 录</button>
</form>
<div class="login-footer">
<router-link to="/regist">还没有账号?立即注册</router-link>
</div>
</div>
</div>
</template>
<style scoped>
.error-msg {
color: red;
font-size: 0.8rem;
margin-top: 0.2rem;
}
.login-wrapper {
/* min-height: 100vh; */ /* 移除 min-height */
flex-grow: 1; /* 让 wrapper 在 main-content 内填充可用空间 */
display: flex;
justify-content: center;
align-items: center;
/* background-color: #fff; */ /* 背景色由 main-content 或 body 控制,或者按需保留 */
padding: 2rem; /* 保留内边距 */
}
.login-card {
background: white;
padding: 2.5rem 3rem;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 420px;
text-align: center;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.login-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
}
.login-header {
margin-bottom: 2rem;
}
.login-header svg {
color: #4361ee;
margin-bottom: 0.5rem;
}
.login-header h2 {
font-size: 1.8rem;
color: #333;
margin-bottom: 0.5rem;
}
.login-header p {
color: #777;
font-size: 0.95rem;
}
.login-form {
display: flex;
flex-direction: column;
gap: 1.5rem; /* 增加输入框间距 */
}
.input-group {
text-align: left;
}
.input-group label {
display: block;
margin-bottom: 0.5rem;
color: #555;
font-weight: 500;
font-size: 0.9rem;
}
.input-group input {
width: 100%;
padding: 0.8rem 1rem; /* 增加内边距 */
border: 1px solid #ddd;
border-radius: 6px; /* 更圆润的边角 */
font-size: 1rem;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
box-sizing: border-box; /* 确保 padding 不会撑大元素 */
}
.input-group input:focus {
outline: none;
border-color: #4361ee;
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1); /* 添加聚焦光晕效果 */
}
.btn-login {
padding: 0.9rem; /* 增加按钮内边距 */
background: linear-gradient(90deg, #4361ee 0%, #3a53c0 100%); /* 渐变背景 */
color: white;
border: none;
border-radius: 6px;
font-size: 1.1rem; /* 增大字体 */
font-weight: 600; /* 加粗字体 */
cursor: pointer;
transition: all 0.3s ease;
margin-top: 1rem; /* 与上方输入框的间距 */
letter-spacing: 1px; /* 增加字间距 */
}
.btn-login:hover {
background: linear-gradient(90deg, #3a53c0 0%, #4361ee 100%);
box-shadow: 0 4px 15px rgba(67, 97, 238, 0.3);
transform: translateY(-2px);
}
.login-footer {
margin-top: 1.5rem;
font-size: 0.9rem;
}
.login-footer a {
color: #4361ee;
text-decoration: none;
transition: color 0.3s ease;
}
.login-footer a:hover {
color: #3451db;
text-decoration: underline;
}
</style>
2.来到IDEA,打开SysUserController
把代码改成下面这样:
package com.youngshu.todolist.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.youngshu.todolist.common.Result;
import com.youngshu.todolist.common.ResultCodeEnum;
import com.youngshu.todolist.pojo.SysUser;
import com.youngshu.todolist.service.SysUserService;
import com.youngshu.todolist.service.impl.SysUserServiceImpl;
import com.youngshu.todolist.utils.WebUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import com.youngshu.todolist.utils.MD5Utils;
// 该类处理用户相关的请求
@WebServlet("/user/*")
public class SysUserController extends BaseController{
private SysUserService sysUserService = new SysUserServiceImpl();
// 注册用户
protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1 接收客户端提交的json参数,并转换为User对象,获取信息
SysUser registUser = WebUtils.readJson(req, SysUser.class);
// 2 调用服务层方法,完成注册功能
//将参数放入一个SysUser对象中,在调用regist方法时传入
int rows = sysUserService.regist(registUser);
// 3 根据注册结果(成功 失败) 做页面跳转
Result result = Result.ok(null);
if (rows < 1) {
result = Result.build(null, ResultCodeEnum.USERNAME_USED);
}
WebUtils.writeJson(resp, result);
}
// 登录用户
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 接收用户名和密码
SysUser sysUser = WebUtils.readJson(req, SysUser.class);
//2 调用服务层方法,根据用户名查询用户信息
SysUser loginUser = sysUserService.findByUsername(sysUser.getUsername());
Result result = null;
if (null == loginUser) {
result = Result.build(null, ResultCodeEnum.USERNAEM_ERROR);
} else if (!MD5Utils.md5encode(sysUser.getUser_pwd()).equals(loginUser.getUser_pwd())) {
result = Result.build(null, ResultCodeEnum.PASSWORD_ERROR);
} else {
result = Result.ok(null);
}
// 3将登录结果响应给客户端
WebUtils.writeJson(resp, result);
}
// 验证用户名是否存在
protected void checkUsername(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取请求参数
String username = req.getParameter("username");
// 2. 调用服务层方法,根据用户名查询用户信息
SysUser sysUser = sysUserService.findByUsername(username);
// 3.设置响应信息
Result result =Result.ok( null);
if(null != sysUser){
result= Result.build( null, ResultCodeEnum.USERNAME_USED);
}
// 将result对象转换为JSON串响应给客户端
WebUtils.writeJson(resp,result);
}
}
3.打开CROSFilter文件
把代码改成下面这样:
package com.youngshu.todolist.filter;
import com.youngshu.todolist.pojo.SysUser;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(urlPatterns = {"/*"})
public class CROSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
// 处理预检请求,直接返回
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化方法
}
@Override
public void destroy() {
// 销毁方法
}
}
4.重启Tomcat服务器,在前端项目的登录页面中输入正确的用户名和密码,验证是否可以登录成功并跳转到待办事项页面
评论 (0)
暂无评论,来发表第一条评论吧!