web端-登錄頁面驗證碼的實現(springboot+vue前后端分離)超詳細

目錄

一、項目技術棧

二、實現效果圖

?三、實現路線

四、驗證碼的實現步驟

五、完整代碼

1.前端

2.后端


一、項目技術棧

登錄頁面暫時涉及到的技術棧如下:

前端 Vue2 + Element UI + Axios,后端 Spring Boot 2 + MyBatis + MySQL + JWT? +?Maven

二、實現效果圖

三、作者有話說

????????本項目驗證碼的實現只與前端有關,所有后面會先著重講前端是怎么實現的,登錄頁面前后端完整的代碼放在文章的最后。

四、驗證碼的實現步驟

? ? ? ? 本項目將驗證碼作為一個單獨的組件封裝了起來,這樣方便在注冊等其他頁面調用。

首先在components目錄下新建一個ValidCode.vue文件

ValidCode.vue

<template><!-- 驗證碼組件容器- class="ValidCode":基礎樣式類- disabled-select:禁止文本選中(防止用戶復制驗證碼)- @click="refreshCode":點擊刷新驗證碼--><div class="ValidCode disabled-select" style="width: 100%; height: 100%" @click="refreshCode"><!-- 循環渲染驗證碼字符- v-for="(item, index) in codeList":遍歷驗證碼字符數組- :key="index":循環的唯一標識- :style="getStyle(item)":動態綁定每個字符的樣式(顏色、旋轉角度等)- {{item.code}}:顯示字符內容--><span v-for="(item, index) in codeList" :key="index" :style="getStyle(item)">{{item.code}}</span></div>
</template><script>
// 導出驗證碼組件
export default {name: 'validCode', // 組件名稱data () {return {length: 4, // 驗證碼長度(默認4位)codeList: [] // 存儲驗證碼字符及樣式信息的數組}},mounted () {// 組件掛載完成后初始化生成驗證碼this.createdCode()},methods: {/*** 刷新驗證碼* 調用生成驗證碼的方法,實現點擊刷新功能*/refreshCode () {this.createdCode()},/*** 生成驗證碼的核心方法* 1. 隨機生成指定長度的字符* 2. 為每個字符生成隨機樣式(顏色、間距、旋轉角度)* 3. 觸發事件將生成的驗證碼傳遞給父組件*/createdCode () {let len = this.length, // 驗證碼長度codeList = [], // 臨時存儲驗證碼字符數組// 驗證碼字符庫(排除了易混淆的字符如I、O、l、0等)chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz0123456789',charsLen = chars.length // 字符庫長度// 循環生成指定長度的驗證碼字符for (let i = 0; i < len; i++) {// 生成隨機RGB顏色值(控制在較深范圍,保證可讀性)let rgb = [Math.round(Math.random() * 220), // R值(0-220)Math.round(Math.random() * 240), // G值(0-240)Math.round(Math.random() * 200)  // B值(0-200)]// 向數組添加一個驗證碼字符及樣式信息codeList.push({code: chars.charAt(Math.floor(Math.random() * charsLen)), // 隨機獲取字符color: `rgb(${rgb})`, // 隨機顏色padding: `${[Math.floor(Math.random() * 10)]}px`, // 隨機內邊距(0-10px)transform: `rotate(${Math.floor(Math.random() * 90) - Math.floor(Math.random() * 90)}deg)` // 隨機旋轉角度(-90到90度)})}// 將生成的驗證碼數組賦值給data中的codeListthis.codeList = codeList// 觸發自定義事件,將驗證碼字符串傳遞給父組件// 格式:將數組中每個item的code拼接成字符串this.$emit('update:value', codeList.map(item => item.code).join(''))},/*** 獲取單個驗證碼字符的樣式* @param {Object} data - 包含字符樣式信息的對象* @returns {Object} 樣式對象*/getStyle (data) {return {color: data.color, // 字符顏色fontSize: data.fontSize, // 字體大小(雖然未在生成時定義,但預留擴展)padding: data.padding, // 內邊距transform: data.transform // 旋轉角度}}}
}
</script><style>
/* 驗證碼容器樣式 */
.ValidCode{display: flex; /* 使用flex布局 */justify-content: center; /* 水平居中 */align-items: center; /* 垂直居中 */cursor: pointer; /* 鼠標懸停顯示手型,提示可點擊 */
}/* 驗證碼字符樣式 */
.ValidCode span {display: inline-block; /* 使旋轉等樣式生效 */font-size: 18px; /* 基礎字體大小 */
}/* 禁止文本選中樣式(通過user-select實現) */
.disabled-select {user-select: none; /* 標準屬性 */-webkit-user-select: none; /* Chrome/Safari/Edge */-moz-user-select: none; /* Firefox */-ms-user-select: none; /* IE/Edge */
}
</style>

???????我先將驗證碼的相關代碼部分簡單講一下,大家學會方法之后就可以按下面的步驟拿去給其他頁面用了, 最后會給大家完整的login.vue文件代碼。

? ? ? ? 前面這個ValidCode.vue文件是子組件,接下來我們要去login.vue父組件中去引入它

接下來就是將組件放在我們html模版上

然后到js了

我們的data里面需要添加兩個變量,一個是用來接收子組件傳來的驗證碼code,另一個用來存儲用戶輸入的驗證碼validCode,后面的rules也就是校驗規則需要將兩個值進行比較

那接下來我們就說說校驗規則的實現吧

在表單規則 rules 中,插入下面內容,實現當用戶輸入驗證碼或失去焦點時(trigger: 'blur'),Element UI 會自動調用 validateCode 函數進行校驗。

所以下面我們還要定義一個驗證函數,用于 Element UI 表單的驗證碼校驗邏輯。(注意這個函數是寫在data里面的不是methods)

到這還差最后一步,在login.vue里面定義getCode函數,使前面我們剛剛在父組件data中定義好的code給它賦上值

到這我們就實現了驗證碼組件的引入了

看到這有個問題我提問一下:父組件 login.vue 和子組件 ValidCode.vue 它們之間是怎么傳遞的驗證碼值呢?

????????咳咳,我也知道對于剛入門的小伙伴來說父子組件通信確實是個小難點,畢竟大家都是這么走來的,既然我這篇文章是寫給小白的,那我當然要負責到底,簡單說一下吧

首先子組件生成驗證碼之后,會通過 $emit 觸發自定義事件,將驗證碼數據傳遞給父組件。具體位置在methods對象屬性里的createdCode方法的末尾處

父組件呢,是通過 @update:value 監聽事件,并在getCode回調函數中接收數據,通過監聽該事件來接收數據。

再多說一嘴 'update:value' 這個自定義事件名稱是可以修改的,只要保證使用的時候子組件觸發的 $emit 的事件名和父組件 @事件名 監聽的名稱是完全一致的即可。

'update:value' 這種格式,其實是 Vue 中一種推薦的命名約定(用于實現 “雙向綁定” 的語法糖)

當子組件觸發 update:xxx 事件時,父組件可以簡化為 v-model:xxx 的形式監聽

<!-- 等價于 @update:value="code = $event" -->
<valid-code v-model:value="code" />

五、完整代碼

下面是本項目的登錄部分完整代碼,大家可以做適量的增刪工作

登錄頁面做了角色選擇,如果大家的表里面沒有role字段 可以將該部分內容去掉不影響整體的使用

1.前端

Login.vue

<template><div class="container"><div style="width: 400px; padding: 30px; background-color: white; border-radius: 5px; text-align: center; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);" ref="cardRef"><div style="text-align: center; font-size: 25px; margin-bottom: 30px; color: #333; font-weight: bold; position: relative; display: inline-block;" ref="titleRef">社區團購系統<div ref="lineRef" style="position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%); height: 2px; background: linear-gradient(90deg, rgba(51,51,51,0) 0%, rgba(51,51,51,0.8) 50%, rgba(51,51,51,0) 100%);"></div></div><div><el-form :model="form" :rules="rules" ref="formRef"><el-form-item prop="username"><el-input prefix-icon="el-icon-user" placeholder="請輸入賬號" v-model="form.username"></el-input></el-form-item><el-form-item prop="password"><el-input prefix-icon="el-icon-lock" placeholder="請輸入密碼" show-password  v-model="form.password"></el-input></el-form-item><el-form-item prop="validCode"><div style="display: flex;"><el-input prefix-icon="el-icon-circle-check" placeholder="請輸入驗證碼" v-model="form.validCode" size="medium" style="flex: 1;"></el-input><div style="flex:1; height:36px;"><valid-code @update:value="getCode" /> </div></div></el-form-item><el-form-item prop="role"><el-radio-group v-model="form.role"><el-radio label="ADMIN">管理員</el-radio><el-radio label="BUSINESS">商家</el-radio></el-radio-group></el-form-item><el-form-item><el-button style="width: 100%; background-color: #333; border-color: #333; color: white" @click="login">登 錄</el-button></el-form-item><div style="display: flex; align-items: center"><div style="flex: 1"></div><div style="flex: 1; text-align: right; font-size: 12px;"><a href="/register">注冊商家賬號</a></div></div></el-form></div></div></div>
</template><script>
import ValidCode from '@/components/ValidCode.vue';export default {name: "Login",components: {ValidCode},data() {// 驗證碼校驗規則const validateCode = (rule, value, callback) => {if (value === '') {callback(new Error('請輸入驗證碼'))} else if (value.toLowerCase() !== this.code) {callback(new Error('驗證碼錯誤'))} else {callback()}}return {dialogVisible: true,code: '', // 組件生成的驗證碼codeform: { username: '', password: '', validCode: '',  // 用戶輸入的驗證碼,與表單綁定一致  role: 'ADMIN' },rules: {username: [{ required: true, message: '請輸入賬號', trigger: 'blur' },{ min: 4, max: 20, message: "長度4到20個字符", trigger: 'blur'}],password: [{ required: true, message: '請輸入密碼', trigger: 'blur' },{ min: 6, max: 12, message: "長度6到12個字符", trigger: 'blur'}],validCode: [{ required: true, validator: validateCode, trigger: 'blur' }]}};},created() {// 初始化代碼},mounted() {this.$nextTick(() => {const cardWidth = this.$refs.cardRef.offsetWidth;  // 獲取卡片的寬度this.$refs.lineRef.style.width = `${cardWidth}px`;  // 將這個寬度值賦給線條元素lineRef的style.width屬性});},methods: {// 父組件的methods中:定義回調函數接收數據getCode(code) {this.code = code.toLowerCase(); // 將接收的驗證碼轉為小寫后存儲},login() {this.$refs['formRef'].validate((valid) => {if (valid) {// 驗證通過this.$request.post('/login', this.form).then(res => {if (res.code === '200') {localStorage.setItem("xm-user", JSON.stringify(res.data))  // 存儲用戶數據this.$router.push('/')  // 跳轉主頁this.$message.success('登錄成功')} else {this.$message.error(res.msg)}})}})}}
}
</script><style scoped>
.container {height: 100vh;overflow: hidden;background-image: url("@/assets/imgs/bg.jpg");background-size: 100%;display: flex;align-items: center;justify-content: center;color: #666;
}
a {color: #2a60c9;
}
</style>

其他配置:?

package.json

{"name": "vue","version": "0.1.0","private": true,"scripts": {"serve": "vue-cli-service serve","build": "vue-cli-service build"},"dependencies": {"axios": "^1.5.1","core-js": "^3.8.3","element-ui": "^2.15.14","vue": "^2.7.14","vue-router": "^3.5.1"},"devDependencies": {"@vue/cli-plugin-babel": "~5.0.0","@vue/cli-plugin-router": "~5.0.0","@vue/cli-service": "~5.0.0","vue-template-compiler": "^2.6.14"},"browserslist": ["> 1%","last 2 versions","not dead"]
}

main.js

// 導入Vue核心庫
import Vue from 'vue'
// 導入根組件App(項目的頂層組件)
import App from './App.vue'
// 導入路由配置(用于管理頁面跳轉)
import router from './router'
// 導入Element UI組件庫(提供UI組件支持)
import ElementUI from 'element-ui'
// 導入Element UI的默認樣式文件
import 'element-ui/lib/theme-chalk/index.css'
// 導入全局通用樣式(項目自定義的基礎樣式)
import '@/assets/css/global.css'
// 導入主題樣式(可能是項目自定義的主題配色)
import '@/assets/css/theme/index.css'
// 導入按鈕樣式(自定義的按鈕樣式)
import '@/assets/css/btn.css'
// 導入圖標樣式(自定義的圖標樣式)
import '@/assets/css/icon.css'
// 導入封裝好的axios請求工具(用于發送HTTP請求)
import request from "@/utils/request";// 關閉Vue生產環境提示(開發環境下會顯示一些警告,生產環境關閉以優化性能)
Vue.config.productionTip = false// 將request工具掛載到Vue原型上,使其在所有組件中可通過this.$request調用
Vue.prototype.$request = request
// 定義全局基礎URL(后端API的根地址),所有組件可通過this.$baseUrl訪問
Vue.prototype.$baseUrl = 'http://localhost:9090'// 安裝Element UI插件,并全局配置組件尺寸為"small"(統一組件大小)
Vue.use(ElementUI, {size: "small"})// 創建Vue實例
new Vue({// 注入路由配置,使整個應用支持路由功能router,// 渲染根組件App到頁面中(h是createElement函數的簡寫,用于創建虛擬DOM)render: h => h(App)// 將Vue實例掛載到頁面中id為"app"的DOM元素上(對應public/index.html中的<div id="app"></div>)
}).$mount('#app')

utils下的request.js

import axios from 'axios'
import router from "@/router";// 創建可一個新的axios對象
const request = axios.create({baseURL: 'http://localhost:9090',   // 后端的接口地址  ip:porttimeout: 30000                          // 30s請求超時
})// request 攔截器
// 可以自請求發送前對請求做一些處理
// 比如統一加token,對請求參數統一加密
request.interceptors.request.use(config => {// 只有在非文件上傳的情況下才設置Content-Typeif (!config.data || !(config.data instanceof FormData)) {config.headers['Content-Type'] = 'application/json;charset=utf-8';}// 文件上傳時讓瀏覽器自動設置multipart/form-datalet user = JSON.parse(localStorage.getItem("xm-user") || '{}')  // 獲取緩存的用戶信息config.headers['token'] = user.token  // 設置請求頭return config
}, error => {console.error('request error: ' + error) // for debugreturn Promise.reject(error)
});// response 攔截器
// 可以在接口響應后統一處理結果
request.interceptors.response.use(response => {let res = response.data;// 兼容服務端返回的字符串數據if (typeof res === 'string') {res = res ? JSON.parse(res) : res}if (res.code === '401') {router.push('/login')}return res;},error => {console.error('response error: ' + error) // for debugreturn Promise.reject(error)}
)export default request

router下的index.js

import Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)// 解決導航欄或者底部導航tabBar中的vue-router在3.0版本以上頻繁點擊菜單報錯的問題。
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {return originalPush.call(this, location).catch(err => err)
}const routes = [{ path: '/login', name: 'Login', meta: { name: '登錄' }, component: () => import('../views/Login.vue') },{ path: '/register', name: 'Register', meta: { name: '注冊' }, component: () => import('../views/Register.vue') },{ path: '*', name: 'NotFound', meta: { name: '無法訪問' }, component: () => import('../views/404.vue') },
]const router = new VueRouter({mode: 'history',base: process.env.BASE_URL,routes
})// 注:不需要前臺的項目,可以注釋掉該路由守衛
// 路由守衛
// router.beforeEach((to ,from, next) => {
//   let user = JSON.parse(localStorage.getItem("xm-user") || '{}');
//   if (to.path === '/') {
//     if (user.role) {
//       if (user.role === 'USER') {
//         next('/front/home')
//       } else {
//         next('/home')
//       }
//     } else {
//       next('/login')
//     }
//   } else {
//     next()
//   }
// })export default router

2.后端

依賴等配置

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>springboot</artifactId><version>0.0.1-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.9</version><relativePath/></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><!-- Web核心依賴,登錄頁面及接口交互基礎 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 與登錄頁面無關可不配:MinIO文件存儲相關依賴 --><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.7</version></dependency><!-- OKHttp網絡請求客戶端 --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.3</version></dependency><!-- JWT鑒權核心依賴,登錄認證必備 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.2</version></dependency><!-- Lombok依賴,簡化實體類代碼 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><scope>provided</scope></dependency><!-- 數據校驗依賴,登錄表單參數驗證必備 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!-- MySQL數據庫依賴,登錄用戶信息存儲必備 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- MyBatis持久層框架,登錄用戶數據訪問必備 --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.1</version></dependency><!-- 與登錄頁面無關可不配:分頁插件,主要用于數據列表分頁查詢 --><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.6</version><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></exclusion></exclusions></dependency><!-- Hutool工具類庫 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.18</version></dependency><!-- JWT工具依賴,登錄令牌生成與驗證 --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.3.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork></configuration></plugin></plugins></build><repositories><repository><id>public</id><name>aliyun nexus</name><url>https://maven.aliyun.com/repository/public</url><releases><enabled>true</enabled></releases></repository><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories><pluginRepositories><pluginRepository><id>public</id><name>aliyun nexus</name><url>https://maven.aliyun.com/repository/public</url><releases><enabled>true</enabled></releases></pluginRepository></pluginRepositories>
</project>

server:port: 9090# application.yml
logging:level:org.springframework.web: DEBUG  # 輸出 Spring MVC 請求細節com.example: DEBUG              # 輸出自定義代碼的日志#自定義minio配置
minio:access-key: JTfudOAovCfybwraLIh7secret-key: 794DZK90eK8W9YEc53raCWyEbinPlmfCFQjncOcUurl: http://localhost:9005 #API端口bucket-name: group-purchase-2025admin-avatar-base-path: /admin-avatar/   # 管理員頭像在桶內的基礎路徑user-avatar-base-path: /user-avatar/   # 用戶頭像在桶內的基礎路徑business-logo-base-path: /business-logo/   # 商家logo在桶內的基礎路徑business-license-base-path: /business-license/   # 商家營業執照在桶內的基礎路徑banner-base-path: /business-license/   # 輪播圖廣告在桶內的基礎路徑goods-img-base-path: /business-license/   # 商品在桶內的基礎路徑# 數據庫配置
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: root #本地的數據庫用戶名password: root #本地的數據庫密碼url: jdbc:mysql://localhost:3306/manager?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8&allowPublicKeyRetrieval=trueservlet:multipart:max-file-size: 100MBmax-request-size: 100MB# 配置mybatis實體和xml映射
mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.example.entityconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true# 分頁
pagehelper:helper-dialect: mysqlreasonable: truesupport-methods-arguments: trueparams: count=countSqlip: localhost

TokenUtils 是處理 JWT(JSON Web Token)相關操作的工具類,主要作用是簡化令牌的生成、解析、驗證等流程,是實現用戶身份認證和授權的

package com.example.utils;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.common.Constants;
import com.example.common.enums.RoleEnum;
import com.example.entity.Account;
import com.example.service.AdminService;
import com.example.service.BusinessService;
import com.example.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;/*** Token工具類*/
@Component
public class TokenUtils {private static final Logger log = LoggerFactory.getLogger(TokenUtils.class);private static AdminService staticAdminService;// private static BusinessService staticBusinessService;// private static UserService staticUserService;@ResourceAdminService adminService;// @Resource// BusinessService businessService;// @Resource// UserService userService;@PostConstructpublic void setUserService(){staticAdminService = adminService;// staticBusinessService = businessService;// staticUserService = userService;}/*** 生成token*/public static String createToken(String data, String sign) {return JWT.create().withAudience(data) // 將 userId-role 保存到 token 里面,作為載荷.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小時后token過期.sign(Algorithm.HMAC256(sign)); // 以 password 作為 token 的密鑰}/*** 獲取當前登錄的用戶信息*/public static Account getCurrentUser() {try {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String token = request.getHeader(Constants.TOKEN);if (ObjectUtil.isNotEmpty(token)) {String userRole = JWT.decode(token).getAudience().get(0);String userId = userRole.split("-")[0];  // 獲取用戶idString role = userRole.split("-")[1];    // 獲取角色if (RoleEnum.ADMIN.name().equals(role)) {return staticAdminService.selectById(Integer.valueOf(userId));} 
//                 else if (RoleEnum.BUSINESS.name().equals(role)) {
//                     return  staticBusinessService.selectBasicBusinessById(Integer.valueOf(userId));
//                } else if (RoleEnum.USER.name().equals(role)) {
//                    return staticUserService.selectById(Integer.valueOf(userId));
//                }}} catch (Exception e) {log.error("獲取當前用戶信息出錯", e);}return new Account();  // 返回空的賬號對象}
}

?跨域配置

package com.example.common.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 {/*** 創建跨域過濾器實例* @return CorsFilter 跨域過濾器對象**/@Beanpublic CorsFilter corsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration corsConfiguration = new CorsConfiguration();corsConfiguration.addAllowedOrigin("*"); // 1 設置訪問源地址,允許所有域名進行跨域調用corsConfiguration.addAllowedHeader("*"); // 2 設置訪問源請求頭,允許任何請求頭corsConfiguration.addAllowedMethod("*"); // 3 設置訪問源請求方法,允許任何方法(POST、GET等)source.registerCorsConfiguration("/**", corsConfiguration); // 4 對接口配置跨域設置,對所有接口都有效return new CorsFilter(source);}
}

JWT攔截器(雖然登錄注冊不用攔截,但是一塊給大家了,方便后續項目的展開)

package com.example.common.config;import cn.hutool.core.util.ObjectUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.common.Constants;
import com.example.common.enums.ResultCodeEnum;
import com.example.common.enums.RoleEnum;
import com.example.entity.Account;
import com.example.exception.CustomException;
import com.example.service.AdminService;
import com.example.service.BusinessService;
import com.example.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*** JWT攔截器,用于驗證HTTP請求中的JWT令牌* 在請求到達控制器前進行攔截和身份驗證*/
@Component
public class JwtInterceptor implements HandlerInterceptor {private static final Logger log = LoggerFactory.getLogger(JwtInterceptor.class);@Resourceprivate AdminService adminService;// @Resource// private BusinessService businessService;// @Resource// private UserService userService;/*** 請求處理前的攔截方法,用于JWT令牌驗證** @param request  HTTP請求對象,包含請求頭和請求參數* @param response HTTP響應對象,用于返回驗證結果* @param handler  處理請求的處理器* @return 驗證通過返回true,否則拋出異常* true: 令牌驗證通過,請求繼續處理* 拋出異常: 驗證失敗,請求終止* @throws CustomException 當令牌驗證失敗時拋出,包含具體錯誤信息* 未找到token: 拋出TOKEN_INVALID_ERROR* 解析token異常: 拋出TOKEN_CHECK_ERROR* 用戶不存在: 拋出USER_NOT_EXIST_ERROR* 簽名驗證失敗: 拋出TOKEN_CHECK_ERROR** 處理流程:* 1. 從HTTP請求的header或參數中獲取JWT令牌* 2. 解析令牌獲取用戶ID和角色信息* 3. 根據用戶ID查詢數據庫驗證用戶存在性* 4. 使用用戶密碼作為密鑰驗證令牌簽名*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 1. 從http請求的header中獲取tokenString token = request.getHeader(Constants.TOKEN);if (ObjectUtil.isEmpty(token)) {// 如果沒拿到,從參數里再拿一次token = request.getParameter(Constants.TOKEN);}// 2. 開始執行認證if (ObjectUtil.isEmpty(token)) {throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);}Account account = null;try {// 解析token獲取存儲的數據String userRole = JWT.decode(token).getAudience().get(0);String userId = userRole.split("-")[0];String role = userRole.split("-")[1];// 根據userId查詢數據庫if (RoleEnum.ADMIN.name().equals(role)) {account = adminService.selectById(Integer.valueOf(userId));} 
// else if (RoleEnum.BUSINESS.name().equals(role)) {
//                account = businessService.selectById(Integer.valueOf(userId));
//            } else if (RoleEnum.USER.name().equals(role)) {
//                account = userService.selectById(Integer.valueOf(userId));
//            }} catch (Exception e) {throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);}if (ObjectUtil.isNull(account)) {throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);}try {// 密碼加簽驗證 token  使用用戶密碼作為HMAC256算法的密鑰,需確保密碼未被修改,否則驗證失敗JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(account.getPassword())).build();jwtVerifier.verify(token); // 驗證token} catch (JWTVerificationException e) {throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);}return true;}
}

WebConfig這將登錄注冊列為了白名單,不進行攔截

package com.example.common.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;/*** 配置Web應用的MVC相關設置,包括攔截器注冊等功能。*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Resourceprivate JwtInterceptor jwtInterceptor;/*** 注冊和配置攔截器,設置JWT攔截器的路徑匹配規則。** @param registry 攔截器注冊器,用于管理和配置攔截器。* @return void 無返回值* @throws: 無顯式異常拋出,但可能拋出以下運行時異常:*          - IllegalArgumentException: 當路徑模式格式錯誤時拋出。** 處理流程:* 1. 獲取攔截器注冊器實例。* 2. 注冊JwtInterceptor攔截器。* 3. 設置攔截路徑為所有請求("/**")。* 4. 排除不需要攔截的路徑。*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor).addPathPatterns("/**").excludePathPatterns("/").excludePathPatterns("/login").excludePathPatterns("/register");}
}

定義枚舉常量

package com.example.common.enums;public enum RoleEnum {// 管理員ADMIN,// 商家BUSINESS,// 用戶USER,
}

package com.example.common.enums;public enum ResultCodeEnum {SUCCESS("200", "成功"),PARAM_ERROR("400", "參數異常"),TOKEN_INVALID_ERROR("401", "無效的token"),TOKEN_CHECK_ERROR("401", "token驗證失敗,請重新登錄"),PARAM_LOST_ERROR("4001", "參數缺失"),SYSTEM_ERROR("500", "系統異常"),USER_EXIST_ERROR("5001", "用戶名已存在"),USER_NOT_LOGIN("5002", "用戶未登錄"),USER_ACCOUNT_ERROR("5003", "賬號或密碼錯誤"),USER_NOT_EXIST_ERROR("5004", "用戶不存在"),PARAM_PASSWORD_ERROR("5005", "原密碼輸入錯誤"),NO_AUTH("5006","無權限"),PASSWORD_LENGTH_ERROR("40005", "密碼長度不能小于6位"),PASSWORD_UPPERCASE_ERROR("40006", "密碼必須包含大寫字母"),PASSWORD_DIGIT_ERROR("40007", "密碼必須包含數字"),;public String code;public String msg;ResultCodeEnum(String code, String msg) {this.code = code;this.msg = msg;}
}

package com.example.common;import io.jsonwebtoken.Claims;/*** 系統常量接口,定義應用中常用的常量值*/
public interface Constants {// 原有常量String TOKEN = "token";String USER_DEFAULT_PASSWORD = "123456";}

統一結果返回值

package com.example.common;import com.example.common.enums.ResultCodeEnum;/*** 接口統一返回結果封裝類* 用于封裝接口調用的響應結果,包含狀態碼、消息和數據*/
public class Result {// 狀態碼private String code;// 響應消息private String msg;// 響應數據private Object data;/*** 帶數據的構造方法* 用于初始化包含數據的響應結果* @param data 響應數據*/private Result(Object data) {this.data = data;}/*** 無參構造方法* 用于創建空的響應結果對象*/public Result() {}/*** 成功響應(無數據)* 返回默認的成功狀態碼和消息,不包含數據* @return 成功的響應結果對象*/public static Result success() {Result tResult = new Result();tResult.setCode(ResultCodeEnum.SUCCESS.code);tResult.setMsg(ResultCodeEnum.SUCCESS.msg);return tResult;}/*** 成功響應(帶數據)* 返回默認的成功狀態碼和消息,包含指定的數據* @param data 要返回的數據* @return 帶數據的成功響應結果對象*/public static Result success(Object data) {Result tResult = new Result(data);tResult.setCode(ResultCodeEnum.SUCCESS.code);tResult.setMsg(ResultCodeEnum.SUCCESS.msg);return tResult;}/*** 錯誤響應(默認系統錯誤)* 返回默認的系統錯誤狀態碼和消息,不包含數據* @return 系統錯誤的響應結果對象*/public static Result error() {Result tResult = new Result();tResult.setCode(ResultCodeEnum.SYSTEM_ERROR.code);tResult.setMsg(ResultCodeEnum.SYSTEM_ERROR.msg);return tResult;}/*** 錯誤響應(自定義狀態碼和消息)* 返回指定的錯誤狀態碼和消息,不包含數據* @param code 自定義錯誤狀態碼* @param msg 自定義錯誤消息* @return 自定義錯誤的響應結果對象*/public static Result error(String code, String msg) {Result tResult = new Result();tResult.setCode(code);tResult.setMsg(msg);return tResult;}/*** 錯誤響應(基于結果碼枚舉)* 根據指定的結果碼枚舉,返回對應的狀態碼和消息,不包含數據* @param resultCodeEnum 結果碼枚舉對象* @return 對應枚舉的錯誤響應結果對象*/public static Result error(ResultCodeEnum resultCodeEnum) {Result tResult = new Result();tResult.setCode(resultCodeEnum.code);tResult.setMsg(resultCodeEnum.msg);return tResult;}/*** 獲取狀態碼* @return 狀態碼字符串*/public String getCode() {return code;}/*** 設置狀態碼* @param code 要設置的狀態碼*/public void setCode(String code) {this.code = code;}/*** 獲取響應消息* @return 響應消息字符串*/public String getMsg() {return msg;}/*** 設置響應消息* @param msg 要設置的響應消息*/public void setMsg(String msg) {this.msg = msg;}/*** 獲取響應數據* @return 響應數據對象*/public Object getData() {return data;}/*** 設置響應數據* @param data 要設置的響應數據*/public void setData(Object data) {this.data = data;}
}

異常處理

業務異常處理

package com.example.exception;import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;/*** 業務異常類* 擴展了RuntimeException,主要用于實現應用啟動時的機器碼驗證邏輯* 若驗證失敗,會自動關閉應用*/
@Component
public class BusinessException extends RuntimeException {@Resourceprivate ApplicationContext context; // 應用上下文對象,用于關閉應用// 固定訂單編號,用于驗證請求private static final String orderNo = "19355229152217128961";// 驗證類型,固定為BASE_V2_CODEprivate static final String type = "BASE_V2_CODE";/*** 初始化方法,在Bean初始化后自動執行* 主要功能:獲取當前機器碼并進行驗證* 若驗證過程出現異常則靜默處理(不影響應用啟動)*/@PostConstructpublic void init() {try {String machineCode = getMachineCode(); // 獲取當前機器的唯一標識judge(machineCode); // 驗證機器碼合法性} catch (Exception e) {// 捕獲所有異常,避免初始化失敗導致應用啟動異常}}/*** 驗證機器碼合法性* @param machineCode 待驗證的機器碼* 邏輯:向遠程API發送驗證請求,根據返回結果判斷是否合法* 若不合法則調用exit()方法關閉應用*/private void judge(String machineCode) {if (StrUtil.isBlank(machineCode)) {return; // 機器碼為空時不進行驗證}try {// 構建驗證請求參數Map<String, Object> map = MapUtil.<String, Object>builder().put("machineCode", machineCode).put("orderNo", orderNo).put("type", type).build();// 發送GET請求到驗證APIHttpResponse httpResponse = HttpUtil.createGet("https://api.javaxmsz.cn/orders/sourceCodeCheck").form(map).timeout(30000) // 30秒超時.execute();int status = httpResponse.getStatus();if (status != 200) {exit(); // HTTP狀態碼非200時關閉應用return;}// 解析返回的JSON數據String code = JSONUtil.parseObj(httpResponse.body()).getStr("code");if (!"200".equals(code)) {exit(); // 業務碼非200時關閉應用}} catch (Exception e) {// 捕獲驗證過程中的異常,避免影響應用}}/*** 關閉應用的方法* 先關閉Spring應用上下文,再調用系統退出*/private void exit() {((ConfigurableApplicationContext) context).close(); // 關閉Spring容器System.exit(0); // 終止當前運行的Java虛擬機}/*** 獲取當前機器的唯一標識(機器碼)* 根據操作系統類型執行不同的命令獲取硬件信息* @return 機器碼字符串,獲取失敗返回"UNKNOWN"*/public static String getMachineCode() {try {String os = System.getProperty("os.name").toLowerCase(); // 獲取操作系統名稱String command;// 根據不同操作系統設置獲取機器碼的命令if (os.contains("win")) {command = "wmic csproduct get uuid"; // Windows系統:獲取UUID} else if (os.contains("linux")) {command = "dmidecode -s system-uuid | tr 'A-Z' 'a-z'"; // Linux系統:需要root權限} else if (os.contains("mac")) {command = "system_profiler SPHardwareDataType |grep \"r (system)\""; // Mac系統:獲取序列號} else {throw new UnsupportedOperationException("Unsupported OS"); // 不支持的操作系統}// 執行命令并獲取輸出Process process = Runtime.getRuntime().exec(command);BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));String line;StringBuilder output = new StringBuilder();while ((line = reader.readLine()) != null) {output.append(line).append("\n");}// 解析命令輸出,提取機器碼return parseSerial(output.toString(), os);} catch (Exception e) {return "UNKNOWN"; // 發生異常時返回UNKNOWN}}/*** 解析命令輸出,提取機器碼* @param output 命令執行的輸出內容* @param os 操作系統類型* @return 解析后的機器碼*/private static String parseSerial(String output, String os) {if (os.contains("win")) {// Windows系統:去除"UUID"字符和換行,取純字符串return output.replaceAll("UUID", "").replaceAll("\n", "").trim();} else if (os.contains("linux")) {// Linux系統:去除前綴,取純UUIDreturn output.replaceAll(".*ID:\\s+", "").trim();} else if (os.contains("mac")) {// Mac系統:直接返回trim后的結果return output.trim();}return "UNKNOWN";}}

自定義業務異常

package com.example.exception;import com.example.common.enums.ResultCodeEnum;/*** 自定義業務異常類* 繼承 RuntimeException,用于封裝業務處理中的異常信息,包含錯誤碼和錯誤信息*/
public class CustomException extends RuntimeException {private String code;  // 錯誤碼private String msg;   // 錯誤信息/*** 構造方法:通過結果狀態枚舉創建異常對象* @param resultCodeEnum 結果狀態枚舉,包含預設的錯誤碼和錯誤信息*/public CustomException(ResultCodeEnum resultCodeEnum) {this.code = resultCodeEnum.code;this.msg = resultCodeEnum.msg;}/*** 構造方法:通過自定義錯誤碼和錯誤信息創建異常對象* @param code 自定義錯誤碼* @param msg 自定義錯誤信息*/public CustomException(String code, String msg) {this.code = code;this.msg = msg;}/*** 獲取錯誤碼* @return 錯誤碼字符串*/public String getCode() {return code;}/*** 設置錯誤碼* @param code 新的錯誤碼*/public void setCode(String code) {this.code = code;}/*** 獲取錯誤信息* @return 錯誤信息字符串*/public String getMsg() {return msg;}/*** 設置錯誤信息* @param msg 新的錯誤信息*/public void setMsg(String msg) {this.msg = msg;}
}
全局異常處理器

package com.example.exception;import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.example.common.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;/*** 全局異常處理器* 用于統一捕獲和處理應用中拋出的異常,返回標準化的響應結果* 僅處理com.example.controller包下的控制器拋出的異常*/
@ControllerAdvice(basePackages="com.example.controller")
public class GlobalExceptionHandler {private static final Log log = LogFactory.get();/*** 統一處理所有未被捕獲的Exception類型異常* @param request HTTP請求對象* @param e 捕獲到的異常對象* @return 標準化的錯誤響應Result對象*/@ExceptionHandler(Exception.class)@ResponseBody// 標識返回JSON格式響應public Result error(HttpServletRequest request, Exception e){log.error("異常信息:",e); // 記錄異常詳細日志return Result.error(); // 返回默認的錯誤響應}/*** 專門處理自定義業務異常CustomException* @param request HTTP請求對象* @param e 捕獲到的自定義異常對象* @return 包含自定義錯誤碼和錯誤信息的響應Result對象*/@ExceptionHandler(CustomException.class)@ResponseBody// 標識返回JSON格式響應public Result customError(HttpServletRequest request, CustomException e){// 使用自定義異常中封裝的錯誤碼和信息構建響應結果return Result.error(e.getCode(), e.getMsg());}
}

entity 實體類

package com.example.entity;import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;/*** @Author: zwt* @Entity: 管理員*/
public class Admin extends Account implements Serializable {private static final long serialVersionUID = 1L;/** ID */private Integer id;/** 用戶名 */private String username;/** 密碼 */private String password;/** 姓名 */private String name;/** 電話 */@NotBlank(message = "電話不能為空")@Size(max = 11, message = "電話長度不能超過11 個字符")  // 與數據庫長度一致private String phone;/** 郵箱 */private String email;/** 頭像 */private String avatar;/** 角色標識 */private String role;@Overridepublic Integer getId() {return id;}@Overridepublic void setId(Integer id) {this.id = id;}@Overridepublic String getUsername() {return username;}@Overridepublic void setUsername(String username) {this.username = username;}@Overridepublic String getPassword() {return password;}@Overridepublic void setPassword(String password) {this.password = password;}@Overridepublic String getName() {return name;}@Overridepublic void setName(String name) {this.name = name;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}@Overridepublic String getAvatar() {return avatar;}@Overridepublic void setAvatar(String avatar) {this.avatar = avatar;}@Overridepublic String getRole() {return role;}@Overridepublic void setRole(String role) {this.role = role;}
}

controller 前端接口層

package com.example.controller;import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.example.common.Result;
import com.example.common.enums.ResultCodeEnum;
import com.example.common.enums.RoleEnum;
import com.example.entity.Account;
import com.example.service.AdminService;
import com.example.service.BusinessService;
import com.example.service.UserService;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;/*** @Author: zwt* @version 3.0 (2025-07-15)* @Description: 基礎前端接口,包含系統首頁、用戶登錄、注冊和密碼修改等功能**/
@RestController
public class WebController {@Resourceprivate AdminService adminService;@Resourceprivate BusinessService businessService;@Resourceprivate UserService userService;/*** 系統首頁訪問接口** @return Result 統一返回結果,成功時返回"訪問成功"信息*/@GetMapping("/")public Result hello() {return Result.success("訪問成功");}/*** 用戶登錄接口** @param account 包含用戶名、密碼和角色的賬戶對象* @return Result 統一返回結果,成功時返回包含token的賬戶信息* @throws: 若參數缺失返回ResultCodeEnum.PARAM_LOST_ERROR錯誤*/@PostMapping("/login")public Result login(@RequestBody Account account) {if (ObjectUtil.isEmpty(account.getUsername()) || ObjectUtil.isEmpty(account.getPassword())|| ObjectUtil.isEmpty(account.getRole())) {return Result.error(ResultCodeEnum.PARAM_LOST_ERROR);}if (RoleEnum.ADMIN.name().equals(account.getRole())) {account = adminService.login(account);}
//        else if (RoleEnum.BUSINESS.name().equals(account.getRole())) {
//            account = businessService.login(account);
//        }else if (RoleEnum.USER.name().equals(account.getRole())) {
//            account = userService.login(account);
//        }return Result.success(account);}/*** 用戶注冊接口** @param account 包含注冊信息的賬戶對象* @return Result 統一返回結果,成功時返回注冊成功信息* @throws: 若參數缺失返回ResultCodeEnum.PARAM_LOST_ERROR錯誤**/@PostMapping("/register")public Result register(@RequestBody Account account) {if (StrUtil.isBlank(account.getUsername()) || StrUtil.isBlank(account.getPassword())|| ObjectUtil.isEmpty(account.getRole())) {return Result.error(ResultCodeEnum.PARAM_LOST_ERROR);}// 密碼強度校驗
//        if (account.getPassword().length() < 6) {
//            return Result.error(ResultCodeEnum.PASSWORD_LENGTH_ERROR);
//        }
//        if (!account.getPassword().matches(".*[A-Z].*")) {
//            return Result.error(ResultCodeEnum.PASSWORD_UPPERCASE_ERROR);
//        }
//        if (!account.getPassword().matches(".*[0-9].*")) {
//            return Result.error(ResultCodeEnum.PASSWORD_DIGIT_ERROR);
//        }//        if (RoleEnum.ADMIN.name().equals(account.getRole())) {  // RoleEnum.ADMIN.name()獲取枚舉值字符串"ADMIN", 檢查注冊角色是否為ADMIN(管理員)
//            adminService.register(account);  //若是管理員,執行注冊邏輯
//        }
//        if (RoleEnum.BUSINESS.name().equals(account.getRole())) {  // //RoleEnum.ADMIN.name()獲取枚舉值字符串"ADMIN", 檢查注冊角色是否為ADMIN(管理員)
//            businessService.register(account);  //若是管理員,執行注冊邏輯//       }else if (RoleEnum.USER.name().equals(account.getRole())) {  // //RoleEnum.ADMIN.name()獲取枚舉值字符串"ADMIN", 檢查注冊角色是否為ADMIN(管理員)
//            userService.register(account);  //若是管理員,執行注冊邏輯
//        }return Result.success();}/*** 修改密碼接口** @param account 包含用戶名、原密碼和新密碼的賬戶對象* @return Result 統一返回結果,成功時返回修改成功信息* @throws: 若參數缺失返回ResultCodeEnum.PARAM_LOST_ERROR錯誤***/@PutMapping("/updatePassword")public Result updatePassword(@RequestBody Account account) {if (StrUtil.isBlank(account.getUsername()) || StrUtil.isBlank(account.getPassword())|| ObjectUtil.isEmpty(account.getNewPassword())) {return Result.error(ResultCodeEnum.PARAM_LOST_ERROR);}if (RoleEnum.ADMIN.name().equals(account.getRole())) {adminService.updatePassword(account);}
//      else if(RoleEnum.BUSINESS.name().equals(account.getRole())){
//           businessService.updatePassword(account);
//        }return Result.success();}
}

service服務層

package com.example.service;import cn.hutool.core.util.ObjectUtil;
import com.example.common.Constants;
import com.example.common.enums.ResultCodeEnum;
import com.example.common.enums.RoleEnum;
import com.example.entity.Account;
import com.example.entity.Admin;
import com.example.exception.CustomException;
import com.example.mapper.AdminMapper;
import com.example.utils.FileCleanupUtils;
import com.example.utils.TokenUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;/*** @Author: zwt* @version 1.0 (2025-06-20)* @Description: 管理員業務處理*/
@Service
public class AdminService {@Resourceprivate AdminMapper adminMapper;@Resourceprivate FileCleanupUtils fileCleanupUtils;/*** 管理員登錄** @param account 包含用戶名和密碼的賬戶信息* @return 登錄成功的賬戶信息,包含生成的token* @throws CustomException 當用戶不存在時拋出USER_NOT_EXIST_ERROR,密碼錯誤時拋出USER_ACCOUNT_ERROR*/public Account login(Account account) {Account dbAdmin = adminMapper.selectByUsername(account.getUsername());  // 根據用戶名查詢用戶if (ObjectUtil.isNull(dbAdmin)) {  // 驗證用戶是否存在throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);}if (!account.getPassword().equals(dbAdmin.getPassword())) {  // 驗證密碼是否正確throw new CustomException(ResultCodeEnum.USER_ACCOUNT_ERROR);}// 生成JWT tokenString tokenData = dbAdmin.getId() + "-" + RoleEnum.ADMIN.name();  // 將用戶ID和角色類型拼接為令牌的載荷(Payload)數據String token = TokenUtils.createToken(tokenData, dbAdmin.getPassword());  // 調用工具類生成JWT令牌 ,密碼作為HMAC簽名算法的密鑰dbAdmin.setToken(token);  // 將生成的令牌設置到用戶對象中return dbAdmin;}/*** 管理員注冊** @param account 包含注冊信息的賬戶對象*/public void register(Account account) {Admin admin = new Admin();BeanUtils.copyProperties(account, admin);  // 將Account對象屬性復制到Admin對象add(admin);  // 調用add方法完成注冊}/*** 新增管理員** @param admin 管理員實體,包含要新增的管理員信息* @throws CustomException 當用戶名已存在時拋出USER_EXIST_ERROR*/public void add(Admin admin) {Admin dbAdmin = adminMapper.selectByUsername(admin.getUsername());if (ObjectUtil.isNotNull(dbAdmin)) {  // 檢查用戶名是否已存在throw new CustomException(ResultCodeEnum.USER_EXIST_ERROR);  // 若用戶名已存在,拋出USER_EXIST_ERROR(5001)}if (ObjectUtil.isEmpty(admin.getPassword())) {admin.setPassword(Constants.USER_DEFAULT_PASSWORD);  // 設置默認密碼}if (ObjectUtil.isEmpty(admin.getName())) {admin.setName(admin.getUsername());  // 設置默認名稱}admin.setRole(RoleEnum.ADMIN.name());  // 設置管理員角色adminMapper.insert(admin);  // 插入新管理員記錄}}

Mapper 數據訪問層

package com.example.mapper;import com.example.entity.Admin;
import org.apache.ibatis.annotations.Select;import java.util.List;/*** @Author: zwt* @version 1.0 (2025-06-20)* @Description: 管理員相關數據接口*/
public interface AdminMapper {/*** 插入新管理員記錄** @param admin 管理員實體對象,包含要插入的管理員信息* @return 插入操作影響的記錄數*/int insert(Admin admin);/*** 根據用戶名查詢管理員** @param username 要查詢的用戶名* @return 匹配的管理員實體對象,若不存在則返回null*/@Select("select * from admin where username = #{username}")Admin selectByUsername(String username);
}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.AdminMapper"><sql id="Base_Column_List">id,username,password,name,phone,email,avatar,role</sql><insert id="insert" parameterType="com.example.entity.Admin" useGeneratedKeys="true">insert into admin<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="username != null">username,</if><if test="password != null">password,</if><if test="name != null">name,</if><if test="phone != null">phone,</if><if test="email != null">email,</if><if test="avatar != null">avatar,</if><if test="role != null">role,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id},</if><if test="username != null">#{username},</if><if test="password != null">#{password},</if><if test="name != null">#{name},</if><if test="phone != null">#{phone},</if><if test="email != null">#{email},</if><if test="avatar != null">#{avatar},</if><if test="role != null">#{role},</if></trim></insert></mapper>

到這登錄頁面就可以正常運行了,快去試試吧

每天進步一點點,加油 ! ! !?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/92336.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/92336.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/92336.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

瘋狂星期四文案網第33天運營日記

網站運營第33天&#xff0c;點擊觀站&#xff1a; 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 今日訪問量 今日搜索引擎收錄情況 必應收錄239個頁面&#xff0c;還在持續增加中&#xff0c;已經獲得必應的認可&#xff0c;逐漸收錄所有頁面 百度…

客戶端利用MinIO對服務器數據進行同步

MinIO 是一款高性能、開源的對象存儲服務&#xff0c;專為海量數據存儲設計&#xff0c;兼容 Amazon S3 API&#xff08;即與 AWS S3 協議兼容&#xff09;&#xff0c;可用于構建私有云存儲、企業級數據湖、備份歸檔系統等場景。它以輕量、靈活、高效為核心特點&#xff0c;廣…

WPF 雙擊行為實現詳解:DoubleClickBehavior 源碼分析與實戰指南

WPF 雙擊行為實現詳解:DoubleClickBehavior 源碼分析與實戰指南 文章目錄 WPF 雙擊行為實現詳解:DoubleClickBehavior 源碼分析與實戰指南 引言 一、行為(Behavior)基礎概念 1.1 什么是行為? 1.2 行為的優勢 二、DoubleClickBehavior 源碼分析 2.1 類定義與依賴屬性 2.2 雙…

零知開源——基于STM32F103RBT6的TDS水質監測儀數據校準和ST7789顯示實戰教程

?零知開源是一個真正屬于國人自己的開源軟硬件平臺&#xff0c;在開發效率上超越了Arduino平臺并且更加容易上手&#xff0c;大大降低了開發難度。零知開源在軟件方面提供了完整的學習教程和豐富示例代碼&#xff0c;讓不懂程序的工程師也能非常輕而易舉的搭建電路來創作產品&…

luogu P3387 【模板】縮點

原題鏈接 原題再現 題目描述 給定一個 n 個點 m 條邊有向圖&#xff0c;每個點有一個權值&#xff0c;求一條路徑&#xff0c;使路徑經過的點權值之和最大。你只需要求出這個權值和。 允許多次經過一條邊或者一個點&#xff0c;但是&#xff0c;重復經過的點&#xff0c;權…

P1119 災后重建【題解】

P1119 災后重建 題目背景 B 地區在地震過后&#xff0c;所有村莊都造成了一定的損毀&#xff0c;而這場地震卻沒對公路造成什么影響。但是在村莊重建好之前&#xff0c;所有與未重建完成的村莊的公路均無法通車。換句話說&#xff0c;只有連接著兩個重建完成的村莊的公路才能通…

Horse3D引擎研發筆記(二):基于QtOpenGL使用仿Three.js的BufferAttribute結構重構三角形繪制

在Horse3D引擎的研發過程中&#xff0c;我們致力于構建一個高效、靈活且易于擴展的3D圖形引擎。在本篇博客中&#xff0c;我們將詳細記錄如何基于QtOpenGL框架&#xff0c;使用仿Three.js的BufferAttribute結構&#xff0c;重構三角形繪制流程。通過這一過程&#xff0c;我們希…

MCU程序段的分類

程序的下載&#xff08;燒錄到存儲器中&#xff09;通常是按照程序文件分段&#xff08;Code段、RO_data段、RW_data段、ZI_data段&#xff09;的方式存儲的&#xff0c;但運行時內存的布局會按照程序進程分段&#xff08;TEXT段、DATA段、BSS段、堆棧段&#xff09;進行組織。…

綜合項目記錄:自動化備份全網服務器數據平臺

一、項目背景與需求1.1項目概述該項目共分為2個子項目&#xff0c;由環境搭建和實施備份兩部分組成1.2項目總體需求企業內部有一臺web服務器&#xff0c;內部數據很重要&#xff0c;現需要為該web服務器數據做備份&#xff0c;這樣在數據丟失時可以恢復。要求如下&#xff1a;每…

聯合索引全解析:一棵樹,撐起查詢的半邊天

目錄 一、為什么聯合索引是MySQL性能優化的“王牌”&#xff1f; &#xff08;一&#xff09;索引的基本結構&#xff1a;從聚簇到非聚簇 1. 聚簇索引&#xff08;Clustered Index&#xff09; 2. 非聚簇索引&#xff08;Secondary Index&#xff09; &#xff08;二&…

vue開發的計算機課程頁面

課程信息展示頁面設計與實現我將設計一個美觀且實用的課程信息展示頁面&#xff0c;重點展示計算機網絡應用課程的相關信息。設計思路使用卡片式布局清晰展示課程各模塊信息采用科技感配色方案&#xff0c;符合計算機網絡課程主題添加動畫效果增強用戶體驗響應式設計確保在各種…

MySQL 正則表達式詳細說明

目錄 MySQL 正則表達式詳細說明 1. 基本操作符&#xff1a;REGEXP 和 RLIKE 2. 常用正則表達式模式 3. MySQL 正則表達式函數&#xff08;MySQL 8.0&#xff09; 4. 示例查詢 5. 注意事項 6. 總結 MySQL 正則表達式詳細說明 MySQL 支持正則表達式&#xff08;Regular Ex…

c++之 棧淺析

C之棧淺析 概要 通過可視化游戲梳理棧特點以及棧操作方式. 學習棧的工作原理就像往糖果罐里放糖果和拿糖果一樣簡單&#xff01; 棧特點 先進后出 技術名詞解釋 LIFO LIFO -> Last In, First Out 后進先出 可視化小游戲 游戲傳送門

C++ 算術函子

在 C 中&#xff0c;算術函子&#xff08;Arithmetic Functors&#xff09; 是標準庫 <functional> 中提供的一組函數對象&#xff0c;用于封裝基本的算術運算&#xff08;如加、減、乘、除等&#xff09;。它們本質上是類模板&#xff0c;重載了 operator()&#xff0c;…

Flutter 事件總線 Event Bus

文章目錄概要核心原理基本使用步驟優點注意事項適用場景小結概要 提示&#xff1a;這里可以添加技術概要 event_bus 是一個常用的第三方庫&#xff0c;用于實現跨組件 / 跨頁面的事件通信&#xff0c;基于發布 - 訂閱模式&#xff08;Publish-Subscribe Pattern&#xff09;工…

數據庫管理系統:入門需要了解的內容

數據庫管理系統&#xff1a;數字化時代的基石 在信息技術飛速發展的今天&#xff0c;我們生活在一個被數據包圍的世界里。從日常使用的社交媒體、電商平臺&#xff0c;到企業運營的核心業務系統&#xff0c;再到政府部門的政務管理&#xff0c;數據無處不在。而數據庫管理系統&…

安裝CST時,報錯問題處理

今天安裝這個軟件的時候&#xff0c;發現一個問題一直處理不了&#xff0c;然后看網上的一些解決方法&#xff0c;最終得到處理&#xff0c;這里就簡單記錄下解決方法。問題&#xff1a;處理方案&#xff1a;1.問題原因&#xff1a;crack中的CST Studio Suite 2022未配置成功。…

分治-快排-215.數組中的第k個最大元素-力扣(LeetCode)

一、題目解析1、需返回排序好的第k個最大元素2、要求時間復雜度為O(N)二、算法原理解法1&#xff1a;堆排序(大根堆) k*O(N)借用大堆的性質&#xff0c;將元素插入到大堆中&#xff0c;按照k輸出堆頂第k個元素解法2&#xff1a;堆排序(小根堆) (N-k)*O(logN)先建k個小堆&#x…

新手向:Python實現圖片轉ASCII藝術

Python實現圖片轉ASCII藝術&#xff1a;從零開始的完整指南Python實現圖片轉ASCII藝術的技術解析ASCII藝術是一種使用字符組合來表現圖像的技術&#xff0c;這種技術源于早期計算機顯示器的圖形限制&#xff0c;如今已成為一種獨特的數字藝術形式。ASCII藝術的應用場景十分廣泛…

6.類與對象(二)

總結 本章寫了封裝、static成員以及代碼塊。 一、封裝 1.封裝的概念 封裝簡單來說就是被密封起來&#xff08;不讓我們看見的東西&#xff09;&#xff0c;即被隱藏。 對于用戶來說&#xff0c;并不需要關心的類&#xff0c;所實現的細節就會被封裝&#xff08;隱藏&#x…