怎么维护好网站,怎么做婚介网站,小程序注册个人和企业有什么区别,网站搜索引擎优化技术使用 163 邮箱实现 Spring Boot 邮箱验证码登录
本文将详细介绍如何使用网易 163 邮箱作为 SMTP 邮件服务器#xff0c;实现 Spring Boot 项目中的邮件验证码发送功能#xff0c;并解决常见配置报错问题。 一、为什么需要邮箱授权码#xff1f;
出于安全考虑#xff0c;大…使用 163 邮箱实现 Spring Boot 邮箱验证码登录
本文将详细介绍如何使用网易 163 邮箱作为 SMTP 邮件服务器实现 Spring Boot 项目中的邮件验证码发送功能并解决常见配置报错问题。 一、为什么需要邮箱授权码
出于安全考虑大多数邮箱服务商如 163、QQ都不允许直接使用登录密码进行第三方邮件发送。 这时候你需要在邮箱设置中开启 SMTP服务 并生成 授权码 来代替密码使用。 二、163 邮箱如何开启第三方登录获取授权码
⚡ 步骤如下
登录网页版 163 邮箱 → 点击【设置】进入左侧【POP3/SMTP/IMAP】菜单勾选【开启客户端授权密码功能】在【授权密码管理】中点击【新建授权码】填入备注如“SpringBoot邮件服务”后生成授权码备份授权码用于项目配置 三、Spring Boot 项目中如何配置 163 邮箱发送邮件
✅ Maven 依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-mail/artifactId
/dependency✅ application.properties 配置
spring.mail.hostsmtp.163.com
spring.mail.port465
spring.mail.username你的邮箱163.com
spring.mail.password你的授权码
spring.mail.protocolsmtps
spring.mail.properties.mail.smtp.authtrue
spring.mail.properties.mail.smtp.ssl.enabletrue
spring.mail.default-encodingUTF-8四、邮件发送核心代码
✅ 工具类 EmailSender.java
Component
public class EmailSender {Autowiredprivate JavaMailSender mailSender;Value(${spring.mail.username})private String fromEmail;public boolean send(String to, String code) {try {SimpleMailMessage message new SimpleMailMessage();message.setFrom(fromEmail);message.setTo(to);message.setSubject(【Boounion 登录验证码】);message.setText(您的验证码是 code 5分钟内有效请勿泄露);mailSender.send(message);return true;} catch (Exception e) {e.printStackTrace();return false;}}
}五、常见错误及解决办法
错误信息原因解决方案Got bad greeting from SMTP host... EOF未启用 SSL 或端口错误使用 port465配置 ssl.enabletrueAuthentication failed使用了登录密码用授权码替换Connection timed out网络被墙打开 465 端口 / 更换网络Cannot send messagefrom 地址不一致setFrom() spring.mail.username 六、邮件验证码防刷优化
使用 Redis 实现“60 秒内同邮箱不能重复发送验证码”的控制避免恶意刷接口。
✅ UserService 代码示例
public boolean isInCooldown(String email) {String key email_code_cooldown: email;return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}public void markCooldown(String email) {redisTemplate.opsForValue().set(email_code_cooldown: email, 1, 60, TimeUnit.SECONDS);
}✅ Controller 中判断逻辑
if (userService.isInCooldown(email)) {return ResponseEntity.status(429).body(验证码已发送请稍后再试);
}七、邮件效果展示
验证码邮件示例
您的验证码是2113375分钟内有效请勿泄露八、总结
步骤状态163 邮箱开通 SMTP✅正确生成并使用授权码✅Spring Boot 邮件配置无误✅Redis 冷却控制防刷✅邮件内容格式清晰✅
以上配置完成后你就可以轻松实现一套完整、安全、可控的邮箱登录流程 附录完整文件可自行补全代码
Spring Boot 项目目录结构参考
src/main/java/org/example/
├── controller/
│ └── LoginController.java # 登录与验证码相关接口
├── model/
│ └── User.java # 用户模型类
├── service/
│ └── UserService.java # 登录逻辑与验证码缓存管理
├── util/
│ └── EmailSender.java # 邮件发送工具类
└── Main.java # SpringBoot 启动类src/main/resources/
├── static/index.html # 前端测试页面
└── application.properties # 邮件 Redis DB 配置项pom.xml ✅
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdorg.example/groupIdartifactIdBoounionERP/artifactIdversion1.0-SNAPSHOT/versionpackagingjar/packaging!-- Spring Boot 父项目 --parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion3.4.3/versionrelativePath//parentpropertiesmaven.compiler.source17/maven.compiler.sourcemaven.compiler.target17/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/propertiesdependencies!-- Spring Boot Web 模块包含内嵌 Tomcat --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- Spring Boot mail 模块 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-mail/artifactId/dependency!-- Spring Boot Redis 模块 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency!-- Spring Boot 开发工具模块 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdscoperuntime/scope/dependency!-- SQL Server JDBC 驱动 --dependencygroupIdcom.microsoft.sqlserver/groupIdartifactIdmssql-jdbc/artifactIdversion11.2.3.jre11/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build
/projectapplication.properties ✅
spring.datasource.urljdbc:sqlserver://localhost:1433;databaseNameBoounionDB;encrypttrue;trustServerCertificatetrue
spring.datasource.usernamesa
spring.datasource.passwordbl123456
spring.datasource.driver-class-namecom.microsoft.sqlserver.jdbc.SQLServerDriverspring.mail.hostsmtp.163.com
spring.mail.port465
spring.mail.username你的邮箱
spring.mail.password你的授权
spring.mail.protocolsmtps
spring.mail.properties.mail.smtp.authtrue
spring.mail.properties.mail.smtp.ssl.enabletrue
spring.mail.default-encodingUTF-8spring.data.redis.hostlocalhost
spring.data.redis.port6379spring.jpa.hibernate.ddl-autonone
server.port8080Main.java ✅
package org.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** * This class Main is responsible for [功能描述].** author darker* version 1.0* */SpringBootApplication
public class Main {public static void main(String[] args) {SpringApplication.run(Main.class, args);}
}LoginController.java ✅
package org.example.controller;import org.example.model.User;
import org.example.service.UserService;
import org.example.util.EmailSender;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;
import java.util.Random;/*** * This class LoginController is responsible for [功能描述].** author darker* version 1.0* */RestController
RequestMapping(/api)
public class LoginController {private final UserService userService;private final EmailSender emailSender;public LoginController(UserService userService, EmailSender emailSender) {this.userService userService;this.emailSender emailSender;}PostMapping(/login)public ResponseEntityMapString, Object login(RequestBody User user) {String result userService.login(user);MapString, Object response new HashMap();switch (result) {case 登录成功:// 拼接带参数的跳转地址String url https://www.baidu.com/s?wdcsdn;response.put(status, success);response.put(redirectUrl, url);return ResponseEntity.ok(response);case 密码错误:response.put(status, error);response.put(message, 密码错误);return ResponseEntity.status(401).body(response);case 用户不存在:response.put(status, error);response.put(message, 用户不存在);return ResponseEntity.status(404).body(response);default:response.put(status, error);response.put(message, 服务器异常);return ResponseEntity.status(500).body(response);}}PostMapping(/login/code)public ResponseEntity? sendEmailCode(RequestBody MapString, String payload) {String email payload.get(email);if (email null || email.isEmpty()) {return ResponseEntity.badRequest().body(邮箱不能为空);}if (!userService.isEmailRegistered(email)) {return ResponseEntity.status(404).body(该邮箱未注册);}// 检查是否在冷却期例如60秒if (userService.isInCooldown(email)) {return ResponseEntity.status(429).body(验证码已发送请稍后再试);}// 生成6位验证码String code String.format(%06d, new Random().nextInt(999999));boolean success emailSender.send(email, code);if (success) {userService.saveEmailCode(email, code);userService.markCooldown(email);return ResponseEntity.ok(验证码发送成功);} else {return ResponseEntity.status(500).body(验证码发送失败);}}PostMapping(/login/email)public ResponseEntity? loginWithEmailCode(RequestBody MapString, String payload) {String email payload.get(email);String code payload.get(code);if (email null || code null) {return ResponseEntity.badRequest().body(邮箱或验证码不能为空);}if (!userService.isEmailRegistered(email)) {return ResponseEntity.status(404).body(该邮箱未注册);}if (!userService.verifyEmailCode(email, code)) {return ResponseEntity.status(401).body(验证码错误或已过期);}MapString, Object response new HashMap();response.put(status, success);response.put(message, 登录成功);return ResponseEntity.ok(response);}
}User.java ✅
package org.example.model;/*** * This class User is responsible for [功能描述].** author darker* version 1.0* */public class User {private String username;private String password;private String email;public User() {}public User(String username, String password) {this.username username;this.password password;}public String getUsername() { return username; }public void setUsername(String username) { this.username username; }public String getPassword() { return password; }public void setPassword(String password) { this.password password; }public String getEmail() { return email; }public void setEmail(String email) { this.email email; }
}UserService.java ✅
package org.example.service;import org.example.model.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;import java.sql.*;/*** * This class UserService is responsible for [功能描述].** author darker* version 1.0* */Service
public class UserService {Value(${spring.datasource.url})private String dbUrl;Value(${spring.datasource.username})private String dbUser;Value(${spring.datasource.password})private String dbPassword;private static final long CODE_COOLDOWN_SECONDS 60;private final StringRedisTemplate redisTemplate;public UserService(StringRedisTemplate redisTemplate) {this.redisTemplate redisTemplate;}// 保存验证码到 Redis5 分钟有效public void saveEmailCode(String email, String code) {String key email_code: email;redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);}// 验证验证码是否正确public boolean verifyEmailCode(String email, String code) {String key email_code: email;String cachedCode redisTemplate.opsForValue().get(key);return code.equals(cachedCode);}// 判断邮箱是否注册public boolean isEmailRegistered(String email) {try (Connection conn DriverManager.getConnection(dbUrl, dbUser, dbPassword)) {String sql SELECT COUNT(*) FROM Users WHERE email ?;try (PreparedStatement stmt conn.prepareStatement(sql)) {stmt.setString(1, email);try (ResultSet rs stmt.executeQuery()) {rs.next();return rs.getInt(1) 0;}}} catch (SQLException e) {e.printStackTrace();return false;}}public boolean isInCooldown(String email) {String key email_code_cooldown: email;return redisTemplate.hasKey(key);}public void markCooldown(String email) {String key email_code_cooldown: email;redisTemplate.opsForValue().set(key, 1, CODE_COOLDOWN_SECONDS, TimeUnit.SECONDS);}public String login(User user) {try (Connection conn DriverManager.getConnection(dbUrl, dbUser, dbPassword)) {String checkUserSql SELECT password FROM Users WHERE username ?;try (PreparedStatement stmt conn.prepareStatement(checkUserSql)) {stmt.setString(1, user.getUsername());try (ResultSet rs stmt.executeQuery()) {if (!rs.next()) {return 用户不存在;}String dbPassword rs.getString(password);if (!dbPassword.equals(user.getPassword())) {return 密码错误;}return 登录成功;}}} catch (SQLException e) {e.printStackTrace();return 数据库错误;}}
}EmailSender.java ✅
package org.example.util;import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Value;/*** * This class EmailSender is responsible for [功能描述].** author draker* version 1.0* */Component
public class EmailSender {private final JavaMailSender mailSender;Value(${spring.mail.username})private String fromEmail;public EmailSender(JavaMailSender mailSender) {this.mailSender mailSender;}/*** 发送验证码邮件* param to 收件人邮箱* param code 验证码内容* return true发送成功false失败*/public boolean send(String to, String code) {try {SimpleMailMessage message new SimpleMailMessage();message.setFrom(fromEmail);message.setTo(to);message.setSubject(【Boounion 登录验证码】);message.setText(您的验证码是 code 5分钟内有效请勿泄露);mailSender.send(message);return true;} catch (Exception e) {System.err.println(邮件发送失败: e.getMessage());return false;}}
}