相关推荐recommended
SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接
作者:mmseoamin日期:2023-11-30

系列文章:

SpringBoot + Vue前后端分离项目实战 || 一:Vue前端设计

SpringBoot + Vue前后端分离项目实战 || 二:Spring Boot后端与数据库连接

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

SpringBoot + Vue前后端分离项目实战 || 四:用户管理功能实现

SpringBoot + Vue前后端分离项目实战 || 五:用户管理功能后续

文章目录

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

        前后端对接

        前端接口修改对接后端

        src\api\user.js中修改请求地址,与后端保持一致

        SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第1张

        记录下前端的src\utils\request.js中的X-Token字段

        SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第2张

        改变开发环境中的请求地址,更改为后端地址http://localhost:9999

        SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第3张

        将前端的模拟数据服务注释关闭

        SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第4张

        后端总体配置

        后端新建一个config包,包中新建两个类

        SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第5张

        • 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);
              }
          }
          

          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 redisTemplate = new RedisTemplate<>();
                  redisTemplate.setConnectionFactory(factory);
                  // 设置键值序列化
                  redisTemplate.setKeySerializer(new StringRedisSerializer());
                  Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(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;
              }
          }
           
          

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

          前端VUE项目的登录接口请求方法为POST,之前介绍过

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第6张

          在UserController中新增代码,用于登录控制

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第7张

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

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

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第8张

          选择第一项:

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第9张

          IDEA会自动生成接口代码

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第10张

          此时,接口上方有个提示1 related problem,鼠标左击,会跳转至接口的实现代码处UserServiceImpl

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第11张

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第12张

          在整个类的定义之处,同样Alt + Enter,选择第一个,弹出的对话框再选第一个,会生成代码

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第13张

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第14张

          生成的代码

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第15张

          在该函数中写上下述代码

          @Override
          public Map login(User user) {
              // 查询数据库
              LambdaQueryWrapper 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 data = new HashMap<>();
                  data.put("token",key);
                  return data;
              }
              // 结果不为空,生成token,前后端分离,前端无法使用session,可以使用token
              // 并将用户信息存入redis
              return null;
          }
          

          返回UserController,此时代码正常

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第16张

          按照上述方法如法炮制

          在UserController中写获取token的代码和logout代码

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第17张

          其中,这两个接口均未实现

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第18张

          代码如下

          @GetMapping("/info")
          public Result> getUserInfo(@RequestParam("token") String token){
              // @RequestParam("token") 是从url中获取值
              // 根据token获取用户信息,信息存进了redis中
              Map 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();
          

          接着就是Alt + Enter修复bug

          在UserServiceImpl中先定义一个redisTemplate

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第19张

          然后在UserServiceImpl中写上下述代码

          @Override
          public Map 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 data = new HashMap<>();
                  data.put("name",loginUser.getUsername());
                  data.put("avatar",loginUser.getAvatar());
                  // 先在xml里写SQL语句id=getRoleNameByUserId,然后去UserMapper里实现接口
                  List 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
          }
          

          注意红圈中的代码,是联表查询,这是自定义的SQL查询,接下来定义它

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第20张

          找到UserMapper然后写上代码:

          public List getRoleNameByUserId(Integer userId);
          

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第21张

          然后去resources\mapper\sys\UserMapper.xml中写SQL语句

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第22张

          
          
          
              
          
          

          测试

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

          先找到redis的安装目录

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第23张

          打开cmd定位到该目录

          运行命令redis-server.exe redis.windows.conf,回车,出现下述界面,然后此窗口最小化,千万别关闭

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第24张

          接着启动SprinfBoot后端

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第25张

          然后启动Vue前端

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第26张

          点击登录后,可以看到http://localhost:9999/user/login接口地址的变化

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第27张

          后端生成的token也注册在redis中

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第28张

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第29张

          点击退出登录,redis中的token也被注销了

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第30张

          SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接,在这里插入图片描述,第31张

          后端所有代码

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

          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> getAllUser() { List list = userService.list(); return Result.success(list,"查询成功"); } @PostMapping("/login") public Result> login(@RequestBody User user){ // 因为 user传过来为json字符串,所以用@RequestBody 进行实体转换 // 业务代码在userService里完成 Map data = userService.login(user); if(data != null){ return Result.success(data,"登录成功"); } return Result.fail(2002,"用户名或密码错误"); } @GetMapping("/info") public Result> getUserInfo(@RequestParam("token") String token){ // @RequestParam("token") 是从url中获取值 // 根据token获取用户信息,信息存进了redis中 Map 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(); } }
          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 { public List getRoleNameByUserId(Integer userId); }
          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 { // Map login(User user); Map getUserInfo(String token); void logout(String token); Map login(User user); }
          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 implements IUserService { @Autowired private RedisTemplate redisTemplate; @Override public Map login(User user) { // 查询数据库 LambdaQueryWrapper 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 data = new HashMap<>(); data.put("token",key); return data; } // 结果不为空,生成token,前后端分离,前端无法使用session,可以使用token // 并将用户信息存入redis return null; } @Override public Map 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 data = new HashMap<>(); data.put("name",loginUser.getUsername()); data.put("avatar",loginUser.getAvatar()); // 先在xml里写SQL语句id=getRoleNameByUserId,然后去UserMapper里实现接口 List 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 } }