项目开发流程(前端 · 后端)
本文开头直奔要点——我会把每一步的目的、必要命令/配置、以及常见的陷阱与注意事项一并给出,便于你在新项目中快速复用或作为检查清单使用。读完你将能:
用 Vite + Vue 快速搭建前端骨架并接入常用组件库(Element Plus、vue-router、axios 等);
将常用的前端工具文件、路由、请求封装做到工程级别的可维护结构;
用 Spring Boot 建立后端服务骨架,配合 MyBatis、Redis 与 Spring Security 完成持久层与安全认证;
了解一套能在开发、调试、上线阶段减少重复工作的实践配置(例如:application.yml、统一响应体 R、登录处理器等)。
下方是我整理并保留的完整开发流程(按“前端 / 后端”分节),代码与配置均为可复制粘贴的实用示例。
—— 下面附上我的开发流程。
前端的:
一、创建 Vue 项目步骤
用 Vite 脚手架工具创建 Vue 项目。
在需要创建项目目录的 Dos 窗口输入以下内容,通过 vite 获取最新版本的 Vue 项目结构:
npm create vite@latest键入该 Vue 项目的名称
继续在原 Dos 窗口输入该项目名称,如:
my-vue-project选择 Vue 项目
Vite 工具还可以创建除了 Vue 项目之外的其他项目,这里选择 Vue 项目。
选择项目语言
有
TypeScript和JavaScript等语言供选择,推荐选择 TypeScript。安装项目依赖
此时,Vue 的项目已经创建完成。
cd 进入刚刚创建的项目,在新的目录输入以下命令:
npm install启动项目
在项目里输入以下命令以启动此 Vue 项目:
npm run dev或者在 Idea 工具中打开此项目,点击项目根目录下的
"dev": "vite",,这一行左边绿色箭头以启动此 Vue 项目。使用浏览器浏览项目
浏览器访问以下默认网址,以浏览该项目:
http://localhost:5173/至此,Vue 项目已经创建完成。下面几个步骤都是更加详细的步骤。
添加常见依赖
继续在原 Dos 窗口输入以下命令以安装依赖:(注意,在这个 vue 项目的目录下,即该目录下一定有之前经过
npm install指令生成的node_modules文件夹)element-plus:(饿了么 plus 组件)
npm install element-plus --save@element-plus/icons-vue:(饿了么 plus icons 组件)
npm install @element-plus/icons-vue --savevue-router:(路由组件)
npm install vue-router --saveaxios:(axios 异步请求)
npm install axios --savexxx:
最后:
编辑
~\src\main.js文件中的所有内容,变成如下内容:// 从 vue 框架,导入 createApp 函数
import { createApp } from 'vue'
// 从 ./App.vue 页面导入 App 组件(一般文件名叫什么,组件名也叫什么)
import App from './App.vue'
let app = createApp(App)
// 从 element-plus 中导入 ElementPlus 组件
import ElementPlus from 'element-plus'
// 导入 element-plus 的 CSS 样式,不需要 from 子句
import 'element-plus/dist/index.css'
app.use(ElementPlus)
// 导入 icons
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
// 从 router.js 中导入 router 组件
import router from "./router/router.js";
app.use(router)
// 利用上面所导入的 createApp 函数,创建一个 vue 应用,mount 是挂载到 #app 地方
app.mount('#app')调整项目文件
可删除
~\src\style.css,并删除其在~\src\main.js中的引用可删除
~\src\components目录及里面的所有文件编辑
~\index.html文件,这里可以修改网站首页的 Tittle编辑
~\src\App.vue文件中的所有内容,变成如下内容:<template>
<!--渲染路由地址所对应的页面组件-->
<router-view/>
</template>编辑
~/vite.config.js文件中的所有内容,变成如下内容:import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
// 设置访问的 ip 地址
host: '0.0.0.0',
// 设置前端 Vue 服务启动端口
port: 8080,
// 设置为 true 代表服务启动时自动打开浏览器
open: true,
}
})创建
~/src/view/IndexVue.vue这里的
view文件夹需额外创建。文件内容为:
<template>
<h1>Hello,这里是系统的首页!</h1>
</template>
<script>
import {defineComponent} from 'vue'
export default defineComponent({
name: "IndexVue",
})
</script>
<style scoped>
</style>创建
~/src/router/router.js这里的
router文件夹需额外创建。// 从 vue-router 中,导入 createRouter, createWebHistory 函数
import {createRouter, createWebHistory} from "vue-router";
// 定义变量
let router = createRouter({
// 路由历史
history: createWebHistory(),
// 配置路由,是一个数组,里面可以配置多个路由
routes: [
{
// 路由路径,这里设置为"/",代表项目先进入以下vue页面
path: "/",
// 路由路径所对应的页面(此文件目录为:~/src/view/LoginVue.vue)
component: () => import("../view/LoginVue.vue"),
},
// {
// // 路由路径
// path: "/dashboard",
// // 路由路径所对应的页面(此文件目录为:~/src/view/DashboardView.vue)
// component: () => import("../view/DashboardView.vue"),
// },
// 这里添加之后的路由,格式如上,如{},{},在大括号里写具体的路径对应
],
})
// 导出创建的路由对象
export default router;仅可修改里面的routes数组里面的内容,每增加一个页面,就新增一个路由
创建
~/src/http/HttpRequest.js这里的
http文件夹需额外创建。文件内容为:
import axios from "axios";
// 定义后端接口地址的前缀
axios.defaults.baseURL = "http://localhost:8089";
export function doGet(url, params) {
return axios({
method: 'get',
url: url,
// params 参数形式:{name: "张三", age: 22}
params: params,
dataType: "json"
})
}
export function doPost(url, data) {
return axios({
method: 'post',
url: url,
data: data,
dataType: "json"
})
}
export function doPut(url, data) {
return axios({
method: 'put',
url: url,
data: data,
dataType: "json"
})
}
export function doDelete(url, params) {
return axios({
method: 'delete',
url: url,
params: params,
dataType: "json"
})
}仅可修改里面的定义后端接口地址的前缀中的后端端口号,其他不建议修改
创建
~/src/util/util.js这里的
util文件夹需额外创建。文件内容为:
import {ElMessage} from "element-plus";
/**
* 消息提示工具方法
* @param msg 消息框的提示信息
* @param type 消息框的类型,可选 "error"|"success"|"warning"|"primary"
*/
export function messageTip(msg,type){
// messageTip 函数
ElMessage({
// true 代表提示信息可被关闭
showClose: true,
// true 代表显示居中
center: true,
// 设置提示信息的消失时间(单位:ms)
duration: 3000,
// 示例 msg 的值:'登录成功,欢迎回来!',
message: msg,
// 示例 type 的值:可选 "error"|"success"|"warning"|"primary"
type: type,
})
}这是个 js 工具类,需提前引入 element-plus。工具的作用是可以更加快捷得写出 弹出消息框 这个需求。使用代码如下:(在 vue 文件的
<script>区域里中使用)import {messageTip} from "../util/util.js";
// ...
messageTip("登录成功,欢迎回来!", "success");
messageTip("登录失败,账号或密码错误!", "error");
// ...
二、一个登录的例子使用 element-plus
在官网找合适的表单组件
我找的是:
找到想要的复制到自己的程序中
复制所有可以用到的组件,组件都是在
el-xxx中的。我复制的是这样的:<el-form :model="form" label-width="auto" style="max-width: 600px">
<el-form-item label="Activity name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="Activity type">
<el-checkbox-group v-model="form.type">
<el-checkbox value="Online activities" name="type">
Online activities
</el-checkbox>
<el-checkbox value="Promotion activities" name="type">
Promotion activities
</el-checkbox>
<el-checkbox value="Offline activities" name="type">
Offline activities
</el-checkbox>
<el-checkbox value="Simple brand exposure" name="type">
Simple brand exposure
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">Create</el-button>
<el-button>Cancel</el-button>
</el-form-item>
</el-form>修改成自己所需要的
<el-form :model="loginForm">
<!--表单的标题-->
<el-form-item>
<h2>系统登录</h2>
</el-form-item>
<!--表单的账号-->
<el-form-item label="账号">
<el-input v-model="loginForm.loginAct" />
</el-form-item>
<!--表单的密码-->
<el-form-item label="密码">
<!--为该字段添加密码类型-->
<el-input type="password" v-model="loginForm.loginPwd" />
</el-form-item>
<!--表单的注册按钮-->
<el-form-item>
<!--添加按钮点击函数:login-->
<el-button type="primary" @click="login">登录</el-button>
</el-form-item>
<!--表单的记住我选项-->
<el-form-item>
<el-checkbox v-model="loginForm.rememberMe">
记住我
</el-checkbox>
</el-form-item>
</el-form>注册 model
在 js 中注册所有提到的 model
<script>
import {defineComponent} from 'vue'
export default defineComponent({
name: "LoginVue",
// 所有的变量都需要注册在 data 里
data(){
return {
// loginForm 是对象,所以注册为 {}
loginForm: {},
// loginAct 是字符串,所以注册为 ""
loginAct: "",
// loginPwd 是字符串,所以注册为 ""
loginPwd: "",
// rememberMe 是布尔类型,所以注册为 false
rememberMe: false,
}
},
// 所有的方法都需要注册在 methods 里
methods: {
// 登录函数
login() {
}
}
})
</script>添加表单的前端验证
Step1 为表单添加规则字段
:rulesStep2 为字段添加属性字段
propStep3 注册上述两步的字段
Step4 添加详细的验证规则
<!--为需要添加验证的表单,添加 rules-->
<el-form :model="loginForm" :rules="loginRules">
<el-form-item class="form-title">
<h2>系统登录</h2>
</el-form-item>
<!--为需要添加验证的账号字段,加上 prop 属性,值为这个字段的字段名-->
<el-form-item label="账号" prop="loginAct">
<el-input v-model="loginForm.loginAct" />
</el-form-item>
<!--为需要添加验证的账号字段,加上 prop 属性,值为这个字段的字段名-->
<el-form-item label="密码" prop="loginPwd">
<el-input type="password" v-model="loginForm.loginPwd" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="login" class="form-button">登录</el-button>
</el-form-item>
<el-form-item>
<el-checkbox v-model="loginForm.rememberMe">
记住我
</el-checkbox>
</el-form-item>
</el-form><script>
import {defineComponent} from 'vue'
export default defineComponent({
name: "LoginVue",
data(){
return {
loginForm: {},
loginAct: "",
loginPwd: "",
rememberMe: false,
// loginRules 是对象,所以注册为 {}
loginRules: {
// 定义 loginAct 的规则,规则可以有多个,所以是数组,用 []
loginAct: [
// 添加账号不能为空的验证
{required: true, message: '请输入账号!', trigger: 'blur'},
],
// 定义 loginPwd 的规则,规则可以有多个,所以是数组,用 []
loginPwd: [
// 添加密码不能为空的验证
{required: true, message: '请输入密码!', trigger: 'blur'},
// 添加密码长度的验证
{min: 6, max: 16, message: '密码的长度在 6-16 之间!', trigger: 'blur'},
],
},
}
},
// 所有的方法都需要注册在 methods 里
methods: {
// 登录函数
login() {
}
}
})
</script>添加表单的提交验证
Step1 为需要添加登录验证的表单,添加 ref
Step2 在提交方法中验证输入框的合法性(共用上一步写的验证)
Step3 选择数据,发送请求
Step4 根据请求的响应,做出不同的选择......
Step5 若响应信息为做出登录请求,则进入系统主页,这里引入路由
<template>
<!--为需要添加登录验证的表单,添加 ref-->
<el-form ref="loginRefForm" :model="loginForm" :rules="loginRules">
<el-form-item class="form-title">
<h2>系统登录</h2>
</el-form-item>
<el-form-item label="账号" prop="loginAct">
<el-input v-model="loginForm.loginAct" />
</el-form-item>
<el-form-item label="密码" prop="loginPwd">
<el-input type="password" v-model="loginForm.loginPwd" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="login" class="form-button">登录</el-button>
</el-form-item>
<el-form-item>
<el-checkbox v-model="loginForm.rememberMe">
记住我
</el-checkbox>
</el-form-item>
</el-form>
</template><script>
import {defineComponent} from 'vue'
// 自动导入依赖
import {doPost} from "../http/HttpRequest.js";
import {messageTip} from "../util/util.js";
export default defineComponent({
name: "LoginVue",
data(){
return {
loginForm: {},
loginAct: "",
loginPwd: "",
rememberMe: false,
loginRules: {
loginAct: [
{required: true, message: '请输入账号!', trigger: 'blur'},
],
loginPwd: [
{required: true, message: '请输入密码!', trigger: 'blur'},
{min: 6, max: 16, message: '密码的长度在 6-16 之间!', trigger: 'blur'},
],
},
}
},
methods: {
login() {
// 提交前验证输入框的合法性
this.$refs.loginRefForm.validate((isValid) => {
if (isValid) {
// 运行到这里说明验证通过
// 使用 formData 上传数据
let formData = new FormData();
// 以键值对的形式写入数据
formData.append('loginAct', this.loginForm.loginAct);
formData.append('loginPwd', this.loginForm.loginPwd);
doPost("/api/login",formData).then((resp) =>{
// 看看响应的形式是怎么样的
// console.log(resp);
if (resp.data.code === 200){
messageTip("登录成功,欢迎回来!", "success");
window.location.href = "/dashboard";
}else {
messageTip("登录失败,账号或密码错误!", "error");
}
});
}
})
},
}
})
</script>import {createRouter, createWebHistory} from "vue-router";
let router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/",
component: () => import("../view/LoginVue.vue"),
},
// 在这里添加要跳转的页面的路由,一个完整的路由就是由下面的 {},这样在一块的,里面是 path 和 component
{
// 路由路径
path: "/dashboard",
// 路由路径所对应的页面(此文件目录为:~/src/view/DashboardView.vue)
component: () => import("../view/DashboardView.vue"),
},
],
})
export default router;
三、持续更新中
后端的:
一、创建 SpringBoot 项目步骤
项目依赖(经验证过的SpringBoot版本:3.5.5)
Spring Boot DevTools
Lombok
Spring Configuration Processor
Spring Web
Spring Security
MyBatis Framework
MySQL Driver
Spring Data Redis (Access+Driver)
编写 application.yml
server
# 设置后端 SpringBoot 端口
port8089
servlet
# 项目路径直接是斜杠
context-path/
# 配置数据库连接相关信息
spring
datasource
typecom.zaxxer.hikari.HikariDataSource
urljdbcmysql//localhost3306/xxxxxxxxx?useUnicode=true&characterEncoding=utf8&useSSL=false
driver-class-namecom.mysql.cj.jdbc.Driver
usernameroot
passwordxxxxxxxxxxxxxxx
hikari
maximum-pool-size30
minimum-idle5
connection-timeout5000
idle-timeout600000
max-lifetime18000000
data
# 配置 redis 的连接信息
redis
host127.0.0.1
port6379
password
database
# 指定以下 mapper.xml 文件的位置
mybatis
mapper-locationsclasspathmapper/*.xml创建项目的包结构
config
handler
manager
mapper
model
query
result
service
impl
util
web
添加项目的工具类
创建
~/util/JsonUtils.java文件内容为:
package com.sangui.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @Author: sangui
* @CreateTime: 2025-08-30
* @Description: json 工具类,进行 java 对象与 json 字符串之间的相互转化
* @Version: 1.0
*/
public class JsonUtils {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/**
* 把 java 对象转成 json 字符串
*
* @param object java 对象
* @return json 字符串
*/
public static String toJson(Object object) {
try {
return OBJECT_MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 把 json 字符串转成 java 对象
*
* @param json json 字符串
* @param clazz 想要转的 java 对象 的类型
* @return java 对象
* @param <T> java 对象的类型
*/
public static <T> T toBean(String json, Class<T> clazz) {
try {
return OBJECT_MAPPER.readValue(json, clazz);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}创建
~/util/ResponseUtils.java文件内容为:
package com.sangui.util;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @Author: sangui
* @CreateTime: 2025-08-30
* @Description: Response 工具类
* @Version: 1.0
*/
public class ResponseUtils {
/**
* 使用 response,把结果写出到前端
*
* @param response 响应
* @param result 结果
*/
public static void write(HttpServletResponse response, String result) {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.write(result);
writer.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (writer != null) {
writer.close();
}
}
}
}创建
~/result/CodeEnum.javapackage com.sangui.result;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Author: sangui
* @CreateTime: 2025-08-30
* @Description: R 状态的枚举类
* @Version: 1.0
*/
public enum CodeEnum {
Ok(200,"成功"),
Fail(500,"失败");
// 结果码
private final int code;
// 结果信息
private final String msg;
}创建
~/result/R.javapackage com.sangui.result;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: sangui
* @CreateTime: 2025-08-30
* @Description: 统一封装 web 层向前端页面返回的结果,即 VO(View Object)
* @Version: 1.0
*/
public class R {
// 表示返回的结果码,比如 200 成功,500 失败
private int code;
// 表示返回的结果信息,比如 用户登录状态失效了,请求数格式有误......
private String msg;
// 表示返回的结果数据,数据可能是一个对象,也可以是一个 int 集合.....
private Object data;
public static R ok(Object data) {
return R.builder()
.code(CodeEnum.Ok.getCode())
.msg(CodeEnum.Ok.getMsg())
.data(data)
.build();
}
public static R fail() {
return R.builder()
.code(CodeEnum.Fail.getCode())
.msg(CodeEnum.Fail.getMsg())
.build();
}
public static R fail(String msg) {
return R.builder()
.code(CodeEnum.Fail.getCode())
.msg(msg)
.build();
}
}
二、一个使用 SpringSecurity 登录作为例子
使用 Idea 插件
Free MyBatis Tool自动生成代码使用插件生成以下内容:
model 实体类
设置该类的包为:com.sangui.model
mapper 接口类
设置接口的名字后缀为:Mapper
设置该类的包为:com.sangui.mapper
mapper 映射文件
设置该类的包为:mapper
注意:
生成代码之前,取消勾选 Rpository-Annotation(Repository注解),保留前面五个选项
生成代码之后,修改数据库中 JdbcType=tinyint 对应类的属性的数据类型,从 Byte 改为 Boolean
model 类中的任何 Boolean 类型的变量,都不要加 is 前缀,从 isXxx 改为 xxx
同时在含有 is_xxx 字段的表对应
mapper映射文件的 <resuletMap> 中设置从 is_xxx 到 xxx 的映射关系千万别忘了在主程序启动类上加上 mapper 包的扫描,如:自行更改可能不一样的包名)
(basePackages = {"com.sangui.mapper"})在生成的 UserMapper 接口中新增按用户名查找用户的方法,示例如下:(自行更改可能不一样的变量名)
User selectByLoginName(String loginName);同时在
mapper映射文件中新增对应 id 的 SQL 语句,示例如下:(自行更改可能不一样的变量名)<select id="selectByLoginName" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_user
where login_act = #{loginName,jdbcType=BIGINT}
</select>修改用于登录的 User 类
类中加入两个属性,示例如下:
// 角色 List
private List<String> roleList;
// 权限标识符 List
private List<String> permissionList;实现 UserDetails 接口
实现 UserDetails 接口的七个方法,示例如下:
// 实现UserDetails的七个方法
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
// 角色
if (!ObjectUtils.isEmpty(this.getRoleList())){
this.getRoleList().forEach(role -> {
list.add(new SimpleGrantedAuthority(role));
});
}
// 权限标识符
if (!ObjectUtils.isEmpty(this.getPermissionList())){
this.getPermissionList().forEach(permission -> {
list.add(new SimpleGrantedAuthority(permission));
});
}
return list;
}
public String getPassword() {
return this.getPasswordHash();
}
public String getUsername() {
return this.getLoginName();
}
public boolean isAccountNonExpired() {
return this.getAccountExpired();
}
public boolean isAccountNonLocked() {
return this.getAccountLocked();
}
public boolean isCredentialsNonExpired() {
return this.getCredentialsExpired();
}
public boolean isEnabled() {
return this.getEnabled();
}
创建
~/config/handler/MyAuthenticationFailureHandler.java文件内容为:
package com.sangui.config.handler;
import com.sangui.result.R;
import com.sangui.util.JsonUtils;
import com.sangui.util.ResponseUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @Author: sangui
* @CreateTime: 2025-08-31
* @Description: user 登录失败的处理器
* @Version: 1.0
*/
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// 登录失败,执行该方法,在该方法中返回 json 给前端,就行了
// 登录失败的统一结果
R result = R.fail(exception.getMessage());
// 把 R 对象转成 json
String resultJson = JsonUtils.toJson(result);
// 把 R 以json返回给前端
ResponseUtils.write(response, resultJson);
}
}创建
~/config/handler/MyAuthenticationSuccessHandler.java文件内容为:
package com.sangui.config.handler;
import com.sangui.model.TUser;
import com.sangui.result.R;
import com.sangui.util.JsonUtils;
import com.sangui.util.ResponseUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @Author: sangui
* @CreateTime: 2025-08-31
* @Description: user 登录成功的处理器
* @Version: 1.0
*/
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// 登录成功,执行该方法,在该方法中返回 json 给前端,就行了
TUser tUser = (TUser) authentication.getPrincipal();
// 登录成功的统一结果
R result = R.ok(tUser);
// 把 R 对象转成 json
String resultJson = JsonUtils.toJson(result);
// 把 R 以 json 返回给前端
ResponseUtils.write(response, resultJson);
}
}注意:
方法中的 TUser 是之前自动创建的实体类,示例代码可能与实际不同,请根据自己的验证账号的实体类的名字决定。
创建
~/config/SecurityConfig.java文件内容为:
package com.sangui.config;
import com.sangui.config.handler.MyAuthenticationFailureHandler;
import com.sangui.config.handler.MyAuthenticationSuccessHandler;
import jakarta.annotation.Resource;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
/**
* @Author: sangui
* @CreateTime: 2025-08-31
* @Description: SpringSecurity 配置文件
* @Version: 1.0
*/
public class SecurityConfig {
// 后端验证登录的 URL
private final String LOGIN_URL = "/api/login";
// 在 user 表中,登录账号的属性名
private final String NAME_OF_USERNAME_IN_USER = "loginName";
// 在 user 表中,密码的属性名
private final String NAME_OF_PASSWORD_IN_USER = "passwordHash";
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, CorsConfigurationSource corsConfigurationSource) throws Exception {
// 禁用跨站请求伪造
return httpSecurity.formLogin((formLogin)->{
formLogin.loginProcessingUrl(LOGIN_URL)
.usernameParameter(NAME_OF_USERNAME_IN_USER)
.passwordParameter(NAME_OF_PASSWORD_IN_USER)
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler);
})
.authorizeHttpRequests((authorize)->{
// 任何请求都需要登录后才能访问,除了 "/api/login"
authorize.requestMatchers(LOGIN_URL).permitAll()
.anyRequest().authenticated();
})
// 方法引用 禁用跨站请求伪造
.csrf(AbstractHttpConfigurer::disable)
// 支持跨域请求
.cors((cors) ->{
cors.configurationSource(corsConfigurationSource);
})
.build();
}
public CorsConfigurationSource corsConfigurationSource(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许任何来源,http://localhost:8080
corsConfiguration.setAllowedOrigins(List.of("*"));
// 运行任何方式,post, get, delete, put
corsConfiguration.setAllowedMethods(List.of("*"));
// 设置运行的请求头
corsConfiguration.setAllowedHeaders(List.of("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",corsConfiguration);
return source;
}
}注意:
修改该类中的这三个值以适配自己的 user 表
// 后端验证登录的 URL
private final String LOGIN_URL = "/api/login";
// 在 user 表中,登录账号的属性名
private final String NAME_OF_USERNAME_IN_USER = "loginName";
// 在 user 表中,密码的属性名
private final String NAME_OF_PASSWORD_IN_USER = "passwordHash";
创建
~/service/UserService.java创建这个 UserService 接口,只需要继承 UserDetailsService 接口就好
示例代码如下:
package com.sangui.service;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* @Author: sangui
* @CreateTime: 2025-08-31
* @Description: UserService
* @Version: 1.0
*/
public interface UserService extends UserDetailsService {
}创建
~/service/impl/UserServiceImpl.java文件内容为:
package com.sangui.service.impl;
import com.sangui.mapper.TUserMapper;
import com.sangui.model.TUser;
import com.sangui.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @Author: sangui
* @CreateTime: 2025-08-31
* @Description: UserServiceImpl
* @Version: 1.0
*/
public class UserServiceImpl implements UserService {
TUserMapper userMapper;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
TUser tUser = userMapper.selectByLoginName(username);
if (tUser == null) {
throw new UsernameNotFoundException("登录账号不存在!");
}
return tUser;
}
}
- 微信
- 赶快加我聊天吧

- 赶快加我聊天吧
