您现在的位置是:首页 > 技术教程 正文

SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接

admin 阅读: 2024-03-28
后台-插件-广告管理-内容页头部广告(手机)

系列文章:
SpringBoot + Vue前后端分离项目实战 || 一:Vue前端设计
SpringBoot + Vue前后端分离项目实战 || 二:Spring Boot后端与数据库连接
SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接
SpringBoot + Vue前后端分离项目实战 || 四:用户管理功能实现
SpringBoot + Vue前后端分离项目实战 || 五:用户管理功能后续

文章目录

    • 前后端对接
      • 前端接口修改对接后端
      • 后端总体配置
      • 后端编写登录登出业务代码
    • 测试
    • 后端所有代码

前后端对接

前端接口修改对接后端

src\api\user.js中修改请求地址,与后端保持一致
在这里插入图片描述

记录下前端的src\utils\request.js中的X-Token字段
在这里插入图片描述

改变开发环境中的请求地址,更改为后端地址http://localhost:9999
在这里插入图片描述

将前端的模拟数据服务注释关闭
在这里插入图片描述

后端总体配置

后端新建一个config包,包中新建两个类
在这里插入图片描述

  • MyCorsConfig用于配置异步访问,对接前端的访问链接"http://localhost:8888",此配置可用Nginx代替
  • MyRedisConfig用于配置Redis序列化服务

MyCorsConfig中配置的代码如下:

package com.ums.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class MyCorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration configuration = new CorsConfiguration(); // 允许什么网址来异步访问 configuration.addAllowedOrigin("http://localhost:8888"); // 获取cookie configuration.setAllowCredentials(true); // 允许什么方法? POST、GET,此处为* 意味全部允许 configuration.addAllowedMethod("*"); // 允许所有的请求头 configuration.addAllowedHeader("*"); // 设置资源过滤器,过滤什么资源 UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",configuration); return new CorsFilter(urlBasedCorsConfigurationSource); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

MyRedisConfig中用于配置的代码如下:

package com.ums.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import javax.annotation.Resource; import java.text.SimpleDateFormat; import java.util.TimeZone; @Configuration public class MyRedisConfig { @Resource private RedisConnectionFactory factory; @Bean public RedisTemplate redisTemplate(){ RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); // 设置键值序列化 redisTemplate.setKeySerializer(new StringRedisSerializer()); Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<Object>(Object.class); redisTemplate.setValueSerializer(serializer); // 序列化,死代码 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); om.setTimeZone(TimeZone.getDefault()); om.configure(MapperFeature.USE_ANNOTATIONS, false); om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); om.setSerializationInclusion(JsonInclude.Include.NON_NULL); serializer.setObjectMapper(om); return redisTemplate; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

后端编写登录登出业务代码

前端VUE项目的登录接口请求方法为POST,之前介绍过
在这里插入图片描述

在UserController中新增代码,用于登录控制
在这里插入图片描述

@PostMapping("/login") public Result<Map<String,Object>> login(@RequestBody User user){ // 因为 user传过来为json字符串,所以用@RequestBody 进行实体转换 // 业务代码在userService里完成 Map<String,Object> data = userService.login(user); if(data != null){ return Result.success(data,"登录成功"); } return Result.fail(2002,"用户名或密码错误"); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

如下图所示userService.login()方法会爆红,因为该方法没有被定义或实现,此时鼠标点击并按Alt+Enter
在这里插入图片描述

选择第一项:
在这里插入图片描述

IDEA会自动生成接口代码
在这里插入图片描述

此时,接口上方有个提示1 related problem,鼠标左击,会跳转至接口的实现代码处UserServiceImpl
在这里插入图片描述

在这里插入图片描述

在整个类的定义之处,同样Alt + Enter,选择第一个,弹出的对话框再选第一个,会生成代码
在这里插入图片描述
在这里插入图片描述

生成的代码
在这里插入图片描述

在该函数中写上下述代码

@Override public Map<String, Object> login(User user) { // 查询数据库 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getUsername, user.getUsername()); wrapper.eq(User::getPassword, user.getPassword()); User loginUser = this.baseMapper.selectOne(wrapper); // 结果不为空,生成token,将用户信息存入redis if (loginUser != null) { // 用UUID,终极方案是jwt String key = "user:" + UUID.randomUUID(); // 存入redis loginUser.setPassword(null); // 设置密码为空,密码没必要放入 redisTemplate.opsForValue().set(key, loginUser,30, TimeUnit.MINUTES); // timeout为登录时间 // 返回数据 Map<String, Object> data = new HashMap<>(); data.put("token",key); return data; } // 结果不为空,生成token,前后端分离,前端无法使用session,可以使用token // 并将用户信息存入redis return null; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

返回UserController,此时代码正常
在这里插入图片描述

按照上述方法如法炮制
在UserController中写获取token的代码和logout代码
在这里插入图片描述

其中,这两个接口均未实现
在这里插入图片描述

代码如下

@GetMapping("/info") public Result<Map<String,Object>> getUserInfo(@RequestParam("token") String token){ // @RequestParam("token") 是从url中获取值 // 根据token获取用户信息,信息存进了redis中 Map<String,Object> data = userService.getUserInfo(token); if(data != null){ return Result.success(data); } return Result.fail(2003,"登录信息无效,请重新登录"); } @PostMapping("/logout") public Result<?> logout(@RequestHeader("X-Token") String token){ userService.logout(token); return Result.success();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

接着就是Alt + Enter修复bug

在UserServiceImpl中先定义一个redisTemplate
在这里插入图片描述

然后在UserServiceImpl中写上下述代码

@Override public Map<String, Object> getUserInfo(String token) { // 之前已将对象进行序列化处理存入redis,现在从redis中取出需要反序列化处理 Object obj = redisTemplate.opsForValue().get(token); // 此对象是map类型,稍后需要序列化为Json字符串 if (obj!= null) { User loginUser = JSON.parseObject(JSON.toJSONString(obj), User.class); Map<String,Object> data = new HashMap<>(); data.put("name",loginUser.getUsername()); data.put("avatar",loginUser.getAvatar()); // 先在xml里写SQL语句id=getRoleNameByUserId,然后去UserMapper里实现接口 List<String> roleList = this.baseMapper.getRoleNameByUserId(loginUser.getId()); data.put("roles",roleList); return data; } return null; } @Override public void logout(String token) { redisTemplate.delete(token); // 从redis中删除token }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

注意红圈中的代码,是联表查询,这是自定义的SQL查询,接下来定义它
在这里插入图片描述

找到UserMapper然后写上代码:

public List<String> getRoleNameByUserId(Integer userId);
  • 1

在这里插入图片描述

然后去resources\mapper\sys\UserMapper.xml中写SQL语句
在这里插入图片描述

<?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.ums.sys.mapper.UserMapper"> <select id="getRoleNameByUserId" parameterType="Integer" resultType="String"> SELECT b.role_name FROM x_user_role a,x_role b WHERE a.role_id=b.role_id AND a.user_id = #{userId} select> mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

测试

由于配置了redis,所以在启动SpringBoot之前先启动Redis

先找到redis的安装目录
在这里插入图片描述

打开cmd定位到该目录
运行命令redis-server.exe redis.windows.conf,回车,出现下述界面,然后此窗口最小化,千万别关闭
在这里插入图片描述

接着启动SprinfBoot后端
在这里插入图片描述

然后启动Vue前端
在这里插入图片描述

点击登录后,可以看到http://localhost:9999/user/login接口地址的变化
在这里插入图片描述

后端生成的token也注册在redis中
在这里插入图片描述
在这里插入图片描述

点击退出登录,redis中的token也被注销了
在这里插入图片描述
在这里插入图片描述

后端所有代码

防止笔记失误,附上所有代码

  1. UserController中的代码package com.ums.sys.controller; import com.ums.common.vo.Result; import com.ums.sys.entity.User; import com.ums.sys.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.stereotype.Controller; import java.util.List; import java.util.Map; /** *

    * 前端控制器 *

    * * @author anthony * @since 2023-06-16 */
    @RestController @RequestMapping("/user") // @CrossOrigin //处理跨域,因为前端和后端的IP一致但端口不一致,所以浏览器认为跨域,不给访问,可用Ngx来解决 public class UserController { @Autowired private IUserService userService; @GetMapping("/all") public Result<List<User>> getAllUser() { List<User> list = userService.list(); return Result.success(list,"查询成功"); } @PostMapping("/login") public Result<Map<String,Object>> login(@RequestBody User user){ // 因为 user传过来为json字符串,所以用@RequestBody 进行实体转换 // 业务代码在userService里完成 Map<String,Object> data = userService.login(user); if(data != null){ return Result.success(data,"登录成功"); } return Result.fail(2002,"用户名或密码错误"); } @GetMapping("/info") public Result<Map<String,Object>> getUserInfo(@RequestParam("token") String token){ // @RequestParam("token") 是从url中获取值 // 根据token获取用户信息,信息存进了redis中 Map<String,Object> data = userService.getUserInfo(token); if(data != null){ return Result.success(data); } return Result.fail(2003,"登录信息无效,请重新登录"); } @PostMapping("/logout") public Result<?> logout(@RequestHeader("X-Token") String token){ userService.logout(token); return Result.success(); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
  2. mapper\UserMapper中的代码package com.ums.sys.mapper; import com.ums.sys.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import java.util.List; /** *

    * Mapper 接口 *

    * * @author chenhao * @since 2023-06-16 */
    public interface UserMapper extends BaseMapper<User> { public List<String> getRoleNameByUserId(Integer userId); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  3. service\IUserService接口中的代码package com.ums.sys.service; import com.ums.sys.entity.User; import com.baomidou.mybatisplus.extension.service.IService; import java.util.Map; /** *

    * 服务类 *

    * * @author chenhao * @since 2023-06-16 */
    public interface IUserService extends IService<User> { // Map login(User user); Map<String, Object> getUserInfo(String token); void logout(String token); Map<String, Object> login(User user); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  4. service\impl\UserServiceImpl中的代码package com.ums.sys.service.impl; import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ums.sys.entity.User; import com.ums.sys.mapper.UserMapper; import com.ums.sys.service.IUserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; /** *

    * 服务实现类 *

    * * @author chenhao * @since 2023-06-16 */
    @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { @Autowired private RedisTemplate redisTemplate; @Override public Map<String, Object> login(User user) { // 查询数据库 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getUsername, user.getUsername()); wrapper.eq(User::getPassword, user.getPassword()); User loginUser = this.baseMapper.selectOne(wrapper); // 结果不为空,生成token,将用户信息存入redis if (loginUser != null) { // 用UUID,终极方案是jwt String key = "user:" + UUID.randomUUID(); // 存入redis loginUser.setPassword(null); // 设置密码为空,密码没必要放入 redisTemplate.opsForValue().set(key, loginUser,30, TimeUnit.MINUTES); // timeout为登录时间 // 返回数据 Map<String, Object> data = new HashMap<>(); data.put("token",key); return data; } // 结果不为空,生成token,前后端分离,前端无法使用session,可以使用token // 并将用户信息存入redis return null; } @Override public Map<String, Object> getUserInfo(String token) { // 之前已将对象进行序列化处理存入redis,现在从redis中取出需要反序列化处理 Object obj = redisTemplate.opsForValue().get(token); // 此对象是map类型,稍后需要序列化为Json字符串 if (obj!= null) { User loginUser = JSON.parseObject(JSON.toJSONString(obj), User.class); Map<String,Object> data = new HashMap<>(); data.put("name",loginUser.getUsername()); data.put("avatar",loginUser.getAvatar()); // 先在xml里写SQL语句id=getRoleNameByUserId,然后去UserMapper里实现接口 List<String> roleList = this.baseMapper.getRoleNameByUserId(loginUser.getId()); data.put("roles",roleList); return data; } return null; } @Override public void logout(String token) { redisTemplate.delete(token); // 从redis中删除token } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索