門戶網(wǎng)站概念網(wǎng)絡(luò)營銷推廣服務(wù)
文章目錄
- 1. 后端項目搭建
-
- 1.1 環(huán)境準(zhǔn)備
- 1.2 數(shù)據(jù)表準(zhǔn)備
- 1.3 SpringBoot3項目創(chuàng)建
- 1.4 MySql環(huán)境整合,使用druid連接池
- 1.5 整合mybatis-plus
-
- 1.5.1 引入mybatis-plus
- 1.5.2 配置代碼生成器
- 1.5.3 配置分頁插件
- 1.6 整合swagger3(knife4j)
-
- 1.6.1 整合
- 1.6.2 使用
- 1.7 數(shù)據(jù)交互處理
-
- 1.7.1 響應(yīng)數(shù)據(jù)封裝(公共返回數(shù)據(jù)類)
- 1.7.2 分頁查詢返回數(shù)據(jù)封裝
- 1.8 全局異常處理
- 1.9 整合JWT,生成token
- 1.10 封裝ThreadLocal 工具類
- 1.11 MD5封裝
- 1.12登錄驗證攔截
- 1.13 登錄和獲取當(dāng)前用戶信息接口處理
- 2. 前端項目搭建
-
- 2.1 環(huán)境準(zhǔn)備
- 2.2 創(chuàng)建Vue3項目
- 2.3 項目搭建準(zhǔn)備
- 2.4 Element Plus 安裝使用
- 2.5 axios 安裝使用
-
- 2.5.1 安裝
- 2.5.2 配置(創(chuàng)建實例,配置請求、響應(yīng)攔截器)
- 2.5.3 配置跨域
- 2.6 Vue Router 安裝使用
- 2.7 Pinia狀態(tài)管理庫
- 2.7.1 Pinia持久化插件-persist
- 2.8 搭建管理頁面基礎(chǔ)框架
-
- 2.8.1 在src下創(chuàng)建api目錄,次目錄存放請求http方法的封裝,創(chuàng)建user.js, 里邊寫封裝請求方法
- 2.8.2 登錄頁面
- 2.8.3 布局頁面
- 3.項目代碼
項目使用SpringBoot3+Vue3, 后端使用springboot3, mybatisPlus, druid,knife4j(swagger3),Jwt; 前端 vue3 , element-plus, axios, pinia, vue-router; 項目前后端分離, 可持續(xù)擴展, 代碼放到最后
1. 后端項目搭建
1.1 環(huán)境準(zhǔn)備
- spring-boot3 最低支持jdk17, 所以需要準(zhǔn)備jdk17環(huán)境
- Idea 版本IntelliJ IDEA 2021.2及以后,版本關(guān)系參考[https://blog.csdn.net/m0_62258564/article/details/134527268](https://blog.csdn.net/m0_62258564/article/details/134527268)
- maven 版本參考以上鏈接
- MySql8
1.2 數(shù)據(jù)表準(zhǔn)備
創(chuàng)建數(shù)據(jù)庫 base_manage, 并創(chuàng)建表
CREATE TABLE user(id INT NOT NULL AUTO_INCREMENT COMMENT '主鍵',login_name VARCHAR(255) NOT NULL COMMENT '登錄名(賬號)',password VARCHAR(255) NOT NULL COMMENT '密碼',name varchar(50) NOT NULL COMMENT '姓名',sex char(1) NOT NULL COMMENT '性別',phone VARCHAR(20) COMMENT '聯(lián)系電話',PRIMARY KEY(id))COMMENT '用戶信息表';
用戶表只是便于后續(xù)過程搭建操作,可根據(jù)需求修改
1.3 SpringBoot3項目創(chuàng)建
springboot版本可根據(jù)需求選擇,這里選則默認的3.2.4
耐心等待項目構(gòu)建完成, 構(gòu)建完成pom文件如下,請注意,mysql驅(qū)動包<groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId>
較之前版本有所改變
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.buzhisuoyun</groupId><artifactId>base_manage</artifactId><version>0.0.1-SNAPSHOT</version><name>base_manage</name><description>base_manage</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
1.4 MySql環(huán)境整合,使用druid連接池
添加 jdbc依賴,防止項目啟動時找不到Bean報錯
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
添加druid依賴
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-3-starter -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.20</version>
</dependency>
項目使用yml格式配置文件,修改resources下application.properties
為application.yml, 并刪除文件內(nèi)容
配置項目端口號
server:port: 8099
配置數(shù)據(jù)源和druid 連接池
spring:# 數(shù)據(jù)源datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/base_manage?serverTimezone=UTCusername: rootpassword: root
啟動項目, 查看日志,端口和druid 初始化, 整合成功
1.5 整合mybatis-plus
1.5.1 引入mybatis-plus
官網(wǎng)地址: https://baomidou.com/
1、添加依賴
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-spring-boot3-starter -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version>
</dependency>
2、配置
# mybatis-plus
mybatis-plus:configuration:# sql日志, 開發(fā)調(diào)試時開啟log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:id-type: auto
啟動項目,出現(xiàn)mybatis-plus日志
1.5.2 配置代碼生成器
1.引入相關(guān)依賴
<!-- mybatis-plus 代碼生成器-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.5</version>
</dependency><!-- mybatis-plus代碼生成器模板引擎 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency>
-
新建utis包,并在下邊創(chuàng)建MybatisPlusGenerator.java代碼生成器配置類
package com.buzhisuoyun.base_manage.utils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;import java.io.File;
import java.sql.Types;
import java.util.Collections;public class MybatisPlusGenerator {
static final String url = “jdbc:mysql://127.0.0.1:3306/base_manage?serverTimezone=UTC”; // 數(shù)據(jù)庫地址
static final String username = “root”; // 數(shù)據(jù)庫用戶名
static final String password = “root”; // 數(shù)據(jù)庫密碼
static final String authorName = “buzhisuoyun”; // 作者名
static final String parentPackageNameResource = “com/buzhisuoyun/base_manage”; // mapper.xml路徑
static final String parentPackageNameJava = “com.buzhisuoyun.base_manage”; // java 文件父包名
// 要生成代碼對應(yīng)的數(shù)據(jù)表名
static final String tableName = “user”;public static void main(String[] args) {FastAutoGenerator.create(url, username, password)// 1.全局配置.globalConfig(builder -> {builder.author(authorName) // 設(shè)置作者.enableSpringdoc() // 開啟 swagger 模式// 獲取當(dāng)前工程路徑并定位到項目java目錄下.outputDir(System.getProperty("user.dir") + "/src/main/java"); // 指定輸出目錄})// 2.數(shù)據(jù)源配置.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {int typeCode = metaInfo.getJdbcType().TYPE_CODE;if (typeCode == Types.SMALLINT) {// 自定義類型轉(zhuǎn)換return DbColumnType.INTEGER;}return typeRegistry.getColumnType(metaInfo);}))// 3.包名策略配置.packageConfig(builder -> {builder.parent(parentPackageNameJava) // 設(shè)置父包名.entity("entity").mapper("mapper").service("service").serviceImpl("service.impl").controller("controller")//.moduleName("system") // 設(shè)置父包模塊名.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/" + parentPackageNameResource + "/mapper")); // 設(shè)置mapperXml生成路徑})// 策略配置.strategyConfig(builder -> {builder.addInclude(tableName) // 設(shè)置需要生成的表名// 覆蓋已生成文件.entityBuilder().enableFileOverride().mapperBuilder().enableFileOverride().serviceBuilder().enableFileOverride().formatServiceFileName("%sService");//.addTablePrefix("t_", "c_"); // 設(shè)置過濾表前綴})// 配置模板.templateConfig(builder -> {//builder.controller(""); // 不生成controller}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默認的是Velocity引擎模板.execute(); }
}
- 修改數(shù)據(jù)庫相關(guān)信息
- 修改authorName
- 修改parentPackageNameResource, parentPackageNameJava
- 修改tableName, 要生成代碼的數(shù)據(jù)表名稱,多個表使用,分割
- .enableSpringdoc() 可以選擇,生成swagger3文檔注釋
修改完成后運行main函數(shù),生成相應(yīng)代碼,mapper.xml在resource下與java同路徑下
實體類import io.swagger.v3.oas.annotations.media.Schema;報錯,是因為swagger3依賴還未導(dǎo)入,下邊整合swagger3后就不會報錯了
1.5.3 配置分頁插件
新建config包,并在下邊創(chuàng)建配置類MybatisPlusConfig.java
package com.buzhisuoyun.base_manage.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@MapperScan("com/buzhisuoyun/base_manage/mapper")
public class MybatisPlusConfig {/*** 添加分頁插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多個插件,切記分頁最后添加//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多數(shù)據(jù)源可以不配具體類型 否則都建議配上具體的DbTypereturn interceptor;}
}
@MapperScan(“com/buzhisuoyun/base_manage/mapper”) 掃描mapper路勁,也可在啟動類配置
1.6 整合swagger3(knife4j)
1.6.1 整合
引入依賴
<!-- API 文檔 knife4j -->
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.5.0</version>
</dependency>
配置:
# springdoc-openapi 配置
springdoc:swagger-ui:path: /swagger-ui.htmltags-sorter: alphaoperations-sorter: alphaapi-docs:path: /v3/api-docsgroup-configs:- group: 'default'paths-to-match: '/**'packages-to-scan: com.buzhisuoyun.base_manage# knife4j 配置
knife4j:# 是否啟用增強enable: true# 開啟生產(chǎn)環(huán)境屏蔽production: false# 是否認證登錄basic:# basic是否開啟,默認為falseenable: trueusername: knife4jpassword: knife4jsetting:language: zh_cnenable-version: trueenable-swagger-models: true
在config下創(chuàng)建配置類
package com.buzhisuoyun.base_manage.config;import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class Knife4jConfig {@Beanpublic OpenAPI springShopOpenApi() {return new OpenAPI()// 接口文檔標(biāo)題.info(new Info().title("接口文檔").description("api接口文檔").version("1.0版本"));}
}
配置完后,啟動項目,訪問路徑http://localhost:9999/doc.html,用戶名密碼就是配置文件中的,結(jié)果如圖
1.6.2 使用
實體類使用@Schema 注解,在mybatis-plus選擇的話會自動生成
Conntroller 層使用:
@Tag(name = "用戶接口")
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;
/*** 用戶列表分頁* @param pageSize 每頁顯示的條數(shù)* @param currentPage 要查詢的頁* @param name 用戶姓名* @return Result<PageResultBean<List<User>>>*/@GetMapping("/pageList")@Operation(summary = "用戶列表分頁查詢")@Parameters({@Parameter(name = "Authorization", in = ParameterIn.HEADER, required = true, description = "token"),@Parameter(name = "pageSize", required = true, description = "每頁顯示的條數(shù)"),@Parameter(name = "currentPage", required = true, description = "要查詢的頁"),@Parameter(name = "name", description = "用戶姓名", required = false)})public Result<PageResultBean<User>> pageList(@RequestParam int pageSize, @RequestParam int currentPage, @Nullable @RequestParam String name) {IPage<User> page = userService.pageList(pageSize, currentPage, name);if (page == null) {return Result.error("查詢失敗");}//PageResultBean<User> pageResultBean = new PageResultBean<User>(page.getTotal(), page.getRecords());return Result.success(PageResultBean.getInstance(page.getTotal(), page.getRecords()));}
}
1.7 數(shù)據(jù)交互處理
1.7.1 響應(yīng)數(shù)據(jù)封裝(公共返回數(shù)據(jù)類)
封裝返回數(shù)據(jù)封裝類,放到common包下:
package com.buzhisuoyun.base_manage.common;import io.swagger.v3.oas.annotations.media.Schema;public class Result<T> {@Schema(description = "業(yè)務(wù)狀態(tài)碼 0:成功 1: 失敗")private int code; // 業(yè)務(wù)狀態(tài)碼 0:成功 1: 失敗@Schema(description = "提示信息")private String message; // 提示信息@Schema(description = "返回數(shù)據(jù)")private T data; // 響應(yīng)數(shù)據(jù)public Result(int code, String message, T data) {this.code = code;this.message = message;this.data = data;}// 操作成功返回響應(yīng)結(jié)果(帶響應(yīng)數(shù)據(jù))public static <E> Result<E> success(E data) {return new Result<>(0, "操作成功", data);}public static <E> Result<E> success() {return new Result<>(0, "操作成功", null);}public static <E> Result<E> error(String message) {return new Result<>(1, message, null);}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;}@Overridepublic String toString() {return "Result{" +"code=" + code +", message='" + message + ''' +", data=" + data +'}';}
}
1.7.2 分頁查詢返回數(shù)據(jù)封裝
封裝分頁查詢數(shù)據(jù),放到common包下:
package com.buzhisuoyun.base_manage.common;import io.swagger.v3.oas.annotations.media.Schema;import java.util.List;public class PageResultBean<T> {@Schema(description = "數(shù)據(jù)總條數(shù)")private Long total;@Schema(description = "當(dāng)前頁數(shù)據(jù)集合")private List<T> items;public PageResultBean() {}public PageResultBean(Long total, List<T> items) {this.total = total;this.items = items;}public static <E> PageResultBean<E> getInstance(Long total, List<E> items) {return new PageResultBean<>(total, items);}public Long getTotal() {return total;}public void setTotal(Long total) {this.total = total;}public List<T> getItems() {return items;}public void setItems(List<T> items) {this.items = items;}
}
1.8 全局異常處理
創(chuàng)建xception包,在包下放全局異常處理類GlobalExceptionHandler.java,類名放置 位置可以隨意,為便于分類整理, 同類包放到一個包下
使用springboot @RestControllerAdvice 注解配置
package com.buzhisuoyun.base_manage.exception;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;// 全局異常處理
@RestControllerAdvice
public class GlobalExceptionHandler {private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);@ExceptionHandler(Exception.class)public Result<String> handlerException(Exception e) {logger.warn(e.getMessage());return Result.error(StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "操作失敗");}
}
1.9 整合JWT,生成token
引入依賴
<!-- jwt --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version></dependency>
封裝工具類, 放到utils包下:
package com.buzhisuoyun.base_manage.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;import java.util.Date;
import java.util.Map;public class JwtUtil {private static final String KEY = "buzhisuoyun"; // 密鑰// 接收數(shù)據(jù),生成token并返回public static String getToken(Map<String, Object> claims) {return JWT.create().withClaim("claims", claims).withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 失效時間1小時.sign(Algorithm.HMAC256(KEY));}// 接收token,驗證并返回數(shù)據(jù)public static Map<String, Object> parseToken(String token) {return JWT.require(Algorithm.HMAC256(KEY)).build().verify(token).getClaim("claims").asMap();}
}
1.10 封裝ThreadLocal 工具類
因項目使用前后端分離,使用ThreadLocal 線程變量存儲用戶登錄信息,替代session
package com.buzhisuoyun.base_manage.utils;public class ThreadLocalUtil {// 提供ThreadLocal 對象private static final ThreadLocal<Object> THREAD_LOCAL = new ThreadLocal<>();// 獲取存儲值public static <T> T get() {return (T) THREAD_LOCAL.get();}// 存儲值public static void set(Object value) {THREAD_LOCAL.set(value);}// 清除THREAD_LOCAL 防止內(nèi)存泄漏public static void remove() {THREAD_LOCAL.remove();}
}
1.11 MD5封裝
package com.buzhisuoyun.base_manage.utils;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;public class Md5Util {/*** 默認的密碼字符串組合,用來將字節(jié)轉(zhuǎn)換成 16 進制表示的字符,apache校驗下載的文件的正確性用的就是默認的這個組合*/protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};protected static MessageDigest messagedigest = null;static {try {messagedigest = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException nsaex) {System.err.println(Md5Util.class.getName() + "初始化失敗,MessageDigest不支持MD5Util。");nsaex.printStackTrace();}}/*** 生成字符串的md5校驗值** @param s* @return*/public static String getMD5String(String s) {return getMD5String(s.getBytes());}/*** 判斷字符串的md5校驗碼是否與一個已知的md5碼相匹配** @param password 要校驗的字符串* @param md5PwdStr 已知的md5校驗碼* @return*/public static boolean checkPassword(String password, String md5PwdStr) {String s = getMD5String(password);return s.equals(md5PwdStr);}public static String getMD5String(byte[] bytes) {messagedigest.update(bytes);return bufferToHex(messagedigest.digest());}private static String bufferToHex(byte bytes[]) {return bufferToHex(bytes, 0, bytes.length);}private static String bufferToHex(byte bytes[], int m, int n) {StringBuffer stringbuffer = new StringBuffer(2 * n);int k = m + n;for (int l = m; l < k; l++) {appendHexPair(bytes[l], stringbuffer);}return stringbuffer.toString();}private static void appendHexPair(byte bt, StringBuffer stringbuffer) {char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字節(jié)中高 4 位的數(shù)字轉(zhuǎn)換, >>>// 為邏輯右移,將符號位一起右移,此處未發(fā)現(xiàn)兩種符號有何不同char c1 = hexDigits[bt & 0xf];// 取字節(jié)中低 4 位的數(shù)字轉(zhuǎn)換stringbuffer.append(c0);stringbuffer.append(c1);}// 測試public static void main(String[] args) {System.out.println(Md5Util.getMD5String("admin"));}}
1.12登錄驗證攔截
1、創(chuàng)建包interceptors,在包下配置登錄攔截器
package com.buzhisuoyun.base_manage.interceptors;import com.buzhisuoyun.base_manage.utils.JwtUtil;
import com.buzhisuoyun.base_manage.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import java.util.Map;@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 獲取tokenString token = request.getHeader("Authorization");// 驗證tokentry {Map<String, Object> claims = JwtUtil.parseToken(token);// 存儲業(yè)務(wù)信息到線程變量ThreadLocalUtil.set(claims);// 放行攔截return true;} catch (Exception e) {// 登錄信息異?;蛭吹卿?#xff0c;http響應(yīng)狀態(tài)碼為401response.setStatus(401);// 攔截請求return false;}}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清除ThreadLocal業(yè)務(wù)數(shù)據(jù)ThreadLocalUtil.remove();}
}
2、在config包下創(chuàng)建WebConfig配置類,注冊登錄攔截器
package com.buzhisuoyun.base_manage.config;import com.buzhisuoyun.base_manage.interceptors.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import java.util.ArrayList;
import java.util.List;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 配置放行資源// 無需攔截的接口集合List<String> ignorePath = new ArrayList<>();// knife4j(swagger)ignorePath.add("/swagger-resources/**");ignorePath.add("/doc.html");ignorePath.add("/v3/**");ignorePath.add("/webjars/**");ignorePath.add("/static/**");ignorePath.add("/templates/**");ignorePath.add("/error");// 登錄頁面ignorePath.add("/user/login");registry.addInterceptor(loginInterceptor).excludePathPatterns(ignorePath);}
}
1.13 登錄和獲取當(dāng)前用戶信息接口處理
@PostMapping("/login")@Operation(summary = "用戶登錄")@Parameters({@Parameter(name = "loginName", description = "登錄名", required = true, schema = @Schema(type = "sting")),@Parameter(name = "password", description = "密碼", required = true, schema = @Schema(type = "sting"))})public Result<String> login(@RequestBody User user) {if (user == null || "".equals(user.getLoginName()) || user.getLoginName() == null || "".equals(user.getPassword()) || user.getPassword() == null) {return Result.error("用戶名密碼不能為空");}// 檢驗用戶名是否存在User eruser = userService.findByLoginName(user.getLoginName());if (eruser == null) {return Result.error("用戶名不存在");}// 檢驗用戶密碼是否正確if (Md5Util.getMD5String(user.getPassword()).equals(eruser.getPassword())) {Map<String, Object> claims = new HashMap<>();claims.put("id", eruser.getId());claims.put("name", eruser.getName());claims.put("loginName", eruser.getLoginName());String token = JwtUtil.getToken(claims);return Result.success(token);}return Result.error("密碼錯誤");}/*** 獲取當(dāng)前登錄用戶信息* @return User*/@GetMapping("/currentUser")@Operation(summary = "獲取當(dāng)前登錄用戶信息")@Parameter(name = "Authorization", in = ParameterIn.HEADER, required = true, description = "token")public Result<User> getCurrentUser() {Map<String, Object> userSession = ThreadLocalUtil.get();int id = (int) userSession.get("id");User user = userService.getUserById(id);if (user != null) {return Result.success(user);}return Result.error("用戶狀態(tài)異常");}
因為是做簡單后端項目搭建,所以token存在一定問題,如在修改用戶密碼后必須強制重新登錄或采用redis緩存token,同時驗證瀏覽器token和用戶token,或采用第三方認證中心解決等
項目工程結(jié)構(gòu)如圖
2. 前端項目搭建
2.1 環(huán)境準(zhǔn)備
- node安裝
- vscode安裝
2.2 創(chuàng)建Vue3項目
在將要存放vue3項目的路徑打開cmd, 使用以下命令創(chuàng)建項目
npm init vue@latest
此時項目創(chuàng)建完成,vscode打開項目目錄,在資源目錄空白右鍵,打開終端
執(zhí)行命令 npm install
安裝依賴,等待安裝完成后執(zhí)行 npm run dev
運行項目
訪問路徑可訪問項目
在終端ctrl c 可停止運行項目
項目描述如圖,圖引自黑馬開源教程PPT
2.3 項目搭建準(zhǔn)備
說明:項目中使用組合式API
刪掉components下所有文件,刪除App.vue文件不需要的東西,最后如下所示
<script setup></script><template><router-view></router-view>
</template><style scoped></style>
2.4 Element Plus 安裝使用
-
安裝
npm install element-plus --save
-
引入:在main.js中引入Element Plus(參照官方文檔)https://element.eleme.cn/#/zh-CN/component/quickstart
import { createApp } from ‘vue’
import ElementPlus from ‘element-plus’
import ‘element-plus/dist/index.css’
import App from ‘./App.vue’const app = createApp(App)
app.use(ElementPlus)
app.mount(‘#app’) -
使用: 訪問Element官方文檔,復(fù)制組件代碼,修改調(diào)整
2.5 axios 安裝使用
2.5.1 安裝
npm install axios
2.5.2 配置(創(chuàng)建實例,配置請求、響應(yīng)攔截器)
在src目錄下新建 utils, 并在utils 下創(chuàng)建request.js 進行axios配置
// 請求配置import axios from "axios";// 定義公共前綴,創(chuàng)建請求實例
// const baseUrl = "http://localhost:8080";
const baseURL = '/api/';
const instance = axios.create({baseURL})import { ElMessage } from "element-plus"
import { useTokenStore } from "@/stores/token.js"
// 配置請求攔截器
instance.interceptors.request.use((config) => {// 請求前回調(diào)// 添加tokenconst tokenStore = useTokenStore()// 判斷有無tokenif (tokenStore.token) {config.headers.Authorization = tokenStore.token}return config},(err) => {// 請求錯誤的回調(diào)Promise.reject(err)}
)import router from "@/router";
// 添加響應(yīng)攔截器
instance.interceptors.response.use(result => {// 判斷業(yè)務(wù)狀態(tài)碼if (result.data.code === 0) {return result.data;}// 操作失敗ElMessage.error(result.data.message ? result.data.message : '服務(wù)異常')// 異步操作的狀態(tài)轉(zhuǎn)換為失敗return Promise.reject(result.data)},err => {// 判斷響應(yīng)狀態(tài)碼, 401為未登錄,提示登錄并跳轉(zhuǎn)到登錄頁面if (err.response.status === 401) {ElMessage.error('請先登錄')router.push('/login')} else {ElMessage.error('服務(wù)異常')}// 異步操作的狀態(tài)轉(zhuǎn)換為失敗return Promise.reject(err) }
)export default instance
2.5.3 配置跨域
在vite.config.js 中defineConfig配置代理,實現(xiàn)跨域
server: {proxy: {'/api': { // 獲取路徑中包含了/api的請求target: 'http://localhost:9999', // 服務(wù)端地址changeOrigin: true, // 修改源rewrite:(path) => path.replace(/^/api/, '') // api 替換為 ''}}}
2.6 Vue Router 安裝使用
-
安裝
npm install vue-router@4
-
在src/router/index.js中創(chuàng)建路由器,并導(dǎo)出
// 導(dǎo)入vue-router
import {createRouter, createWebHistory} from ‘vue-router’// 導(dǎo)入組件
import LoginVue from ‘@/views/Login.vue’
import LayoutVue from ‘@/views/Layout.vue’
import UserList from ‘@/views/user/UserList.vue’
import EditPassword from ‘@/views/user/EditPassword.vue’
import DisplayUser from ‘@/views/user/DisplayUser.vue’// 定義路由關(guān)系
const routes = [
{path: ‘/login’, component: LoginVue},
{
path: ‘/’, component: LayoutVue, redirect: ‘’, children: [
{path: ‘/user/userlist’, name: “/user/userlist”, component: UserList, meta: {
title: “用戶列表”
},},
{path: ‘/user/editpassword’, name: “/user/editpassword”, component: EditPassword, meta: {
title: “修改密碼”
}
},
{path: ‘/user/displayuser’, name: “/user/displayuser”, component: DisplayUser, meta: {
title: “個人信息”
}}
]
}
]// 創(chuàng)建路由器
const router = createRouter({
history: createWebHistory(),
routes: routes
})export default router
-
在vue應(yīng)用實例中使用vue-router
在main.js 中
import router from ‘@/router’
app.use(router) -
聲明router-view標(biāo)簽,展示組件內(nèi)容
在app.vue 中
2.7 Pinia狀態(tài)管理庫
- 安裝
npm install pinia
2.7.1 Pinia持久化插件-persist
-
安裝persist npm install pinia-persistedstate-plugin
-
在pinia中使用persist
main.js
import {createPersistedState} from’pinia-persistedstate-plugin’ const persist = createPersistedState()
pinia.use(persist) -
定義狀態(tài)Store時指定持久化配置參數(shù)
-
在src/stores/下定義token.js和userInfo.js 用來存儲token和用戶相關(guān)信息
token.js
// 定義 store
import { defineStore } from “pinia”
import {ref} from ‘vue’
/*
第一個參數(shù):名字,唯一性
第二個參數(shù):函數(shù),函數(shù)的內(nèi)部可以定義狀態(tài)的所有內(nèi)容返回值: 函數(shù)
*/
export const useTokenStore = defineStore(‘token’, () => {
// 響應(yīng)式變量
const token = ref(‘’)// 修改token值函數(shù) const setToken = (newToken) => {token.value = newToken }// 移除token值函數(shù) const removeToke = () => {token.value = '' }return {token, setToken, removeToke }
},
{
persist: true // 持久化存儲
}
)userInfo.js
import { defineStore } from “pinia”
import {ref} from ‘vue’const useUserInfoStore = defineStore(‘userInfo’, () => {
const info = ref({})const setInfo = (newInfo) => {info.value = newInfo }const removeInfo = () => {info.value = {} }return {info, setInfo, removeInfo}
},
{
persist: true
}
)export default useUserInfoStore;
-
在組件中使用store 示例
import {userLoginService} from ‘@/api/user.js’
import {useTokenStore} from ‘@/stores/token.js’
import {useRouter} from ‘vue-router’
const router = useRouter()
const tokenStore = useTokenStore();
const login = async ()=>{
// 校驗表單
if (!ruleFormRef.value) return
console.log(“校驗”)
await ruleFormRef.value.validate(async (valid) => {
if (valid) {
console.log(“校驗成功”)
// 調(diào)用接口,完成登錄
let result = await userLoginService(registerData.value);
/* if(result.code===0){
alert(result.msg? result.msg : ‘登錄成功’)
}else{
alert(‘登錄失敗’)
} */
//alert(result.msg? result.msg : ‘登錄成功’)
ElMessage.success(result.msg ? result.msg : ‘登錄成功’)
//token存儲到pinia中
tokenStore.setToken(result.data)
//跳轉(zhuǎn)到首頁 路由完成跳轉(zhuǎn)
router.push(‘/’)
} else {
console.log(“校驗失敗”)
}
})
}// 配置請求攔截器
instance.interceptors.request.use(
(config) => {
// 請求前回調(diào)
// 添加token
const tokenStore = useTokenStore()
// 判斷有無token
if (tokenStore.token) {
config.headers.Authorization = tokenStore.token
}
return config
},
(err) => {
// 請求錯誤的回調(diào)
Promise.reject(err)
}
)
2.8 搭建管理頁面基礎(chǔ)框架
2.8.1 在src下創(chuàng)建api目錄,次目錄存放請求http方法的封裝,創(chuàng)建user.js, 里邊寫封裝請求方法
import request from "@/utils/request.js"// 登錄接口調(diào)用函數(shù)
export const userLoginService = (loginData) => {return request.post('/user/login', loginData)
}// 獲取當(dāng)前登錄用戶信息
export const currentUserService = () => {return request.get('/user/currentUser')
}// 獲取所有用戶信息
export const allUserService = () => {return request.get('/user/userList')
}// 分頁查詢
export const pageListService = (pageParam) => {return request.get('/user/pageList', {params: pageParam})
}// 新增用戶
export const addUserService = (addData) => {return request.post('/user/add', addData)
}// 根據(jù)id獲取用戶信息
export const getUserById = (id) => {return request.get('/user/getuserById', {params: id})
}// 修改用戶信息
export const updateUserService = (data) => {return request.put('/user/update', data)
}// 刪除用戶
export const deleteByIdService = (id) => {console.log("deleteRequestid:", id)return request.delete('/user/delete/' + id)
}
2.8.2 登錄頁面
- 安裝
npm install sass -D
在src下創(chuàng)建views目錄,用于存放vue頁面組件
登錄 Login.vue
<script setup>
import { User, Lock } from '@element-plus/icons-vue'
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
//定義數(shù)據(jù)模型
const registerData = ref({loginName: 'admin',password:'admin',rePassword: ''
})// 定義表單組件的引用
const ruleFormRef = ref(null)//定義表單校驗規(guī)則
const rules = ref({loginName: [{ required: true, message: '請輸入用戶名', trigger: 'blur' },{ min: 5, max: 16, message: '長度為5~16位非空字符', trigger: 'blur' }],password: [{ required: true, message: '請輸入密碼', trigger: 'blur' },{ min: 5, max: 16, 2: '長度為5~16位非空字符', trigger: 'blur' }]
})//綁定數(shù)據(jù),復(fù)用注冊表單的數(shù)據(jù)模型
//表單數(shù)據(jù)校驗
//登錄函數(shù)
import {userLoginService} from '@/api/user.js'
import {useTokenStore} from '@/stores/token.js'
import {useRouter} from 'vue-router'
const router = useRouter()
const tokenStore = useTokenStore();
const login = async ()=>{// 校驗表單if (!ruleFormRef.value) returnconsole.log("校驗")await ruleFormRef.value.validate(async (valid) => {if (valid) {console.log("校驗成功")// 調(diào)用接口,完成登錄let result = await userLoginService(registerData.value);/* if(result.code===0){alert(result.msg? result.msg : '登錄成功')}else{alert('登錄失敗')} *///alert(result.msg? result.msg : '登錄成功')ElMessage.success(result.msg ? result.msg : '登錄成功')//token存儲到pinia中tokenStore.setToken(result.data)//跳轉(zhuǎn)到首頁 路由完成跳轉(zhuǎn)router.push('/')} else {console.log("校驗失敗")}})
}//定義函數(shù),清空數(shù)據(jù)模型的數(shù)據(jù)
const clearRegisterData = ()=>{registerData.value={loginName: '',password:'',rePassword:''}
}
</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 登錄表單 --><el-form ref="ruleFormRef" :model=registerData size="large" autocomplete="off" :rules="rules"><el-form-item><h1>登錄</h1></el-form-item><el-form-item prop="loginName"><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.loginName"></el-input></el-form-item><el-form-item prop="password"><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input></el-form-item><el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><!-- <el-link type="primary" :underline="false">忘記密碼?</el-link> --></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="login">登錄</el-button></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" scoped>
/* 樣式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url('@/assets/login_bg.jpg') no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
</style>
2.8.3 布局頁面
-
Layout .vue
項目結(jié)構(gòu):
3.項目代碼
https://download.csdn.net/download/qq_51355375/89085020
此項目不包含動態(tài)路由,菜單權(quán)限部分, 如需要可參考 : https://blog.csdn.net/qq_51355375/article/details/139722876