
在现代Web应用开发中,前后端分离架构已成为主流模式。本文将详细介绍一个基于Vue.js和SpringBoot的前后端分离登录系统的设计与实现,涵盖了前端表单验证、验证码组件、后端权限处理等关键技术点。
一、前端登录页面实现
1.1 页面结构与样式
<template>
<div class="Login-container">
<div class="allClass">
<div class="titleClass"><b>欢迎登录人工智能系统</b></div>
<!-- 表单部分省略 -->
</div>
</div>
</template>
<style scoped>
.Login-container {
height: 100vh;
background-image: linear-gradient(to bottom right, deepskyblue, darkcyan);
overflow: hidden;
}
/* 其他样式省略 */
</style>
前端页面采用了Element UI组件库,整体布局简洁美观:
-
使用全屏渐变背景增强视觉效果
-
登录框居中显示,采用圆角设计
-
响应式布局适应不同屏幕尺寸
1.2 表单验证实现
data() {
return {
ruleList: {
name: [
{required: true, message: '请输入用户名即您的邮箱', trigger: 'blur'},
{min: 3, max: 60, message: '长度在3-60个字符', trigger: 'blur'}
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'},
{min: 3, max: 60, message: '长度在3-60个字符', trigger: 'blur'}
],
validCode: [
{required: true, message: '请输入验证码', trigger: 'blur'},
{min: 3, max: 4, message: '长度在3-4个字符', trigger: 'blur'}
]
}
}
}
表单验证特点:
-
采用Element UI的表单验证机制
-
对用户名、密码、验证码都设置了必填和长度验证
-
触发方式为
blur(失去焦点时验证)
1.3 验证码组件
系统集成了一个独立的验证码组件ValidCode:
<ValidCode @input="createValidCode" style="margin-left: 15px;margin-top: 5px;"/>
methods: {
createValidCode(data) {
this.validCode = data
}
}
验证码功能特点:
-
点击可刷新验证码
-
不区分大小写验证
-
通过自定义事件传递验证码值
1.4 登录逻辑处理
loginClick() {
this.$refs["userForm"].validate(valid => {
if (valid) { // 表单校验合法
// 验证码校验
if (this.user.validCode.toLowerCase() !== this.validCode.toLowerCase()) {
this.$message.error("验证码错误")
return false
}
// 发送登录请求
this.$http.post("/big/login", this.user).then(res => {
if (res.data.code === "200") {
// 存储用户信息和菜单
localStorage.setItem("user", JSON.stringify(res.data.object))
localStorage.setItem("menuList", JSON.stringify(res.data.object.menuList))
setRoutes() // 动态设置路由
// 根据角色跳转不同页面
if (res.data.object.role === "white") {
this.$router.push("/main") // 前台首页
} else {
this.$router.push("/index_home") // 后台首页
}
} else {
this.$message.error(res.data.message)
}
})
}
})
}
登录流程:
-
表单验证通过后检查验证码
-
发送登录请求到后端
-
成功后存储用户信息和菜单权限
-
动态设置路由
-
根据用户角色跳转到不同首页
二、后端登录接口实现
2.1 登录接口核心代码
@PostMapping("/login")
public Res login(@RequestBody User user) {
// 密码SHA256加密
user.setPassword(MyUtils.getSHA256StrJava(user.getPassword()));
// 查询用户
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("name", user.getName());
userQueryWrapper.eq("password", user.getPassword());
User existUser = userMapper.selectOne(userQueryWrapper);
if (existUser == null) {
return Res.error(Constants.CODE_600, "用户名或密码错误");
}
// 生成Token
String token = Token.productToken(existUser);
existUser.setToken(token);
// 处理角色权限
Role role = new Role();
role.setName(existUser.getRole());
Role existRole = roleMapper.selectOne(roleQueryWrapper);
// 构建菜单权限树
List<Integer> menuIdList = new ArrayList<>();
if (existRole.getMenuIds() != null) {
// 处理菜单ID字符串
String menuIds = existRole.getMenuIds().replace("[", "").replace("]", "");
String[] tempArray = menuIds.split(",");
for (String temp : tempArray) {
menuIdList.add(Integer.parseInt(temp));
}
}
// 补充父菜单ID
List<Integer> tempList = new ArrayList<>(menuIdList);
for (Integer menuId : menuIdList) {
Menu menu = menuMapper.selectById(menuId);
if (menu != null && menu.getFather() != null && !tempList.contains(menu.getFather())) {
tempList.add(menu.getFather());
}
}
// 构建菜单树结构
List<Menu> menuList = menuMapper.selectBatchIds(tempList);
List<Menu> fatherMenuList = menuList.stream()
.filter(temp -> temp.getFather() == null)
.collect(Collectors.toList());
for (Menu item : fatherMenuList) {
List<Menu> childrenList = menuList.stream()
.filter(temp -> item.getId().equals(temp.getFather()))
.collect(Collectors.toList());
item.setChildren(childrenList);
}
existUser.setMenuList(fatherMenuList);
return Res.success(existUser);
}
2.2 后端登录处理流程
-
密码加密处理:
-
使用SHA256算法对用户密码加密
-
加密后与数据库存储的密码比对
-
-
用户查询:
-
使用MyBatis-Plus的QueryWrapper构建查询条件
-
同时匹配用户名和密码
-
-
Token生成:
-
登录成功后生成Token
-
Token将用于后续请求的身份验证
-
-
权限处理:
-
查询用户角色对应的菜单权限
-
处理菜单ID字符串转换为列表
-
补充必要的父菜单ID
-
构建树形菜单结构
-
-
返回结果:
-
返回包含Token和菜单权限的用户信息
-
前端将使用这些信息初始化路由和界面
-
三、关键技术点解析
3.1 动态路由实现
前端在登录成功后调用setRoutes()方法动态设置路由:
setRoutes() // 动态设置路由
原理是根据后端返回的菜单权限,动态添加路由配置,实现权限控制。
3.2 密码安全策略
-
前端不存储密码明文
-
传输过程建议使用HTTPS
-
后端使用SHA256加密存储
-
数据库不存储明文密码
3.3 菜单权限树构建
后端通过以下步骤构建菜单树:
-
从角色配置中获取菜单ID列表
-
补充必要的父菜单ID
-
查询完整的菜单信息
-
构建父子关系的树形结构
List<Menu> fatherMenuList = menuList.stream()
.filter(temp -> temp.getFather() == null)
.collect(Collectors.toList());
for (Menu item : fatherMenuList) {
List<Menu> childrenList = menuList.stream()
.filter(temp -> item.getId().equals(temp.getFather()))
.collect(Collectors.toList());
item.setChildren(childrenList);
}
四、系统扩展与优化建议
-
登录安全增强:
-
添加登录失败次数限制
-
实现IP异常检测
-
考虑添加二次验证
-
-
性能优化:
-
菜单权限缓存
-
Token刷新机制
-
接口响应优化
-
-
功能扩展:
-
第三方登录集成
-
多因素认证
-
登录日志记录
-
五、总结
本文详细介绍了基于Vue和SpringBoot的前后端分离登录系统的设计与实现,涵盖了从前端表单验证到后端权限处理的完整流程。该系统具有以下特点:
-
前后端完全分离,通过API交互
-
完善的表单验证机制
-
动态路由和权限控制
-
安全的密码处理策略
-
灵活的菜单权限配置
通过这种架构,系统具有良好的扩展性和维护性,可以方便地适应各种业务需求的变化。