构建一个 Springboot3 + Vue3 的全栈项目的步骤

  1. 创建 Vue3 项目
    1
    npm create vite@latest
  2. 创建项目之后,安装一些必要的依赖, 这个icon图标很有必要安装
    进入vue项目,安装依赖
    1
    cd vue-project && npm install axios element-plus @element-plus/icons-vue
  3. 首先配置vite.config.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    server: {
    proxy: {
    '/api': {
    target: 'http://localhost:8080',
    changeOrigin: true,
    rewrite: (path) => path.replace(/^\/api/, '')
    }
    }
    }
  4. 创建api请求工具request.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import axios from 'axios'
    import { ElMessage } from 'element-plus'
    const service = axios.create({
    baseURL: '/api',
    timeout: 5000
    })

    // 请求拦截器
    service.interceptors.request.use(
    config => {
    // 在这里可以添加token等认证信息
    const token = localStorage.getItem('token')
    if (token) {
    config.headers['Authorization'] = `Bearer ${token}`
    }
    return config
    },
    error => {
    console.error(error)
    return Promise.reject(error)
    }
    )

    // 响应拦截器
    service.interceptors.response.use(
    response => {
    const res = response.data
    // 这里可以根据后端返回的状态码进行不同的处理
    if (res.code === 200) {
    return res
    } else {
    ElMessage.error(res.message || '请求失败')
    return Promise.reject(new Error(res.message || '请求失败'))
    }
    },
    error => {
    console.error('请求错误:', error)
    ElMessage.error(error.message || '请求失败')
    return Promise.reject(error)
    }
    )

    export default service
  5. 创建API接口文件:新建api包,里面新增js文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import request from '@/utils/request'

    // 用户登录
    export function login(data) {
    return request({
    url: '/user/login',
    method: 'post',
    data
    })
    }

    // 获取用户信息
    export function getUserInfo() {
    return request({
    url: '/user/info',
    method: 'get'
    })
    }

    // 用户注册
    export function register(data) {
    return request({
    url: '/user/register',
    method: 'post',
    data
    })
    }

    // 退出登录
    export function logout() {
    return request({
    url: '/user/logout',
    method: 'post'
    })
    }
  6. 在组件中使用API
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import { login } from '@/api/user'
    const handleLogin = async () => {
    if (!loginFormRef.value) return

    try {
    await loginFormRef.value.validate()
    loading.value = true

    const res = await login(loginForm)
    if (res.code === 200) {
    ElMessage.success('登录成功')
    localStorage.setItem('token', res.data.token)
    router.push('/')
    }
    } catch (error) {
    console.error('登录失败:', error)
    } finally {
    loading.value = false
    }
    }
  7. 在主应用文件main.js引入Element Plus:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import { createApp } from 'vue'
    import { createPinia } from 'pinia'
    import ElementPlus from 'element-plus'

    // 样式导入顺序很重要
    import 'element-plus/dist/index.css'

    import * as ElementPlusIconsVue from '@element-plus/icons-vue'
    import App from './App.vue'
    import router from './router'

    const app = createApp(App)

    // 注册所有图标
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
    }

    app.use(createPinia())
    app.use(router)
    app.use(ElementPlus)

    app.mount('#app')
  8. 在index.vue文件中配置路由守卫
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    import { createRouter, createWebHistory } from 'vue-router'
    import { ElMessage } from 'element-plus'

    // 路由配置
    const routes = [
    {
    path: '/login',
    name: 'Login',
    component: () => import('@/components/Login.vue'),
    meta: {
    title: '登录',
    requiresAuth: false
    }
    },
    {
    path: '/',
    name: 'Layout',
    component: () => import('@/components/Layout.vue'),
    meta: {
    requiresAuth: true
    },
    children: [
    {
    path: '',
    name: 'Home',
    component: () => import('@/views/Home.vue'),
    meta: {
    title: '首页',
    requiresAuth: true
    }
    },
    {
    path: 'profile',
    name: 'Profile',
    component: () => import('@/views/Profile.vue'),
    meta: {
    title: '个人中心',
    requiresAuth: true
    }
    }
    ]
    },
    {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('@/views/NotFound.vue'),
    meta: {
    title: '404',
    requiresAuth: false
    }
    }
    ]

    const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes
    })

    // 白名单路由
    const whiteList = ['/login']

    // 全局前置守卫
    router.beforeEach(async (to, from, next) => {
    // 设置页面标题
    document.title = to.meta.title ? `${to.meta.title} - Vue Demo` : 'Vue Demo'

    const token = localStorage.getItem('token')

    if (token) {
    if (to.path === '/login') {
    // 已登录状态下访问登录页,重定向到首页
    next({ path: '/' })
    } else {
    try {
    // 这里可以添加获取用户信息的逻辑
    // const userStore = useUserStore()
    // await userStore.getUserInfo()
    next()
    } catch (error) {
    // 获取用户信息失败,可能是token过期
    localStorage.removeItem('token')
    ElMessage.error('登录状态已过期,请重新登录')
    next(`/login?redirect=${to.path}`)
    }
    }
    } else {
    // 未登录
    if (whiteList.includes(to.path)) {
    // 白名单路由,直接访问
    next()
    } else {
    // 其他路由重定向到登录页
    next(`/login?redirect=${to.path}`)
    }
    }
    })

    // 全局后置钩子
    router.afterEach(() => {
    // 路由切换后的操作,例如关闭loading等
    // loading.value = false
    })

    export default router
  9. 配置根组件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <script setup>
    import { RouterView } from 'vue-router'
    </script>

    <template>
    <RouterView></RouterView>
    </template>

    <style scoped>

    </style>
  10. 安装依赖 启动服务器
    1
    2
    pnpm install
    pnpm dev
  11. 安装后端必要的依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
        <dependencies>
    <!--springboot 基础依赖-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- 测试依赖 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>

    <!-- 安全验证 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- web依赖 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- mybatis数据库操作 -->
    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.4</version>
    </dependency>

    <!-- mysql数据库 -->
    <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
    </dependency>

    <!-- 注解依赖 -->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>annotationProcessor</scope>
    </dependency>

    <!-- 规则校验 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

    <!-- jwt登录鉴权 -->
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
    </dependency>
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
    </dependency>
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
    </dependency>


    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter-test</artifactId>
    <version>3.0.4</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>
  12. 配置相应的包
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    ├── BackendInitConfigApplication.java      # 启动类
    ├── common/
    │ ├── Result.java
    │ └── exception/
    │ ├── BusinessException.java
    │ └── GlobalExceptionHandler.java
    ├── config/
    │ └── SecurityConfig.java
    ├── controller/
    │ └── UserController.java
    ├── mapper/
    │ └── UserMapper.java
    ├── model/
    │ ├── dto/
    │ │ ├── request/
    │ │ │ └── UserLoginRequestDTO.java
    │ │ └── response/
    │ │ └── UserLoginResponseDTO.java
    │ └── entity/
    │ └── User.java
    └── service/
    ├── impl/
    │ └── UserServiceImpl.java
    └── interfaces/
    └── UserService.java
  13. Result.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package jiejun_vuespringboot.backend_init_config.common;

    import lombok.Data;

    @Data
    public class Result<T> {
    private Integer code;
    private String message;
    private T data;

    public static <T> Result<T> success(T data) {
    Result<T> result = new Result<>();
    result.setCode(200);
    result.setMessage("操作成功");
    result.setData(data);
    return result;
    }

    public static <T> Result<T> error(String message) {
    Result<T> result = new Result<>();
    result.setCode(500);
    result.setMessage(message);
    return result;
    }

    public static <T> Result<T> error(Integer code, String message) {
    Result<T> result = new Result<>();
    result.setCode(code);
    result.setMessage(message);
    return result;
    }
    }
  14. GrobalExceptionHandler.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package jiejun_vuespringboot.backend_init_config.common.exception;

    import jiejun_vuespringboot.backend_init_config.common.Result;
    import org.springframework.validation.BindException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;

    @RestControllerAdvice
    public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusinessException(BusinessException e) {
    return Result.error(e.getMessage());
    }

    @ExceptionHandler(BindException.class)
    public Result<Void> handleBindException(BindException e) {
    return Result.error(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
    e.printStackTrace();
    return Result.error("系统错误");
    }
    }
  15. BusinessException.java
    1
    2
    3
    4
    5
    6
    7
    package jiejun_vuespringboot.backend_init_config.common.exception;

    public class BusinessException extends RuntimeException {
    public BusinessException(String message) {
    super(message);
    }
    }
  16. CorsConfig.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
        package jiejun_vuespringboot.backend_init_config.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 CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
    CorsConfiguration config = new CorsConfiguration();
    // 允许前端的域名(开发环境可以用*,生产建议写具体域名)
    config.addAllowedOriginPattern("*");
    // 允许的请求头
    config.addAllowedHeader("*");
    // 允许的请求方法
    config.addAllowedMethod("*");
    // 允许携带cookie
    config.setAllowCredentials(true);
    // 预检请求的缓存时间(秒)
    config.setMaxAge(3600L);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
    }
    }
  17. SecurityConfig.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package jiejun_vuespringboot.backend_init_config.config;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
    .csrf(csrf -> csrf.disable()) // 禁用CSRF
    .authorizeHttpRequests(auth -> auth
    .requestMatchers("/user/login").permitAll() // 允许登录接口匿名访问
    .anyRequest().authenticated() // 其他接口需要认证
    )
    .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    return http.build();
    }
    }
  18. JwtUtil.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package jiejun_vuespringboot.backend_init_config.config;

    import io.jsonwebtoken.*;
    import io.jsonwebtoken.security.Keys;
    import java.util.Date;
    import java.util.Map;
    import javax.crypto.SecretKey;

    public class JwtUtil {
    // 建议放到配置文件
    private static final String SECRET = "your-256-bit-secret-your-256-bit-secret"; // 长度要足够
    private static final long EXPIRATION = 86400000L; // 24小时

    private static SecretKey getKey() {
    return Keys.hmacShaKeyFor(SECRET.getBytes());
    }

    public static String generateToken(Map<String, Object> claims) {
    return Jwts.builder()
    .setClaims(claims)
    .setIssuedAt(new Date())
    .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
    .signWith(getKey())
    .compact();
    }

    public static Claims parseToken(String token) {
    return Jwts.parserBuilder()
    .setSigningKey(getKey())
    .build()
    .parseClaimsJws(token)
    .getBody();
    }
    }
  19. JwtAuthenticationFilter.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package jiejun_vuespringboot.backend_init_config.config;

    import io.jsonwebtoken.Claims;
    import jiejun_vuespringboot.backend_init_config.config.JwtUtil;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
    import org.springframework.util.StringUtils;
    import org.springframework.web.filter.OncePerRequestFilter;

    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Collections;

    public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    throws ServletException, IOException {
    String authHeader = request.getHeader("Authorization");
    if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
    String token = authHeader.substring(7);
    try {
    Claims claims = JwtUtil.parseToken(token);
    String username = claims.get("username", String.class);
    if (username != null) {
    UsernamePasswordAuthenticationToken authentication =
    new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList());
    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    SecurityContextHolder.getContext().setAuthentication(authentication);
    }
    } catch (Exception e) {
    // token无效,忽略,后续会被Spring Security拦截
    }
    }
    filterChain.doFilter(request, response);
    }
    }
  20. UseController.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package jiejun_vuespringboot.backend_init_config.controller;

    import jiejun_vuespringboot.backend_init_config.common.Result;
    import jiejun_vuespringboot.backend_init_config.model.dto.request.UserLoginRequestDTO;
    import jiejun_vuespringboot.backend_init_config.model.dto.response.UserLoginResponseDTO;
    import jiejun_vuespringboot.backend_init_config.service.interfaces.UserService;
    import jakarta.validation.Valid;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @RequestMapping("/user")
    public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public Result<UserLoginResponseDTO> login(@Valid @RequestBody UserLoginRequestDTO requestDTO) {
    UserLoginResponseDTO responseDTO = userService.login(requestDTO);
    return Result.success(responseDTO);
    }
    }
  21. UserLoginRequestDTO.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package jiejun_vuespringboot.backend_init_config.model.dto.request;

    import jakarta.validation.constraints.NotBlank;
    import lombok.Data;

    @Data
    public class UserLoginRequestDTO {
    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    private String password;
    }
  22. UserService.java
    1
    2
    3
    4
    5
    6
    7
    8
    package jiejun_vuespringboot.backend_init_config.service.interfaces;

    import jiejun_vuespringboot.backend_init_config.model.dto.request.UserLoginRequestDTO;
    import jiejun_vuespringboot.backend_init_config.model.dto.response.UserLoginResponseDTO;

    public interface UserService {
    UserLoginResponseDTO login(UserLoginRequestDTO requestDTO);
    }
  23. UserServiceImpl.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    package jiejun_vuespringboot.backend_init_config.service.impl;

    import jiejun_vuespringboot.backend_init_config.common.exception.BusinessException;
    import jiejun_vuespringboot.backend_init_config.mapper.UserMapper;
    import jiejun_vuespringboot.backend_init_config.model.dto.request.UserLoginRequestDTO;
    import jiejun_vuespringboot.backend_init_config.model.dto.response.UserLoginResponseDTO;
    import jiejun_vuespringboot.backend_init_config.model.entity.User;
    import jiejun_vuespringboot.backend_init_config.service.interfaces.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Service;

    import java.util.ArrayList;
    import java.util.List;

    @Service
    public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserLoginResponseDTO login(UserLoginRequestDTO requestDTO) {
    // 1. 查询用户
    User user = userMapper.findByUsername(requestDTO.getUsername());
    if (user == null) {
    throw new BusinessException("用户不存在");
    }

    // 2. 验证密码
    if (!passwordEncoder.matches(requestDTO.getPassword(), user.getPassword())) {
    throw new BusinessException("密码错误");
    }

    // 3. 检查用户状态
    if (user.getStatus() != 1) {
    throw new BusinessException("用户已被禁用");
    }

    // 4. 使用jwt,生成token
    Map<String, Object> claims = new HashMap<>();
    claims.put("username", user.getUsername());
    claims.put("userId", user.getId());
    String token = JwtUtil.generateToken(claims);

    // 5. 构建返回对象
    UserLoginResponseDTO responseDTO = new UserLoginResponseDTO();
    responseDTO.setToken(token);
    responseDTO.setUsername(user.getUsername());
    responseDTO.setPermissions(new ArrayList<>()); // 这里简化处理,实际应该查询用户权限

    return responseDTO;
    }
    }
  24. UserLoginResponseDTO.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package jiejun_vuespringboot.backend_init_config.model.dto.response;

    import lombok.Data;
    import java.util.List;

    @Data
    public class UserLoginResponseDTO {
    private String token;
    private String username;
    private List<String> permissions;
    }
  25. User.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package jiejun_vuespringboot.backend_init_config.model.entity;

    import lombok.Data;

    @Data
    public class User {
    private Long id;
    private String username;
    private String password;
    private String salt;
    private Integer status;
    private String createTime;
    private String updateTime;
    }
  26. UserMapper.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package jiejun_vuespringboot.backend_init_config.mapper;

    import jiejun_vuespringboot.backend_init_config.model.entity.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;

    @Mapper
    public interface UserMapper {
    @Select("SELECT * FROM sys_user WHERE username = #{username}")
    User findByUsername(String username);
    }