ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于SpringBoot+Vue的注册邮件激活功能实战解析

基于SpringBoot+Vue的注册邮件激活功能实战解析 简介本资源是一个基于Vue.js与SpringBoot全栈整合的用户邮箱激活系统实战项目面向Web开发初学者及全栈入门者解决现代Web应用中注册环节的身份核验与安全激活问题。压缩包共63个文件涵盖17个JavaScript前端逻辑文件、10个Java后端核心类含Controller、Service、Entity及邮件配置、5个Vue单文件组件、2个YML配置文件含SMTP邮件服务参数、1个SQL建表脚本及配套HTML/CSS/图片等资源整体体积仅675KB结构精简、开箱即用。已有493人学习下载适合快速理解前后端分离架构下邮箱验证的完整链路。读者可直接运行获得注册表单提交、唯一激活码生成、Thymeleaf动态邮件模板渲染、带时效性的激活链接校验及前后端错误反馈等完整功能模块代码注释清晰目录层次分明含login-demo前端工程与demo后端模块便于对照学习与二次开发。 “注册邮件激活”这套流程基本上是所有带账号体系的Web项目都绕不开的标配功能。这几天刚好有个朋友问我要一套能直接跑的VueSpringBoot注册发送邮件激活项目我就把之前写过的代码重新整理翻了出来。这个项目不大但麻雀虽小五脏俱全前端Vue负责注册表单和互动提示后端SpringBoot负责用户落库、生成激活码、调用SMTP发邮件用户点击邮件里的链接完成账号激活整套流程闭环跑通。对于正在做课程设计、毕业设计或者刚接触前后端分离想练手完整业务闭环的同学来说这个项目非常适合拿来当模板看懂之后自己动手改改就能用。下面我会把这套实现从设计到落地的完整思路拆开讲包括后端怎么写、前端怎么配合、激活链接怎么设计、邮件发送踩过哪些坑以及联调测试时最容易翻车的几个点。内容偏实战代码也是可以直接抄作业的程度。1. 这个项目到底做了什么以及为什么值得自己写一遍先花几百字把项目全貌说清楚避免你下载完代码之后一头雾水不知道该从哪看起。1.1 一句话说清楚系统流程整个系统的核心流程其实很简单可以拆成五步用户在前端Vue注册页填写用户名、邮箱、密码点击注册按钮。前端把表单数据通过POST请求提交给SpringBoot后端接口。后端校验参数后将用户信息写入数据库此时账号状态是“未激活”。后端生成一个唯一的激活码通常是一串UUID拼接成激活链接通过JavaMailSender发送到用户邮箱。用户点击邮件里的激活链接携带激活码访问激活接口后端校验激活码正确且未过期把账号状态改为“已激活”整个注册流程结束。这套流程看着简单但每一步都有值得琢磨的细节。比如激活码为什么要用UUID而不是自增ID激活链接为什么要带过期时间而不是永久有效这些都是面试官爱问、实际项目也容易踩坑的地方。1.2 为什么注册环节非要加邮件激活很多新手会觉得邮件激活多此一举用户注册完直接登录不香吗但只要你做过面向真实用户的系统就会明白这一步有多重要。第一验证邮箱真实性。垃圾注册、批量刷号是每个平台都头疼的问题。如果注册不需要验证邮箱脚本可以伪造几千个假账号产生大量脏数据。邮件激活等于让用户证明“这个邮箱确实是我的”极大地提高了批量注册的成本。第二为后续找回密码等功能铺路。账号密码忘了要通过邮箱找回如果邮箱本身就是假的整个流程就失效了。注册时先完成邮箱验证后面的密码找回、安全通知、营销邮件才能建立在可靠的基础上。第三避免无效账号占用资源。未激活的账号可以定期清理减少数据库垃圾数据。很多系统也会限制未激活账号的登录权限引导用户先去完成激活。所以不要觉得邮件激活是多余的这是生产环境的刚需也是判断一个开发人员有没有真正做过上线项目的一个小标尺。1.3 技术栈选型的实际理由这个项目选择VueSpringBoot本身就是当下Java后端开发圈子最主流的组合没有之一。SpringBoot负责提供RESTful API和处理邮件逻辑Vue负责页面展示和交互体验两者通过JSON数据交互。选这套组合有三个现实理由找工作、做毕设认可度高。目前市面上Java后端岗位的技术栈绝大多数是SpringBoot Vue这套组合你把这套流程吃透面试时聊到注册登录、邮件发送、前后端联调都有真实案例可以讲。SpringBoot对邮件的支持非常省心。spring-boot-starter-mail封装了JavaMail的全部底层细节你只需要配置文件里写上SMTP服务器地址、账号、授权码代码里注入一个JavaMailSender就能发邮件不需要自己写Socket通信。Vue做表单交互开发效率高。注册页的校验、按钮loading状态、成功失败提示用Vue的双向绑定和指令系统写起来非常顺手比传统JSPJQuery时代体验好太多。2. 后端SpringBoot核心逻辑拆解后端是整个项目的核心我按照“用户表设计 → 邮件服务 → 注册接口 → 激活接口”的顺序来讲每个环节都给出关键代码和设计理由。2.1 用户表和实体类怎么设计先看数据库表结构。我没有用复杂的权限表只围绕注册激活这个核心需求设计了一张user表CREATE TABLE user ( id int NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码(BCrypt加密), email varchar(100) NOT NULL COMMENT 邮箱, status tinyint NOT NULL DEFAULT 0 COMMENT 账号状态 0-未激活 1-已激活, activate_code varchar(64) DEFAULT NULL COMMENT 激活码, activate_code_expire_time datetime DEFAULT NULL COMMENT 激活码过期时间, create_time datetime DEFAULT NULL COMMENT 注册时间, activate_time datetime DEFAULT NULL COMMENT 激活时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username), UNIQUE KEY uk_email (email) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;几个字段的设计意图我说明一下status字段是整个激活逻辑的状态机。0表示未激活1表示已激活。激活成功就是一次简单的UPDATE。activate_code字段存激活码这个值必须是全局唯一且不可预测的我用UUID.randomUUID().toString().replace(-, )来生成长度32位碰撞概率可以忽略不计。activate_code_expire_time字段是激活码过期时间。如果不设置过期时间用户几个月后拿着老邮件来点激活会把已经重新注册的同名邮箱覆盖造成安全问题。email字段加唯一索引防止同一个邮箱被重复注册。对应的User实体类可以用Lombok简化Data TableName(user) public class User { private Integer id; private String username; private String password; private String email; private Integer status; private String activateCode; private Date activateCodeExpireTime; private Date createTime; private Date activateTime; }这里用的TableName是MyBatis-Plus的注解。如果你习惯用MyBatis原生或JPA逻辑是一样的字段对上就行。我建议新手直接上MyBatis-Plus避免写大量XML映射把注意力集中在业务流程上。2.2 邮件服务配置与授权码获取SpringBoot发邮件只需要在application.yml里配置SMTP信息。以163邮箱为例spring: mail: host: smtp.163.com port: 465 username: your_account163.com password: your_auth_code protocol: smtps default-encoding: UTF-8 properties: mail: smtp: ssl: enable: true auth: true starttls: enable: true这里最容易踩的坑是password不是邮箱登录密码而是邮箱的SMTP授权码。163、QQ邮箱都需要在网页端设置里开启SMTP服务然后生成一个授权码。QQ邮箱是登录qq邮箱 → 设置 → 账号 → 开启POP3/SMTP服务会得到一个16位授权码。网易邮箱同理。我特别说明一下为什么推荐用465端口SMTPS协议。很多教程用587端口走STARTTLS但在某些服务器环境下公司网络会拦截25端口或587端口的连接导致超时。465端口是SSL直接加密的兼容性更好。所以在配置里我显式写了protocol: smtps和mail.smtp.ssl.enabletrue。邮件发送服务类的核心代码Service public class MailService { Resource private JavaMailSender mailSender; Value(${spring.mail.username}) private String from; public void sendActivateMail(String to, String activateUrl) { SimpleMailMessage message new SimpleMailMessage(); message.setFrom(from); message.setTo(to); message.setSubject(【XX系统】账号激活通知); message.setText(亲爱的用户\n\n 您正在注册XX系统账号请点击以下链接完成激活\n activateUrl \n\n 如果链接无法点击请复制到浏览器地址栏访问。\n 链接有效期为24小时请及时激活。\n\n 如非本人操作请忽略此邮件。); mailSender.send(message); } }这是最简版本的SimpleMailMessage纯文本邮件。生产环境建议用MimeMessageHelper配合HTML模板可以把激活链接做成按钮样式体验更好。比如用Thymeleaf模板引擎渲染邮件内容这部分代码我放在后面扩展章节讲。2.3 注册接口写库、生成激活码、发邮件注册接口是整个后端的重头戏逻辑顺序一定要理清楚先校验参数、再查重、然后生成激活码、保存用户、最后发送邮件。PostMapping(/register) public Result register(RequestBody RegisterDTO dto) { // 1. 参数校验 if (StringUtils.isBlank(dto.getUsername()) || StringUtils.isBlank(dto.getEmail()) || StringUtils.isBlank(dto.getPassword())) { return Result.error(用户名、邮箱、密码不能为空); } // 2. 用户名和邮箱查重 if (userService.getByUsername(dto.getUsername()) ! null) { return Result.error(用户名已被注册); } if (userService.getByEmail(dto.getEmail()) ! null) { return Result.error(该邮箱已被注册); } // 3. 生成激活码和过期时间 String activateCode UUID.randomUUID().toString().replace(-, ); Date expireTime new Date(System.currentTimeMillis() 24 * 60 * 60 * 1000); // 4. 构建用户对象密码加密存储 User user new User(); user.setUsername(dto.getUsername()); user.setPassword(new BCryptPasswordEncoder().encode(dto.getPassword())); user.setEmail(dto.getEmail()); user.setStatus(0); // 默认未激活 user.setActivateCode(activateCode); user.setActivateCodeExpireTime(expireTime); user.setCreateTime(new Date()); userService.save(user); // 5. 拼接激活链接并发送邮件 String activateUrl http://localhost:8080/api/activate?code activateCode; mailService.sendActivateMail(dto.getEmail(), activateUrl); return Result.success(注册成功请查收激活邮件); }这里的细节我要重点展开讲密码加密为什么用BCrypt。绝对不能明文存密码这是底线。BCrypt是不可逆哈希而且每次加密结果都不一样内置盐值处理即使两个用户密码相同密文也不同能在一定程度上抵抗彩虹表攻击。Spring Security里自带BCryptPasswordEncoder单独引入spring-security-crypto这个依赖也可以独立使用。先保存用户再发邮件还是先发邮件再保存我的做法是先保存用户再调用mailService发邮件。如果邮件发送失败通过try-catch捕获后可以返回“注册失败”或“注册成功但邮件发送失败”的提示。但要注意如果先保存用户再发邮件时抛异常会导致用户表里多了一条未激活的脏数据。实际项目里更稳妥的方案是邮件发送失败时删除刚保存的用户或者把发送失败的情况记录到日志表里稍后重试。教学项目我保留了最简逻辑但你心里要清楚生产环境不能这么简单。激活链接拼接的域名写死还是动态获取我这里为了方便返回的是localhost但真实项目里这个地址应该是可配置的比如写到application.yml里app: activate-base-url: http://your-domain.com:8080然后在代码里通过Value注入拼接成完整的链接。否则部署到服务器上之后链接还是指向localhost用户点击之后访问的是他自己的电脑必然失败。2.4 激活接口校验、更新、统一响应激活接口是最简单但最容易写错的地方。我见过很多人只校验激活码是否存在不校验过期时间、不校验账号当前状态结果出现各种逻辑漏洞。GetMapping(/activate) public Result activate(RequestParam(code) String code) { // 1. 根据激活码查询用户 User user userService.getByActivateCode(code); if (user null) { return Result.error(激活链接无效); } // 2. 校验是否已激活 if (user.getStatus() 1) { return Result.success(账号已激活请直接登录); } // 3. 校验是否过期 if (user.getActivateCodeExpireTime() null || user.getActivateCodeExpireTime().before(new Date())) { return Result.error(激活链接已过期请重新发送激活邮件); } // 4. 更新状态 user.setStatus(1); user.setActivateTime(new Date()); user.setActivateCode(null); // 激活后清空激活码防止重复激活 user.setActivateCodeExpireTime(null); userService.updateById(user); return Result.success(激活成功请前往登录); }激活后清空激活码和过期时间这个细节很重要。如果不清空同一个链接可以反复被访问而且每次都会执行一次UPDATE虽然影响不大但逻辑上不够严谨。清空之后用户再点同一条链接第一步查询就查不到返回“激活链接无效”语义也合理。另外这个接口我用的是GET请求因为用户点邮件链接就是一次浏览器GET请求你不可能让用户在地址栏里构造一个POST请求。这一点是邮件激活场景的特殊性和平时写接口要求POST的规范不一样。还有一点容易被忽略如果激活链接指向后端接口激活成功后应该返回一个HTML页面或重定向到前端页面而不是返回JSON。因为用户是在浏览器地址栏直接访问的返回JSON会让用户看到一串不友好的文本。后面讲前端配合的时候我会给出两种处理方式的取舍。3. 前端Vue页面与交互设计后端接口写完了接下来看前端Vue这边怎么配合。前端的主要工作是注册页面、请求封装、激活结果提示以及和激活链接的衔接方式。3.1 注册页面的表单校验我用Vue3 Vite Element Plus来搭建的注册页面这个组合目前用得最多。表单部分直接上代码template div classregister-container el-card classregister-card h2用户注册/h2 el-form refregisterFormRef :modelregisterForm :rulesregisterRules label-width80px el-form-item label用户名 propusername el-input v-modelregisterForm.username placeholder请输入用户名 / /el-form-item el-form-item label邮箱 propemail el-input v-modelregisterForm.email placeholder请输入邮箱 / /el-form-item el-form-item label密码 proppassword el-input v-modelregisterForm.password typepassword show-password placeholder请输入密码 / /el-form-item el-form-item label确认密码 propconfirmPassword el-input v-modelregisterForm.confirmPassword typepassword show-password placeholder请再次输入密码 / /el-form-item el-form-item el-button typeprimary :loadingsubmitting clickhandleRegister 注册 /el-button /el-form-item /el-form /el-card /div /template script setup import { reactive, ref } from vue import { ElMessage } from element-plus import request from /utils/request const registerFormRef ref() const submitting ref(false) const registerForm reactive({ username: , email: , password: , confirmPassword: }) // 自定义确认密码校验 const validateConfirmPassword (rule, value, callback) { if (value ! registerForm.password) { callback(new Error(两次输入的密码不一致)) } else { callback() } } const registerRules { username: [ { required: true, message: 请输入用户名, trigger: blur }, { min: 3, max: 20, message: 用户名长度3-20个字符, trigger: blur } ], email: [ { required: true, message: 请输入邮箱, trigger: blur }, { type: email, message: 邮箱格式不正确, trigger: blur } ], password: [ { required: true, message: 请输入密码, trigger: blur }, { min: 6, message: 密码长度不能少于6位, trigger: blur } ], confirmPassword: [ { required: true, message: 请再次输入密码, trigger: blur }, { validator: validateConfirmPassword, trigger: blur } ] } const handleRegister () { registerFormRef.value.validate(async (valid) { if (!valid) return submitting.value true try { const res await request.post(/api/register, { username: registerForm.username, email: registerForm.email, password: registerForm.password }) if (res.code 200) { ElMessage.success(注册成功激活邮件已发送请查收) } else { ElMessage.error(res.message) } } catch (err) { ElMessage.error(网络异常请稍后重试) } finally { submitting.value false } }) } /script表单校验这块我建议做到前端先拦一遍后端再拦一遍不要怕麻烦。前端校验是为了用户体验用户输入不符合规则时立即提示不用等提交到后端后端校验是为了安全防止有人绕过前端直接调接口。按钮的loading属性也很关键。用户点击注册后后端处理需要一些时间尤其是发邮件如果不加loading用户会以为没点上疯狂点击导致提交多条重复数据。设置了:loadingsubmitting之后按钮会转圈且不可重复点击这是最基础的防重复提交手段。3.2 激活链接的跳转设计前后端分离怎么处理这是前后端分离架构下最需要思考的一个点。激活链接点击后到底该指向谁方案一链接指向后端激活接口后端激活成功后重定向到前端结果页。比如激活链接是http://localhost:8080/api/activate?codexxx后端处理完后返回302重定向到http://localhost:5173/activate-result?statussuccess。这个方案的好处是用户点击链接后后端在服务端直接完成激活逻辑不用前端做任何事缺点是重定向地址需要动态拼接前端的地址如果前端部署域名变了链接就失效了。方案二链接指向前端激活页面携带code参数前端页面调后端接口激活。激活链接是http://localhost:5173/activate?codexxx用户在浏览器打开的是前端Vue页面这个页面在onMounted里拿到URL上的code然后调用/api/activate?codexxx根据后端返回结果展示不同的提示页。这个方案更符合前后端分离的直觉前端可以做出更美观的激活结果页但需要前端多写一个页面。我在这个项目里用的是方案一但代码里也预留了方案二的实现接口。如果只是一次性激活方案一代码量最少如果希望激活成功后引导用户去设置密码、完善资料方案二体验更好。你自己取舍。顺带补充一下Vue Router里配置一个激活结果页通过路由钩子或query参数来展示结果代码大致是这样template div classactivate-result el-result :iconresultStatus :titleresultTitle :sub-titleresultSubTitle template #extra el-button typeprimary clickgoLogin去登录/el-button /template /el-result /div /template script setup import { computed } from vue import { useRoute, useRouter } from vue-router const route useRoute() const router useRouter() const status route.query.status const resultStatus computed(() (status success ? success : error)) const resultTitle computed(() (status success ? 激活成功 : 激活失败)) const resultSubTitle computed(() status success ? 您的账号已完成激活可以登录使用了 : 激活链接无效或已过期请重新注册或联系管理员 ) const goLogin () { router.push(/login) } /script3.3 前端请求封装与跨域处理前端调用后端接口时我用axios统一封装了一个request实例这样不用每个页面都重复写baseURL和拦截器import axios from axios const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || http://localhost:8080, timeout: 10000 }) request.interceptors.response.use( (response) { return response.data }, (error) { return Promise.reject(error) } ) export default request开发环境下Vite默认端口是5173后端SpringBoot默认端口是8080跨域是必然的。处理跨域有两种常见方案我推荐在后端做全局CORS配置因为前端部署到Nginx之后用反向代理也能解决但本地开发时后端允许跨域最直接Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }allowedOriginPatterns使用*的时候如果你不是前后端分离的敏感场景allowCredentials设置为true会报错。这里我一般建议如果不需要携带Cookie就不开allowCredentials或者明确指定前端的来源地址。4. 联调、踩坑与问题排查实录这一章是我最想说的。代码本身不难真正让新手心态爆炸的都是那些不起眼的小问题。我把实际开发中高频出现的问题整理成清单按症状、原因、解决方案三个维度来讲。4.1 邮件发送失败的常见原因邮件发不出去报错信息五花八门但根因基本都是以下几类第一类javax.mail.AuthenticationFailedException认证失败。原因几乎都是授权码填错了或者账号密码格式不对。SpringBoot的spring.mail.username和spring.mail.password必须和SMTP服务商要求一致。QQ邮箱和163邮箱都要求使用授权码而不是网页登录密码。如果你用公司内部邮箱可能还需要配置额外的SSL参数。遇到认证失败先检查配置文件的空格和特殊字符YAML文件里冒号后面必须有空格密码里有特殊字符建议加引号。第二类java.net.ConnectException连接超时。原因一般是服务器网络不允许访问外网SMTP端口。开发机直接跑没问题上了云服务器就超时这是最常见的场景。云服务商出于安全考虑默认封禁25端口你要么在安全组规则里放通465或587端口要么改用服务商的企业邮箱SMTP比如阿里云邮件推送、SendGrid这类服务。这里要说的直接一点如果你在云服务器上部署25端口基本别想用直接用465省事。第三类535 Errorauthentication failed。这是QQ邮箱很经典的报错。QQ邮箱的SMTP服务要求发件人地址必须和认证账号一致而且需要先确保网页端已经开启了SMTP服务。另外QQ邮箱还会拒绝发送包含明显垃圾邮件特征的邮件比如大量链接、敏感词所以激活邮件正文不要写得太像广告。第四类邮件发送成功但进垃圾箱。这不是报错但同样让人头大。如果你使用个人邮箱的SMTP发送激活邮件由于缺乏SPF、DKIM等域名认证记录邮件很容易被收件方的垃圾邮件过滤机制拦截。解决办法是正文不要出现“免费”“领奖”等敏感词发件人昵称设置成系统名称而非纯数字域名邮箱一定要配置SPF记录。如果是个人开发项目最好在正文里加一句“如未收到邮件请检查垃圾箱”的提示这是最务实的手段。4.2 激活链接失效的几种情况激活链接点了之后提示无效或过期这个问题我排查过很多次总结成下面的速查表症状可能原因解决方案提示“激活链接无效”激活码被清空或修改确认用户是否已经激活过激活后激活码会被置空提示“激活链接无效”数据库没有该记录检查激活码是否被URL编码/解码导致不一致提示“激活链接已过期”超过24小时有效期重新发送激活邮件或完善重新发送接口点击链接报404前端路由没配置对应页面检查Vue Router的activate页面路径URL编码问题很隐蔽。如果激活码本身只包含字母和数字UUID去横线后就是这种情况一般不会出现编码问题。但如果你生成的激活码带特殊字符邮件的链接拼接时没有URLEncode点击之后后端拿到的参数可能就不对了。建议激活码统一用UUID去掉横线的纯字母数字格式规避整个坑。另一个隐蔽问题出现在Linux服务器上。有些开发者在Windows本地用UUID生成激活码数据库里存的是小写到了Linux环境某个版本的JDK生成的是大写导致查不到记录。这个问题不常见但只要出现一次就够你查半天。解决方案是在生成激活码之后统一转成小写或者查询时大小写不敏感。4.3 跨域和CORS配置的坑前端报错“Access to XMLHttpRequest at http://localhost:8080/api/register from origin http://localhost:5173 has been blocked by CORS policy”说明后端跨域配置没生效。排查步骤先确认请求是否真的发出去了。F12打开浏览器Network面板如果请求状态是CORS error说明OPTIONS预检请求失败。确认SpringBoot的CorsConfig是否被扫描到。配置类放在启动类同包或子包下才能被ComponentScan扫描到放在其他包忘了加MapperScan或ComponentScan就会静默失效。确认是否有拦截器拦截了OPTIONS请求。如果你的项目里配置了登录拦截器或JWT拦截器注意放行OPTIONS请求因为浏览器跨域会先发一个OPTIONS预检请求被拦截后CORS配置再正确也会失败。SpringBoot的拦截器放行OPTIONS写法Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } // 其他拦截逻辑... return true; }4.4 测试中的其他细节重复注册同一个邮箱。我在user表上加了email唯一索引这是数据库层面的兜底。但注意如果你的注册接口是先查重再插入两个线程同时注册同一个邮箱时查重都通过了最终插入时后插入的那个会抛DuplicateKeyException。我的建议是先用查重做业务层校验再用唯一索引做最后兜底同时在接口层捕获DuplicateKeyException返回友好提示。未激活用户能不能登录这里取决于业务需求。你可以规定只有status1才能登录也可以允许登录但功能受限。这个项目里我做的登录逻辑是校验status未激活时提示“请先激活账号”。你可以在登录接口加同样的校验把提示信息写清楚。测试邮件发送的等待时间。用163或QQ邮箱发邮件通常几秒内就能收到。如果超过几十秒没收到先看后端日志有没有报错再看垃圾箱。生产环境建议引入简单的邮件发送状态表或日志方便排查。5. 代码结构、部署上线与后续扩展建议项目写完只是开始部署上线和扩展同样值得讲因为很多初学者代码本地能跑一到服务器就抓瞎。5.1 完整项目代码结构后端代码结构简单说一下方便你看项目时快速定位src/main/java/com/example/register/ ├── RegisterApplication.java ├── config/ │ └── CorsConfig.java ├── controller/ │ └── UserController.java ├── service/ │ ├── UserService.java │ ├── MailService.java │ └── impl/ │ └── UserServiceImpl.java ├── entity/ │ └── User.java ├── mapper/ │ └── UserMapper.java └── common/ └── Result.java前端结构src/ ├── api/ │ └── user.js ├── utils/ │ └── request.js ├── router/ │ └── index.js ├── views/ │ ├── Register.vue │ ├── Login.vue │ └── ActivateResult.vue └── App.vue新手可以对照这个结构自己从头搭一遍比直接看完整代码效果要好得多。不建议把这些文件放在一个zip里硬啃先厘清职责边界再动手。5.2 部署到服务器时要改哪些配置本地开发通过之后部署到服务器绝不是把jar包丢上去就完事。有几个配置我建议上线前统一检查后端打包。用Maven打jar包时确保测试代码不参与打包否则可能因为测试环境连不上数据库导致打包失败。执行mvn clean package -DskipTests。前端构建。Vue项目打包成静态文件产物在dist目录。你需要把dist目录里的文件通过Nginx托管并配置代理转发API请求server { listen 80; server_name your-domain.com; location / { root /opt/register-front/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files配置很重要如果缺失刷新前端页面路由时会404。这个坑我见过太多次了。数据库连接。本地用的MySQL和服务器上的MySQL版本要保持一致尤其是时区设置。连接串加上serverTimezoneAsia/Shanghai避免日期时间差8小时的问题。激活链接地址。激活链接里的base URL不能再用localhost改成你的服务器公网地址或域名否则用户点击邮件里的链接会指向他自己的机器。5.3 可以扩展的方向如果这个项目对你来说已经不够过瘾了往下延伸的思路很多邮件模板HTML化。用Thymeleaf或FreeMarker渲染邮件内容把激活链接做成按钮样式大幅提升用户体验和邮件打开率。重新发送激活邮件接口。激活码过期后提供输入邮箱、重新发送激活邮件的接口。这里要注意防刷同一个邮箱一天最多发几次。异步发送邮件。当前代码里注册接口是同步调用邮件服务如果SMTP服务器响应慢用户要等很久。可以用Spring Boot的Async注解把发邮件动作异步化注册接口快速返回邮件后台慢慢发。引入Redis存激活码。激活码不一定要存数据库可以存Redis并设置过期时间配合TTL自动删除。不过教学项目里存数据库更直观方便你观察数据变化。对接更多邮箱服务商。支持QQ、163、Gmail甚至阿里云邮件推送在配置层面做多租户邮件通道。这些扩展方向你随便选一个做深都能写在简历上作为项目亮点。写在最后的实操心得最后分享一点自己的真实感受。这类注册激活项目代码量不大但它把后端开发最常见的几个环节都串起来了数据库设计、接口编写、第三方服务集成邮件、前后端联调、部署上线。做一遍这个流程你对SpringBoot开发的理解会从“会写接口”上升到“能把一个功能完整落地”。我在实际做这个项目的过程中感受最深的一点是很多问题不是代码写错而是环境配置和细节逻辑没想清楚。比如授权码不是密码、激活链接要处理过期、跨域要处理OPTIONS预检这些内容不会写在你背的八股文里但实际开发遇到的概率极高。如果你照着这个项目做了一遍还是遇到问题不要急着怀疑代码先看日志。后端的控制台日志、前端的Network面板这两样东西能解决90%的联调问题。邮件收不到就去看SMTP的异常堆栈接口调不通就去看是不是跨域、是不是路径写错、是不是参数名不一致按这个思路排查基本不存在解决不了的问题。这套代码目前只是最基础的版本但结构干净、流程完整拿来理解“注册邮件激活”这条链路足够了。后面你有需要可以在这个基础上继续加功能比如Spring Security做认证授权、JWT做登录态管理等。本文还有配套的精品资源点击获取
返回列表