SpringBoot + Vue front-end and back-end separation project actual combat || Three: Spring Boot back-end and Vue front-end connection

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

Front-end and back-end docking

Modify the front-end interface to connect to the back-end

src\api\user.jsModify the request address to keep it consistent with the backend
Insert image description here

Record the fields src\utils\request.jsin the front endX-Token
Insert image description here

Change the request address in the development environment to the backend addresshttp://localhost:9999
Insert image description here

Turn off the front-end mock data service annotation
Insert image description here

Overall backend configuration

Create a new configpackage in the backend and create two new classes in the package
Insert image description here

  • MyCorsConfigUsed to configure asynchronous access and connect to the front-end access link "http://localhost:8888". This configuration can be Nginxused instead.
  • MyRedisConfigUsed to configure Redis serialization service

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

MyRedisConfigThe 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 POSTintroduced before
Insert image description here

UserControllerAdd code in for login control
Insert image description here

@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
Insert image description here

Select the first item:
Insert image description here

IDEA will automatically generate interface code
Insert image description here

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
Insert image description here

Insert image description here

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.
Insert image description here
Insert image description here

generated code
Insert image description here

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
Insert image description here

Follow the above method and write the obtained code and code
inUserControllertokenlogout
Insert image description here

Among them, these two interfaces are not implemented
Insert image description here

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 + Enterto fix the bug

First UserServiceImpldefine aredisTemplate
Insert image description here

Then UserServiceImplwrite 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 SQLquery. Next, define it
Insert image description here

Find UserMapperand write the code:

public List<String> getRoleNameByUserId(Integer userId);

Insert image description here

Then go resources\mapper\sys\UserMapper.xmland write the SQL statement in
Insert image description here

<?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
Insert image description here

Open cmdthe 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
Insert image description here

Then start SprinfBootthe backend
Insert image description here

Then start Vuethe front end
Insert image description here

After clicking to log in, you can see http://localhost:9999/user/loginthe change of the interface address
Insert image description here

The ones generated by the backend tokenare also registered redisin
Insert image description here
Insert image description here

Click 退出登录, the token in redis is also canceled
Insert image description here
Insert image description here

All code on the backend

Prevent note-taking mistakes and attach all codes

  1. UserControllercode in
    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\UserMappercode in
    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\IUserServicecode in interface
    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\UserServiceImplcode in
    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
        }
    
    }
    

Guess you like

Origin blog.csdn.net/qq_56039091/article/details/131329535