基于若依的邮件登陆功能
一、数据源与邮件配置(application.yml
)
1. 邮件服务配置(QQ 邮箱为例)
spring:mail:from: XXXXXXXXXXX@qq.com # 发件人邮箱(需与username一致)host: smtp.qq.com # QQ邮箱SMTP服务器地址username: XXXXXXXXXXX@qq.com # 邮箱账号password: XXXXXXXXXXXXXXXX # SMTP授权码(非邮箱密码,需在QQ邮箱后台申请)port: 465 # SSL加密端口(QQ邮箱固定为465或587)properties:mail:smtp:socketFactory:class: javax.net.ssl.SSLSocketFactory # 使用SSL套接字工厂ssl:trust: smtp.qq.com # 信任的SSL主机auth: true # 启用认证starttls:enable: true # 启用TLS加密required: true # 强制TLS
- 关键配置:
port: 465
+SSLSocketFactory
确保邮件通过 SSL 加密传输。auth: true
开启用户认证,starttls
增强传输安全性。from
必须与username
一致,否则 QQ 邮箱会拒绝发送。
配置流程
获取并使用 QQ 邮箱授权码
1. 生成授权码
- 步骤 1:登录 QQ 邮箱网页版(https://mail.qq.com)。
- 步骤 2:点击右上角的 设置 → 账户。
- 步骤 3:在 “账户安全” 或 “POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务” 中,找到 生成授权码 选项(可能需要先启用 POP3/IMAP 服务)。
- 若未启用服务,系统会提示先开启(如 “开启 IMAP/SMTP 服务”),按提示通过短信验证或密保工具完成验证。
- 步骤 4:验证通过后,系统会生成一个16 位的授权码(例如:
abcdefgh12345678
),请妥善保存。
2. 使用授权码替代密码
在你的代码或邮件客户端中,将原本输入邮箱密码的位置,替换为刚刚生成的授权码。
二、后端核心代码逻辑
1. 邮件登录控制器(EmailLoginController
)
@RestController
@RequestMapping("/email")
public class EmailLoginController {@Autowired private EmailUtils emailUtils;@Autowired private SysEmailLoginService sysEmailLoginService;@Autowired private RedisCache redisCache;// 发送邮箱验证码@GetMapping("/sendLoginCode")public AjaxResult sendLoginCode(@RequestParam String to) {String code = generateVerifyCode(); // 生成6位随机数String verifyKey = "email-login-code:" + to;redisCache.setCacheObject(verifyKey, code); // 存入Redis(默认有效期由Redis配置决定)emailUtils.sendSimpleEmail(to, "登录验证码", code); // 发送邮件return AjaxResult.success("验证码发送成功!");}// 邮箱登录接口@PostMapping("/login")public AjaxResult login(@RequestBody MailLoginBody body) {String token = sysEmailLoginService.login(body.getEmail(), body.getCode()); // 验证邮箱+验证码return AjaxResult.success().put(Constants.TOKEN, token); // 返回JWT令牌}private String generateVerifyCode() {return String.valueOf(100000 + new SecureRandom().nextInt(900000)); // 生成100000-999999的随机数}
}
一、整体功能概述
这个控制器提供了两个核心接口:
- 发送验证码接口:生成随机验证码,存储到 Redis,并通过邮件发送给用户
- 邮箱登录接口:验证用户输入的邮箱和验证码,生成 JWT 令牌
二、核心组件与依赖
@RestController
@RequestMapping("/email")
public class EmailLoginController {@Autowired private EmailUtils emailUtils; // 邮件发送工具类@Autowired private SysEmailLoginService service; // 邮箱登录业务逻辑@Autowired private RedisCache redisCache; // Redis缓存操作工具
}
- EmailUtils:封装邮件发送逻辑(如调用 JavaMailSender)
- SysEmailLoginService:处理登录核心业务(验证码验证、用户认证)
- RedisCache:缓存验证码,设置有效期(防重复使用)
三、发送验证码接口
@GetMapping("/sendLoginCode")
public AjaxResult sendLoginCode(@RequestParam String to) {String code = generateVerifyCode(); // 生成6位随机数String verifyKey = "email-login-code:" + to;redisCache.setCacheObject(verifyKey, code); // 存入RedisemailUtils.sendSimpleEmail(to, "登录验证码", code); // 发送邮件return AjaxResult.success("验证码发送成功!");
}
关键点:
-
验证码生成:
private String generateVerifyCode() {// 生成100000-999999的随机数(6位数字)return String.valueOf(100000 + new SecureRandom().nextInt(900000)); }
- 使用
SecureRandom
(强随机数生成器)比Random
更安全 - 范围控制确保生成的数字固定为 6 位
- 使用
-
Redis 存储:
- Key 格式:
email-login-code:用户邮箱
(避免不同用户冲突)
- Key 格式:
-
邮件发送:
- 调用封装好的工具类发送纯文本邮件
- 邮件内容直接包含验证码(实际项目建议增加 HTML 模板)
四、邮箱登录接口
@PostMapping("/login")
public AjaxResult login(@RequestBody MailLoginBody body) {String token = service.login(body.getEmail(), body.getCode());return AjaxResult.success().put(Constants.TOKEN, token);
}
关键点:
-
请求体结构:
public class MailLoginBody {private String email;private String code;// getters/setters }
-
业务逻辑:
// SysEmailLoginService.login() 可能的实现 public String login(String email, String code) {// 1. 从Redis获取验证码String verifyKey = "email-login-code:" + email;String cachedCode = redisCache.getCacheObject(verifyKey);// 2. 校验验证码if (!code.equals(cachedCode)) {throw new BusinessException("验证码错误");}// 3. 删除已使用的验证码(防复用)redisCache.deleteObject(verifyKey);// 4. 用户存在性校验(从数据库查询)User user = userService.getUserByEmail(email);if (user == null) {// 处理新用户注册逻辑(可选)user = userService.registerByEmail(email);}// 5. 生成JWT令牌return jwtService.generateToken(user.getId()); }
2. 邮箱登录服务(SysEmailLoginService
)
@Component
public class SysEmailLoginService {@Autowired private RedisCache redisCache;@Autowired private ISysUserService userService;public String login(String email, String code) {validateEmailCaptcha(email, code); // 校验验证码SysUser user = userService.selectUserByEmail(email); // 通过邮箱查询用户if (user == null) throw new UserNotExistsException("邮箱未注册"); // 用户不存在校验// 后续流程:Spring Security认证、生成JWT令牌(与账号密码登录一致)return tokenService.createToken(loginUser); // 生成并返回令牌}private void validateEmailCaptcha(String email, String code) {String verifyKey = "email-login-code:" + email;String cacheCode = redisCache.getCacheObject(verifyKey);if (cacheCode == null) throw new CaptchaExpireException("验证码过期"); // 有效期校验if (!code.equals(cacheCode)) throw new CaptchaException("验证码错误"); // 正确性校验redisCache.deleteObject(verifyKey); // 验证后删除Redis中的验证码,防止重复使用}
}
实现了基于邮箱验证码的登录服务逻辑,是典型的无密码登录方案。
一、核心功能概述
SysEmailLoginService
负责处理邮箱验证码登录的核心业务逻辑,主要包括:
- 验证码校验:从 Redis 获取缓存的验证码并验证
- 用户存在性检查:通过邮箱查询用户是否存在
- 令牌生成:验证通过后生成 JWT 令牌
二、依赖组件分析
@Component
public class SysEmailLoginService {@Autowired private RedisCache redisCache; // Redis缓存操作@Autowired private ISysUserService userService; // 用户服务接口@Autowired private TokenService tokenService; // JWT令牌服务(代码中未显式注入,但方法中使用)
}
- RedisCache:用于存储和获取验证码(缓存有效期通常设置为 5-10 分钟)
- ISysUserService:与用户数据库交互,查询用户信息
- TokenService:生成和管理 JWT 令牌(典型实现如
jjwt
库)
三、登录主流程解析
public String login(String email, String code) {validateEmailCaptcha(email, code); // 校验验证码(核心逻辑)SysUser user = userService.selectUserByEmail(email);if (user == null) throw new UserNotExistsException("邮箱未注册");// 构建登录用户对象(包含用户信息和权限)LoginUser loginUser = new LoginUser(user.getId(), user.getUsername(), ...);// 生成并返回JWT令牌(包含用户ID等信息)return tokenService.createToken(loginUser);
}
关键点:
- 验证码校验:调用私有方法
validateEmailCaptcha
完成 - 用户查询:通过邮箱查询数据库中的用户记录
- 用户注册逻辑:当前代码未处理新用户注册(邮箱不存在时直接抛异常)
- 实际项目中可能需要添加自动注册逻辑(如首次登录自动创建账号)
四、验证码校验逻辑
private void validateEmailCaptcha(String email, String code) {String verifyKey = "email-login-code:" + email;String cacheCode = redisCache.getCacheObject(verifyKey);// 1. 校验验证码是否存在(是否过期)if (cacheCode == null) throw new CaptchaExpireException("验证码过期");// 2. 校验验证码是否正确(忽略大小写)if (!code.equals(cacheCode)) throw new CaptchaException("验证码错误");// 3. 验证通过后立即删除验证码(防止重复使用)redisCache.deleteObject(verifyKey);
}
流程
用户 前端 后端 Redis 邮件服务器| | | | || 输入邮箱| | | ||------->| | | || |/sendCode| | || |------->| | || | |生成验证码 | || | |存入Redis |<-----------|| | |调用邮件服务 | || | |----------->| || |<-------| | || | | | || 输入验证码| | | ||------->| | | || |/login | | || |------->| | || | |从Redis获取 | || | |验证码 |<-----------|| | |验证 | || | |查询用户 | || | |生成JWT令牌 | || |<-------| | ||<-------| | | |
3. 邮件工具类(EmailUtils
)
@Component
public class EmailUtils {@Resource private MailConfig mailConfig;@Value("${spring.mail.username}") private String from;public void sendSimpleEmail(String to, String subject, String content) {SimpleMailMessage message = new SimpleMailMessage();message.setFrom(from); // 发件人message.setTo(to); // 收件人message.setSubject(subject); // 主题message.setText(content); // 内容(纯文本)mailConfig.getMailSender().send(message); // 调用JavaMail发送}
}
三、前端交互(邮箱登录页面)
1. 页面结构(Vue + Element UI)
<el-tab-pane label="邮箱登录" name="email"><el-form ref="emailForm" :model="emailForm" :rules="emailRules"><el-input v-model="emailForm.email" placeholder="请输入邮箱" /><el-input v-model="emailForm.emailCode" placeholder="请输入验证码" style="width: 63%"><el-button type="primary" :disabled="emailCodeBtnDisabled" @click="getEmailCode">{{ emailCodeBtnText }}</el-button></el-input><el-button @click="handleLogin">登录</el-button></el-form>
</el-tab-pane>
- 邮箱输入框:用于输入注册邮箱
- 验证码输入框:右侧附带获取验证码按钮
- 登录按钮:提交邮箱和验证码进行登录验证
2. 验证码发送逻辑(前端)
methods: {getEmailCode() {this.$refs.emailForm.validateField("email", (error) => {if (!error) { // 邮箱格式正确sendEmailCode({ to: this.emailForm.email }).then(() => {this.$message.success("验证码已发送");this.startCountdown(); // 启动60秒倒计时}).catch(() => {this.$message.error("发送失败,请重试");});}});},startCountdown() {let countdown = 60;this.emailCodeBtnDisabled = true;this.emailCodeBtnText = "60秒后重试";const timer = setInterval(() => {countdown--;if (countdown <= 0) {clearInterval(timer);this.emailCodeBtnDisabled = false;this.emailCodeBtnText = "获取验证码";} else {this.emailCodeBtnText = `${countdown} 秒后重试`;}}, 1000);}
}
3. 登录提交逻辑
handleLogin() {this.$refs.emailForm.validate(valid => {if (valid) {this.$store.dispatch("EmailLogin", { // 调用Vuex中的邮箱登录逻辑email: this.emailForm.email,code: this.emailForm.emailCode}).then(() => {this.$router.push("/index"); // 登录成功跳转首页}).catch(() => {// 处理登录失败(如验证码错误、邮箱未注册)});}});
}
- 与后端交互:
- 通过
$store.dispatch
调用后端/email/login
接口,传递邮箱和验证码。 - 成功后存储 JWT 令牌(通过
setToken
方法),并跳转页面。
- 通过