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

Anthony_CH 2024-07-16 10:33:25 阅读 77

系列文章:

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

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

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

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

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

文章目录

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

测试后端所有代码

前后端对接

前端接口修改对接后端

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

后端总体配置

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

在这里插入图片描述

<code>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,之前介绍过

在这里插入图片描述

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

在这里插入图片描述

<code>@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会自动生成接口代码

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

生成的代码

在这里插入图片描述

在该函数中写上下述代码

<code>@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,此时代码正常

在这里插入图片描述

按照上述方法如法炮制

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

在这里插入图片描述

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

在这里插入图片描述

代码如下

<code>@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

在这里插入图片描述

然后在<code>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查询,接下来定义它

在这里插入图片描述

找到<code>UserMapper然后写上代码:

public List<String> getRoleNameByUserId(Integer userId);

在这里插入图片描述

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

在这里插入图片描述

<code><?xml version="1.0" encoding="UTF-8"?>code>

<!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">code>

<select id="getRoleNameByUserId" parameterType="Integer" resultType="String">code>

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的安装目录

在这里插入图片描述

打开<code>cmd定位到该目录

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

在这里插入图片描述

接着启动<code>SprinfBoot后端

在这里插入图片描述

然后启动<code>Vue前端

在这里插入图片描述

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

在这里插入图片描述

后端生成的<code>token也注册在redis

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

后端所有代码

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

<code>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();

}

}

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

}

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

}

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

}

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。