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

news2024/11/23 19:28:22

文章目录

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

前后端对接

前端接口修改对接后端

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

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

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

前端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,"用户名或密码错误");
}

如下图所示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;
}

返回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();

接着就是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
}

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

找到UserMapper然后写上代码:

public List<String> getRoleNameByUserId(Integer userId);

在这里插入图片描述

然后去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>

测试

由于配置了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;
    
    /**
     * <p>
     *  前端控制器
     * </p>
     *
     * @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();
        }
    }
    
    
  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;
    
    /**
     * <p>
     *  Mapper 接口
     * </p>
     *
     * @author chenhao
     * @since 2023-06-16
     */
    public interface UserMapper extends BaseMapper<User> {
    
    
        public List<String> 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;
    
    /**
     * <p>
     *  服务类
     * </p>
     *
     * @author chenhao
     * @since 2023-06-16
     */
    public interface IUserService extends IService<User> {
    
        // Map<String, Object> login(User user);
    
        Map<String, Object> getUserInfo(String token);
    
        void logout(String token);
    
        Map<String, Object> 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;
    
    /**
     * <p>
     *  服务实现类
     * </p>
     *
     * @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
        }
    
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/685567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang每日一练(leetDay0108) 灯泡开关I\II Bulb Switcher

目录 319. 灯泡开关 Bulb Switcher &#x1f31f;&#x1f31f; 672. 灯泡开关II Bulb Switcher ii &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每…

基于Java校园快递一站式服务系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

【八大排序(十)】八大排序效率与稳定性分析

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:八大排序专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习排序知识   &#x1f51d;&#x1f51d; 八大排序总结 1. 前言2. 什么是排序算法的…

基于多模态变分对抗主动学习的下游医学图像分析任务

文章目录 M-VAAL: Multimodal Variational Adversarial Active Learning for Downstream Medical Image Analysis Tasks摘要本文方法实验结果 M-VAAL: Multimodal Variational Adversarial Active Learning for Downstream Medical Image Analysis Tasks 摘要 在医学领域&…

chatgpt赋能python:Python自动执行某个软件

Python自动执行某个软件 Python是一种简单易用且非常流行的编程语言&#xff0c;常用于自动化和数据分析。如果你想自动执行某个软件&#xff0c;那么Python将是一个非常好的选择。在本文中&#xff0c;我们将介绍如何使用Python自动执行某个软件&#xff0c;以及如何做好SEO优…

第 351 场LeetCode周赛

A 美丽下标对的数目 模拟 class Solution { public:int countBeautifulPairs(vector<int> &nums) {int n nums.size();int res 0;for (int i 0; i < n; i)for (int j i 1; j < n; j)if (gcd(to_string(nums[i])[0] - 0, to_string(nums[j]).back() - 0) …

人工智能技术与GIS结合的发展

个人本是GIS专业出身&#xff0c;不知名985高校本硕。工作几年后先后积累了国土空间规划、cesium开发、地理信息数据采集、地理大数据处理&#xff0c;遥感影像处理、人工智能识别&#xff0c;做过十多个500万以上的相关项目&#xff0c;有一些浅薄的经验&#xff0c;想和大家分…

tinykv project4总结

主要目标 实现mvcc和2pc, Percolator partA 将存储分为三个独立的部分&#xff0c;lock&#xff08;管理锁记录&#xff09; default(存储数据)&#xff0c;write(提交的记录)&#xff0c;提高并行性 对于lock存储&#xff0c;只要存储一份&#xff08;因为一个行同时只能有…

chatgpt赋能python:用Python编写聊天机器人:打造AI智能助手

用Python编写聊天机器人&#xff1a;打造AI智能助手 简介 聊天机器人在现代生活中越来越受欢迎。一个好的聊天机器人能够回答我们的问题、执行任务、提供娱乐&#xff0c;甚至成为我们的朋友。Python是一种强大的编程语言&#xff0c;其模块化和易学的特性使其成为开发聊天机…

GPT-4在药物发现中的作用|景联文科技

GPT-4是一种生成式AI模型&#xff0c;可以响应文本和图像&#xff0c;它代表了生成式AI可能实现的重大进步。 药物发现的最早任务之一是检索和观察与靶蛋白结合的已知分子。这可能会导致一种基于知识的筛选方法&#xff0c;人们试图通过仅检查这些分子来进行筛选。我们让GPT-4…

【MySQL数据库的表连接语句】

目录 一、连接查询1、inner join(内连接)2、left join(左连接)3、right join(右连接) 二、CREATE VIEW三、UNION取非交集的值 五、CASE六、空值(NULL) 和 无值() 的区别 一、连接查询 A表 B表 UPDATE store_info SET store_nameWashington WHERE sales300;#修改一下表里面的…

chatgpt赋能Python-python自动化办公真的有用吗_知乎

简介 如今&#xff0c;Python作为一种必学的编程语言&#xff0c;已经走进了各行各业的办公场景。Python自动化办公也逐渐成为了一个热门话题&#xff0c;很多人开始使用Python来进行一些机械化、重复性的办公工作&#xff0c;例如数据清洗、文本处理、文件管理、自动发送邮件…

专业CPU信息检测工具:CPU-Z

今天小编为大家测试了一款轻量级的CPU处理器的测试工具&#xff0c;可以查看CPU的详细信息&#xff0c;以供各位同学们学习。 一、简单介绍 CPU-Z是一款非常流行的CPU检测软件&#xff0c;被广大用户所熟知。它是目前最受欢迎的CPU检测软件之一&#xff0c;除了Intel和AMD自带…

chatgpt赋能python:Python自动化填表:省时省力的数据录入方式

Python自动化填表&#xff1a;省时省力的数据录入方式 现代社会&#xff0c;数据填写是我们日常工作中不可避免的一项任务。但手动填写数据不仅费时费力&#xff0c;还容易出错。那么有没有一种方法可以既省时又省力呢&#xff1f;答案是有的&#xff0c;那就是Python自动化填…

Swagger与knife4j接口文档组件详解

swagger介绍 相信无论是前端还是后端开发&#xff0c;都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力&#xff0c;经常来不及更新。其实无论是前端调用后端&#xff0c;还是后端调用后端&#xff…

【数据挖掘】——常见算法对比和选择

&#x1f935;‍♂️ 个人主页&#xff1a;Lingxw_w的个人主页 ✍&#x1f3fb;作者简介&#xff1a;计算机科学与技术研究生在读 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01; &#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4…

2022(一等奖)D678基于改进结构函数法的大气气溶胶遥感反演

作品介绍 1 应用背景 大气气溶胶是大气中重要的成分之一&#xff0c;是悬浮于大气中的固体和液体微粒与它们的气体载体共同组成的多相体系&#xff0c;其尺度大约在10-3到102 μm之间。大气气溶胶的特性对空气质量具有良好的指示作用&#xff0c;气溶胶的研究对空气质量的监测…

读发布!设计与部署稳定的分布式系统(2版)笔记12_超时模式

1. “模式采用量”绝不是好的质量指标 1.1. 应该形成一种“面向恢复”的思维模式 1.2. 良好的模式能为开发工程师提供架构和设计方面的指导&#xff0c;从而减少、消除或缓解系统中的裂纹产生的影响 1.2.1. 在新发布软件后&#xff0c;它们能让你睡个安稳觉 2. 超时 2.1. …

车载网络测试 - CANCANFD - 基础篇_04

9、CAN报文包含帧格式 1&#xff09;数据帧 2&#xff09;远程帧 3&#xff09;错误帧 4&#xff09;过载帧 SOF&#xff1a;Start Of Frame&#xff0c;帧起始位&#xff1b;一个显性位&#xff0c;表明一帧的开始 RTR&#xff1a;Remote Transmission Request&#xff0c;远…

数字孪生世界建设核心能力:数字孪生高效建模能力

创建高保真的数字孪生体虚拟模型是构建数字孪生应用的重要步骤之一&#xff0c;需要真实的再现物理实体的几何图形、属性、行为和规则等。数字孪生体模型不仅要在几何结构上与物理实体保持一致&#xff0c;更重要的是要能模拟物理实体的时空状态、行为、功能等。 1、三维数字孪…