SpringBoot + Vue フロントエンドとバックエンドの分離プロジェクト実戦 || 3: Spring Boot バックエンドと Vue フロントエンドの接続

一連の記事:
SpringBoot + Vue フロントエンドとバックエンドの分離プロジェクトの実践 || 1 つ: Vue フロントエンド設計
SpringBoot + Vue フロントエンドとフロントエンドの分離プロジェクトの実践 || 2 つ: Spring Boot バックエンドとデータベース接続
SpringBoot + Vue フロントエンドとフロントエンド分離プロジェクトの実践 || 3: Spring Boot バックエンドと Vue フロントエンド接続
SpringBoot + Vue フロントエンドとバックエンド分離プロジェクトの実践 || 4: ユーザー管理機能実装
SpringBoot + Vue フロントエンド分離プロジェクト実践 || 5: ユーザー管理機能フォローアップ

フロントエンドとバックエンドのドッキング

バックエンドに接続するようにフロントエンド インターフェイスを変更する

src\api\user.jsリクエストアドレスを変更してバックエンドとの一貫性を保つ
ここに画像の説明を挿入します

src\utils\request.jsフロントエンドのX-Tokenフィールドを記録する
ここに画像の説明を挿入します

開発環境のリクエストアドレスをバックエンドアドレスに変更するhttp://localhost:9999
ここに画像の説明を挿入します

フロントエンドのモックデータサービスのアノテーションをオフにする
ここに画像の説明を挿入します

全体的なバックエンド構成

バックエンドで新しいconfigパッケージを作成し、パッケージ内に 2 つの新しいクラスを作成します
ここに画像の説明を挿入します

  • MyCorsConfig非同期アクセスを構成し、フロントエンド アクセス リンクに接続するために使用され"http://localhost:8888"ます。この構成はNginx代わりに使用できます。
  • MyRedisConfigRedis シリアル化サービスの構成に使用されます

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。この時点ではコードは正常です
ここに画像の説明を挿入します

上記の方法に従い、取得したコードとコード
を記述します。UserControllertokenlogout
ここに画像の説明を挿入します

このうち、この 2 つのインターフェイスは実装されていません
ここに画像の説明を挿入します

コードは以下のように表示されます

@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それからバグを修正する時が来ました

最初に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.xmlSQL ステートメントを次のように記述します。
ここに画像の説明を挿入します

<?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、Enter キーを押すと、次のインターフェイスが表示されます。このウィンドウを最小化します。閉じないでください。
ここに画像の説明を挿入します

次にSprinfBootバックエンドを起動します
ここに画像の説明を挿入します

次にVueフロントエンドを起動します
ここに画像の説明を挿入します

クリックしてログインすると、http://localhost:9999/user/loginインターフェースアドレスの変更が確認できます。
ここに画像の説明を挿入します

バックエンドで生成されたものtokenも登録されますredis
ここに画像の説明を挿入します
ここに画像の説明を挿入します

をクリックする退出登录と、redis 内のトークンもキャンセルされます
ここに画像の説明を挿入します
ここに画像の説明を挿入します

バックエンド上のすべてのコード

メモ取りミスを防ぎ、すべてのコードを添付

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

おすすめ

転載: blog.csdn.net/qq_56039091/article/details/131329535