系列文章:
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中配置的代码如下:
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
前端VUE项目的登录接口请求方法为POST,之前介绍过

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

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

选择第一项:

IDEA会自动生成接口代码

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


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


生成的代码

在该函数中写上下述代码
@Override public Maplogin(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,此时代码正常

按照上述方法如法炮制
在UserController中写获取token的代码和logout代码

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

代码如下
@GetMapping("/info")
public Result
接着就是Alt + Enter修复bug
在UserServiceImpl中先定义一个redisTemplate

然后在UserServiceImpl中写上下述代码
@Override public MapgetUserInfo(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查询,接下来定义它

找到UserMapper然后写上代码:
public ListgetRoleNameByUserId(Integer userId);

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

由于配置了redis,所以在启动SpringBoot之前先启动Redis
先找到redis的安装目录

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

接着启动SprinfBoot后端

然后启动Vue前端

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

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


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


防止笔记失误,附上所有代码
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
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); }
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); }
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 ServiceImplimplements 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 } }