要在Spring Boot的登录注册流程中添加邮箱验证,通常步骤如下:
- 用户注册时发送验证码邮件: 在用户注册时,你可以生成一个验证码(或一个验证链接),并将其通过邮件发送给用户。
- 用户点击邮件中的验证链接: 用户收到邮件并点击验证链接后,后台会检查验证码是否有效,如果有效,就将用户的状态标记为已验证。
- 邮箱验证后的登录: 只有邮箱验证通过的用户才能登录。
步骤 1:添加依赖
首先,确保你的项目中已经引入了 Spring Boot 的邮件库。在 build.gradle 文件中添加以下依赖:
dependencies {
implementation 'javax.mail:javax.mail-api:1.6.2'
implementation 'org.springframework.boot:spring-boot-starter-mail'
}
步骤 2:添加邮件发送功能
接下来,需要配置邮件发送功能。你可以使用 Spring Boot 的 JavaMailSender 来发送邮件。
- 在 application.properties 中配置邮件服务器(这里我使用的是qq邮箱服务器,可以根据自己的需要配置其它的邮箱服务器):
spring.mail.host=smtp.qq.com
spring.mail.port=587
spring.mail.username=xx@qq.com//邮箱账号
spring.mail.password=xxxx//邮箱密码|授权码
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
- 创建一个邮件发送服务:
package com.example.demo.service;
import jakarta.mail.internet.MimeMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.MailException;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
@Service
public class EmailService {
private final JavaMailSender mailSender;
public EmailService(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void sendVerificationEmail(String to, String token) throws MailException, MessagingException, jakarta.mail.MessagingException {
String subject = "Please verify your email address";
String text = "Click the following link to verify your email: " +
"http://localhost:8080/verify?token=" + token;
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom("xx@qq.com");
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text);
mailSender.send(message);
}
}
步骤 3:更新实体类以支持邮箱功能
为其添加带参构造函数以及email verificationToken verified字段:
package com.example.demo.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Size;
@Entity
@Table(name = "users")
public class User {
// 无参构造函数
public User() {}
// 带参构造函数
public User(String email, String password, String verificationToken, boolean verified) {
this.email = email;
this.password = password;
this.verificationToken = verificationToken;
this.verified = verified;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
@Size(min=2, max=20, message = "username must be at least 2 characters long and at most 20 characters")
private String username;
@Column(nullable = false)
@Size(min=2, max=20, message = "password must be at least 2 characters long and at most 20 characters")
private String password;
@Column(nullable = false, unique = true)
@Email(message = "Wrong email format")
private String email;
private String verificationToken;
private boolean verified;
getter and setter...
步骤 4:生成Token并保存
更改用户登陆注册服务UserService以生成Token并将其存储在数据库中,确保它有一定的有效期。
package com.example.demo.service;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.UUID;
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final EmailService emailService;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, EmailService emailService) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.emailService = emailService;
}
public void registerUser(String email, String username, String password) {
if (userRepository.findByUsername(username).isPresent()) {
throw new RuntimeException("Username is already taken.");
}
if (userRepository.findByEmail(email).isPresent()) {
throw new RuntimeException("Email is already taken.");
}
// 1. 创建用户实体,存储到数据库
String token = UUID.randomUUID().toString();
User user = new User(email, username, passwordEncoder.encode(password), token, false); // false 表示未验证
userRepository.save(user);
// 2. 发送验证邮件
try {
emailService.sendVerificationEmail(email, token);
} catch (Exception e) {
// 处理邮件发送失败的情况
e.printStackTrace();
}
}
public Boolean loginUser(String username, String password) {
...
}
}
步骤 5:更新UserRepository
为其增加findByVerificationToken()方法
package com.example.demo.repository;
import com.example.demo.model.User;
import org.springframework.data.repository.CrudRepository;
//@Repository
public interface UserRepository extends CrudRepository<User, Long> {
...
User findByVerificationToken(String username);
}
步骤 6:验证邮件链接
在收到用户点击验证链接后,后台根据URL中的token查找用户并更新其状态。
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.stereotype.Controller;
@Controller
public class VerificationController {
private final UserRepository userRepository;
public VerificationController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/verify")
public String verifyEmail(@RequestParam("token") String token) {
User user = userRepository.findByVerificationToken(token);
if (user != null && !user.isVerified()) {
user.setVerified(true);
user.setVerificationToken(null); // 清除 token
userRepository.save(user);
return "redirect:/login"; // 返回验证成功的页面
} else {
return "redirect:/error"; // 验证失败或已验证的情况
}
}
}
步骤 7:用户登录时检查邮箱验证状态
在用户登录时,检查用户是否已经验证邮箱,如果没有验证则拒绝登录。
package com.example.demo.service;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.stereotype.Service;
import java.util.Optional;
import ...
@Service
public class UserService {
private final UserRepository userRepository;
...
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, EmailService emailService) {
this.userRepository = userRepository;
...
}
public void registerUser(String email, String username, String password) {
...
}
public Boolean loginUser(String username, String password) {
Optional<User> user = userRepository.findByUsername(username);
if (user.isPresent() && passwordEncoder.matches(password, user.get().getPassword()) && user.get().isVerified()) {
return true;
};
return false;
}
}