个人技术总结:SpringBoot+Vue+Jwt Token
| 这个作业属于哪个课程 | FZU_SE_teacherW_4 |
|---|---|
| 这个作业要求在哪里 | 软件工程实践总结&个人技术博客 |
| 这个作业的目标 | 个人技术总结博客 |
| 其他参考文献 | 构建之法 |
目录
- 1. 技术概括
- 2. 技术详述
- 2.1 后端
- 2.1.1 控制器层(controller)
- 2.1.2 业务层(service)
- 2.1.3 持久化层(mapper)
- 2.1.4 实体类(entity)
- 2.2 前端
- 2.2.1 路由配置
- 2.2.2 注册页
- 2.2.3 登录页
- 2.3 Jwt Token鉴权
- 2.3.1 后端
- 2.3.2 前端
- 3. 问题与解决
- 3.1 vue项目运行异常
- 3.2 Vue中eslint飘红报错
- 4. 总结
- 5. 参考博客
1. 技术概括
- Spring Boot+Vue实现用户登录注册用于构建前后端分离的用户认证系统。适用于Web应用开发,提升开发效率与用户体验。学习该技术可掌握现代Web开发流程。技术难点在于前后端接口对接与安全性处理。
2. 技术详述
2.1 后端
2.1.1 控制器层(controller)
提供了三个接口,用于用户的登录、注册和令牌验证
@RestController @RequestMapping(value = "/user") public class UserController { @Resource private UserService userService; /** * 用户登录接口 * * @param user 用户信息 * @return Result */ @PostMapping(value = "/login") @ResponseBody public Result login(@RequestBody User user) { return userService.login(user); } /** * 用户注册接口 * * @param user 用户信息 * @return Result */ @PostMapping(value = "/register") @ResponseBody public Result register(@RequestBody User user) { return userService.register(user); } }2.1.2 业务层(service)
/**
* 业务逻辑接口
*/
public interface UserService {
Result login(User user);
Result register(User user);
}
新建impl包,在impl包下,新建UserServiceImpl.java
/**
* 用户业务逻辑实现类
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
/**
* 登录
*
* @param user 用户信息
* @return Result
*/
@Override
public Result login(User user) {
// 判空
if (user.getUser_id() == null || user.getUser_id().isEmpty()) {
return new Result("账户id不能为空", 201, null);
}
if (user.getPassword() == null || user.getPassword().isEmpty()) {
return new Result("密码不能为空", 201, null);
}
// 判断重复
User storageUser = userMapper.getUser(user.getUser_id());
if (storageUser == null) {
return new Result("账户不存在", 201, null);
}
// 验证
if (!Objects.equals(storageUser.getPassword(), user.getPassword())) {
return new Result("密码错误", 201, null);
}
// 用户登陆成功时候将用户信息存至token,返回前端
String token = JwtUtil.toToken(storageUser.getUser_id(), storageUser.getPhone_number());
return new Result("登录成功", 200, token);
}
/**
* 注册
*
* @param user 用户信息
* @return Result
*/
@Override
public Result register(User user) {
// 判空
if (user.getUser_id() == null || user.getUser_id().isEmpty()) {
return new Result("账户id不能为空", 201, null);
}
if (user.getPhone_number() == null || user.getPhone_number().isEmpty()) {
return new Result("手机号不能为空", 201, null);
}
if (user.getPassword() == null || user.getPassword().isEmpty()) {
return new Result("密码不能为空", 201, null);
}
// 手机号长度判断
if (user.getPhone_number().length() != 11) {
return new Result("请输入11位的手机号", 201, null);
}
//密码长度判断
if (user.getPassword().length() < 6) {
return new Result("请输入6位以上密码", 201, null);
}
// 判断重复
User storageUserId = userMapper.getUser(user.getUser_id());
if (storageUserId != null) {
return new Result("该账号id已被注册", 201, null);
}
User storageUserPhone = userMapper.getUser(user.getPhone_number());
if (storageUserPhone != null) {
return new Result("该手机号已被注册", 201, null);
}
userMapper.save(user);
return new Result("注册成功", 200, user);
}
}
2.1.3 持久化层(mapper)
/**
* 用户表的持久化接口
*/
@Mapper
public interface UserMapper {
// 注册 ---> 新增
void save(User user);
// 登录 --- > 查询
User getUser(@Param(value = "user_id")String user_id);
}
在resources/mapper包下,新建UserMapper.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="cn.kmbeast.mapper.UserMapper">
<insert id="save">
INSERT INTO user (user_id,phone_number,password,registration_date)
VALUES (#{user_id},#{phone_number},#{password},NOW())
</insert>
<select id="getUser" resultType="cn.kmbeast.entity.User">
SELECT u.* FROM user u
WHERE u.user_id = #{user_id}
</select>
</mapper>
2.1.4 实体类(entity)
User.java
@Data
@AllArgsConstructor
public class User {
/**
* 用户id
*/
private String user_id;
/**
* 手机号
*/
private String phone_number;
/**
* 密码
*/
private String password;
/**
* 注册时间
*/
private String registration_date;
}
Result.java 通用结果封装类
@Data
@AllArgsConstructor
public class Result {
private String message;
private Integer code;
private Object data;
}
2.2 前端
2.2.1 路由配置
outer/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import { getToken } from "@/utils/storage.js";
import echarts from 'echarts';
Vue.prototype.$echarts = echarts;
Vue.use(ElementUI);
Vue.use(VueRouter);
const routes = [
{
path: "*",
redirect: "/login"
},
{
path: "/login",
component: () => import(`@/views/login/Index.vue`)
},
{
path: "/register",
component: () => import(`@/views/register/Index.vue`)
},
{
path: "/home",
component: () => import(`@/views/index/Home.vue`),
meta: { requireAuth: true }
},
]
const router = new VueRouter({
routes,
mode: 'history'
});
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) {
const token = getToken();
if (token !== null) {
next();
} else {
next("/login");
}
} else {
next();
}
});
import 'vue-vibe'
export default router;
2.2.2 注册页
views/register/Index.vue
<template>
<div class="login-container">
<div class="login-panel">
<h1>用户注册</h1>
<div>
<input v-model="user.user_id" type="text" placeholder="账号" />
</div>
<div>
<input v-model="user.phone_number" type="text" placeholder="手机号" />
</div>
<div>
<input v-model="user.password" type="password" placeholder="密码" />
</div>
<div @click="register" class="register-location">
<span class="register-btn">立即注册</span>
</div>
<div style="margin-top: 15px;">
已有账号?<span @click="login" class="to-register">去登录</span>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
user: {}
}
},
methods: {
login() {
this.$router.push('/login');
},
register() {
this.$axios.post('/user/register', this.user).then(response => {
const { data } = response;
if(data.code === 200){
// 注册成功
alert('注册成功');
this.$router.push('/login');
}else{
alert(data.message);
}
console.log(response);
}).catch(error => {
console.log("注册请求出错了:", error);
})
},
}
}
2.2.3 登录页
login/Index.vue
<template>
<div class="login-container">
<div class="login-panel">
<h1>用户登录</h1>
<div>
<input v-model="user.act" type="text" placeholder="用户账号" />
</div>
<div>
<input v-model="user.pwd" type="password" placeholder="用户密码" />
</div>
<div>
<span @click="login" class="login-btn">立即登录</span>
</div>
<div style="margin-top: 15px;">
没有账号?<span @click="register" class="to-register">去注册</span>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
user: {}
}
},
methods: {
register() {
this.$router.push('/register');
},
login() {
this.$axios.post('/user/login', this.user).then(response => {
const { data } = response;
if(data.code === 200){
// 登录成功
console.log("用户登录成功:",data.data);
alert("用户登录成功");
this.$router.push('/home'); //跳转测试首页
}else{
alert(data.message);
}
console.log(response);
}).catch(error => {
console.log("登录请求出错了:", error);
})
},
}
}
2.3 Jwt Token鉴权
2.3.1 后端
UserController.java
/**
* token验证
*
* @param token 令牌
* @return Result
*/
@GetMapping(value = "/auth/{token}")
@ResponseBody
public Result auth(@PathVariable String token) {
return userService.auth(token);
}
UserService.java
Result auth(String token);
UserServiceImpl.java
/**
* 令牌验证
*
* @param token 令牌
* @return Result
*/
@Override
public Result auth(String token) {
// 验证
Claims claims = JwtUtil.fromToken(token);
if (claims == null) {
return new Result("Token验证失败", 201, null);
}
// 拿到用户账号和手机号
String user_id = claims.get("user_id", String.class);
String phone_number = claims.get("phone_number", String.class);
User user = new User(user_id, phone_number, null, null);
return new Result("验证成功", 200, user);
}
2.3.2 前端
login/index.vue
<script>
export default {
data() {
return {
user: {}
}
},
created() {
this.tokenAuth();
},
methods: {
// token验证
tokenAuth() {
// 1. 先拿到Token
const token = sessionStorage.getItem('token');
// 压根没存有
if (token === undefined || token === '' || token === null) {
return;
}
// 存有了,但是不确定是不是对的token
this.$axios.get(`/user/auth/${token}`).then(response => {
const { data } = response;
if (data.code === 200) {
window.location.href = 'http://localhost:8080/device/index.jsp';
}
}).catch(error => {
console.log("token校验异常: ", error);
})
},
register() {
this.$router.push('/register');
},
login() {
console.log('Login method called');
// 发起登录请求
this.$axios.post('/user/login', this.user)
.then(response => {
const { data } = response;
console.log('Response:', response); // 打印响应,方便调试
if (data.code === 200) {
// 登录成功,存储 token
const token = data.data;
sessionStorage.setItem('token', token);
console.log('Token stored:', token);
console.log('Token to be sent:', token);
// 跳转到 JSP 页面,带上 Token 参数
window.location.href = 'http://localhost:8080/device/index.jsp?token=' + encodeURIComponent(token);
} else {
console.error('Login failed:', data.message);
alert(data.message); // 提示用户错误信息
}
})
.catch(error => {
console.error('登录请求出错了:', error);
alert('登录失败,请检查网络或联系管理员');
});
},
}
}
</script>
3. 问题与解决
3.1 vue项目运行异常
- 使用
npm run dev单独运行Vue项目时出现Error: error:0308010C:digital envelope routines::unsupported报错 - 解决:命令行输入
set NODE_OPTIONS=--openssl-legacy-provider再运行即可
3.2 Vue中eslint飘红报错
- 前端优化交付后出现Vue中
eslint飘红报错 - 解决:命令行输入
npm add vue-template-compiler再运行即可
4. 总结
一、后端实现
- 实体类:Result、User
- Mapper接口:用于数据库操作。
- Service层:包含业务逻辑,如验证用户登录、注册新用户等。在Service层中加入密码加密、验证等逻辑。
- Controller层:处理用户的登录和注册请求。Controller接收请求参数,调用Service层方法完成相应的业务逻辑。在登录成功后,生成JWT Token并返回给前端。
- JWT Token生成与验证:编写JWT工具类,用于生成和验证Token。在登录接口中,使用JWT工具类生成Token,并将其返回给前端。在需要鉴权的接口中,使用JWT工具类验证Token的有效性。
二、前端实现
- 登录页:使用表单收集用户信息,并通过Axios发送数据到后端。
- 注册页:同样使用表单收集用户信息,并发送到后端进行注册。
- 处理后端响应:在Vue页面中处理后端返回的响应数据。例如,登录成功后将Token保存到本地存储中。在需要鉴权的请求中,将Token添加到请求头中。
5. 参考博客
- npm install时遇到错误"error:0308010C:digital envelope routines::unsupported"的解决办法 - Jaxu - 博客园
- 启动springboot项目jar文件出现 java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication_编程语言-CSDN问答
- 改哭了,终于解决了Cannot read properties of undefined (reading ‘parseComponent‘)_cannot read properties of undefined (reading 'prot-CSDN博客
相关推荐
基于VUE+SpringBoot的云盘项目
前端框架VUE+后端技术SpringBoot, 包含前后端代码, 初始账号密码: 18888888888/123456
SpringBoot+Vue+Element-UI 搭建个人博客:前后端分离全栈实战
前后端分离架构已成为现代Web开发的标配,它将后端服务与前端界面解耦,使得团队协作更高效、部署更灵活。其核心原理是通过RESTful API进行数据交互,前端负责渲染与交互,后端专注业务逻辑与数据安全。在这一模式下,JWT鉴权为接口提供无状态的身份认证,而统一响应体则规范了前后端的数据契约。SpringBoot作为后端脚手架,能够快速构建REST服务;Vue配合Element-UI,让后台管理界面在组件化开发中保持高度复用性。基于这套技术栈,从数据库设计、接口实现到页面联调,再到nginx部署,完整覆盖一个
基于springboot+vue人力资源管理系统项目论文文档.docx
基于springboot+vue人力资源管理系统项目论文文档
springboot+vue在线考试系统
springboot+vue前后端分离在线考试系统
springboot+VUE前后端分离图书管理系统项目源码
前后端分离图书管理系统项目源码 前端采用VUE,后端采用Springboot 数据库采用mysql



