Series of articles:
SpringBoot + Vue front-end and back-end separation project practice || One: Vue front-end design
SpringBoot + Vue front-end separation project practice || Two: Spring Boot backend and database connection
SpringBoot + Vue front-end separation project practice || Three: Spring Boot backend and Vue frontend connection
SpringBoot + Vue front-end and front-end separation project practice || Four: User management function implementation
SpringBoot + Vue front-end separation project practice || Five: User management function follow-up
Article directory
Front-end and back-end docking
Modify the front-end interface to connect to the back-end
src\api\user.js
Modify the request address to keep it consistent with the backend
Record the fields src\utils\request.js
in the front endX-Token
Change the request address in the development environment to the backend addresshttp://localhost:9999
Turn off the front-end mock data service annotation
Overall backend configuration
Create a new config
package in the backend and create two new classes in the package
MyCorsConfig
Used to configure asynchronous access and connect to the front-end access link"http://localhost:8888"
. This configuration can beNginx
used instead.MyRedisConfig
Used to configure Redis serialization service
MyCorsConfig
The code configured in is as follows:
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
The code used for configuration is as follows:
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;
}
}
Write login and logout business code on the backend
The login interface request method of the front-end VUE project is as POST
introduced before
UserController
Add code in for login control
@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,"用户名或密码错误");
}
As shown in the figure below userService.login()
, the method will become popular because the method has not been defined or implemented. At this time, click the mouse and pressAlt+Enter
Select the first item:
IDEA will automatically generate interface code
At this time, there is a prompt above the interface 1 related problem
. Left-clicking the mouse will jump to the implementation code of the interface.UserServiceImpl
At the definition of the entire class, similarly Alt + Enter
, select the first one, then select the first one in the pop-up dialog box, and the code will be generated.
generated code
Write the following code in this function
@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;
}
Return UserController
, the code is normal at this time
Follow the above method and write the obtained code and code
inUserController
token
logout
Among them, these two interfaces are not implemented
code show as below
@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();
Then it’s time Alt + Enter
to fix the bug
First UserServiceImpl
define aredisTemplate
Then UserServiceImpl
write the following code in
@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
}
Note that the code in the red circle is a joint table query. This is a customized SQL
query. Next, define it
Find UserMapper
and write the code:
public List<String> getRoleNameByUserId(Integer userId);
Then go resources\mapper\sys\UserMapper.xml
and write the SQL statement in
<?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>
test
Since it is configured redis
, Redis is started before starting SpringBoot.
First find the installation directory of redis
Open cmd
the directory and
run the command redis-server.exe redis.windows.conf
, press Enter, the following interface will appear, then minimize this window, do not close it
Then start SprinfBoot
the backend
Then start Vue
the front end
After clicking to log in, you can see http://localhost:9999/user/login
the change of the interface address
The ones generated by the backend token
are also registered redis
in
Click 退出登录
, the token in redis is also canceled
All code on the backend
Prevent note-taking mistakes and attach all codes
UserController
code inpackage 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
code inpackage 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
code in interfacepackage 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
code inpackage 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 } }