今天我們來看看如何用 Spring Boot 簡易搭建一個郵箱驗證接口。
許多的網站在註冊賬號之後,都會發送一封郵件到註冊郵箱裏,而用戶需要到郵箱裏打開這封驗證郵件,並點擊郵件裏的鏈接,以向網站證明自己爲該郵箱的擁有者。
1. 配置郵箱參數
爲了讓 Spring Boot 在用戶註冊成功的時候發送郵件,我們需要先添加一個郵箱服務依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
我們需要配置一個郵箱服務來幫我們發送驗證郵箱的郵件,但在配置郵箱參數之前,我們要先決定使用哪一個郵箱服務。這裏我們以 QQ 郵箱爲例子,朋友們也可以搭建自己的郵箱服務器。
登錄 QQ 郵箱之後,點擊 設置 -> 賬戶,打開 “POP3/SMTP服務”,接着取得第三方客戶端授權碼。
接着我們打開 application.properties 文件來進行配置:
spring.mail.host=smtp.qq.com
spring.mail.protocol=smtp
spring.mail.username= # 填寫你的發件郵箱地址
spring.mail.password= # 填寫第三方客戶端授權碼
spring.mail.default-encoding=UTF-8
spring.mail.port=465
# 此爲郵件加密服務
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
2. 創建實體類
當用戶完成註冊之後,我們需要在郵箱驗證表裏創建一條記錄使用戶的郵箱與一個唯一的加密 token 匹配,以提升之後驗證郵箱時的安全性。
用戶需要點擊帶有該 token 參數的網址,接着我們會檢查這個表,token 覈對成功之後纔算是成功驗證郵箱。
創建一個郵箱驗證表實體類:
@Entity
@Table(name = "email_verifier")
public class EmailVerifier {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String email;
private int userId;
// 驗證用的 token
private String token;
// getters and setters
}
接着創建一個用戶實體類。我們需要給用戶實體類一個 boolean 值的屬性,代表其郵箱是否已經通過驗證。默認爲 false,因爲剛註冊成功的時候郵箱肯定是還沒有通過驗證的。
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String email;
// 郵箱是否成功驗證,默認爲 false
@Column(name = "is_email_verified", columnDefinition = "boolean default false")
private boolean isEmailVerified;
@JsonIgnore
private String password;
// getters and setters
}
3. 創建倉庫類
我們需要寫一個簡單的用戶倉庫類
@Repository
@Transactional
public class UserRepositoryImpl implements UserRepository {
@PersistenceContext
private EntityManager entityManager;
// 將新用戶插入用戶表
public void addUser(String email, String password) {
User user = new User();
entityManager.persist(user);
user.setEmail(email);
user.setPassword(password);
entityManager.flush();
}
// 將用戶表裏的郵箱是否已驗證屬性設爲 true
public void setUserEmailVerified(int userId) {
User user = findById(userId);
entityManager.persist(user);
user.setEmailVerified(true);
entityManager.flush();
}
}
接着再寫一個簡單的郵箱倉庫類
@Repository
@Transactional
public class EmailRepositoryImpl implements EmailRepository {
@PersistenceContext
private EntityManager entityManager;
// 往郵箱驗證表添加新註冊用戶郵箱
public void addEmailVerifier(int userId, String email, String token) {
EmailVerifier emailVerifier = new EmailVerifier();
entityManager.persist(emailVerifier);
emailVerifier.setUserId(userId);
emailVerifier.setEmail(email);
emailVerifier.setToken(token);
entityManager.flush();
}
// 根據用戶 id 取得郵箱驗證表數據
public EmailVerifier getEmailVerifierByUserId(int userId) {
EmailVerifier emailVerifier = entityManager.createQuery("SELECT e FROM EmailVerifier e WHERE e.userId = :userId", EmailVerifier.class)
.setParameter("userId", userId)
.getSingleResult();
return emailVerifier;
}
// 根據 token 取得郵箱驗證表數據
public EmailVerifier getEmailVerifierByToken(String token) {
EmailVerifier emailVerifier = entityManager.createQuery("SELECT e FROM EmailVerifier e WHERE e.token = :token", EmailVerifier.class)
.setParameter("token", token)
.getSingleResult();
return emailVerifier;
}
}
4. 創建服務類
由於當我們向 QQ 郵箱發送請求時,整個線程需要等待 QQ 郵箱服務器返回發送結果,因此會導致線程堵塞,所以我們可以使用多線程來對 QQ 郵箱發送異步請求。
首先我們需要創建一個線程池配置類:
@EnableAsync // 使用該註解,這樣 Spring Boot 纔會掃描 @Async 註解
@Configuration
public class TaskPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("task-executor");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(600);
return executor;
}
}
接着在我們的郵箱業務邏輯中,我們會需要使用到 @Async 註解。
編寫一個郵箱服務類
@Service
public class EmailServiceImpl implements EmailService {
@Autowired
private MailSender mailSender;
@Autowired
private EmailRepository emailRepository;
@Autowired
private UserRepository userRepository;
@Async("taskExecutor")
@Override
public void sendVerificationEmail(int userId) {
try {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
EmailVerifier emailVerifier = getEmailVerifierByUserId(userId);
String email = emailVerifier.getEmail();
String token = emailVerifier.getToken();
String text = "請點擊鏈接驗證郵箱: https://此處輸入你的主服務器地址/api/v1/user/email_verification?token=" + token;
simpleMailMessage.setTo(email);
simpleMailMessage.setSubject("Email Verification");
simpleMailMessage.setFrom("[email protected]"); // 填寫你的 QQ 郵箱地址
simpleMailMessage.setText(text);
mailSender.send(simpleMailMessage);
} catch(Exception e) {
e.printStackTrace();
}
}
public void addEmailVerifier(int userId, String email, String token) {
emailRepository.addEmailVerifier(userId, email, token);
}
public EmailVerifier getEmailVerifierByUserId(int userId) {
return emailRepository.getEmailVerifierByUserId(userId);
}
public EmailVerifier getEmailVerifierByToken(String token) {
EmailVerifier emailVerifier = emailRepository.getEmailVerifierByToken(token);
return emailVerifier;
}
public void setUserEmailVerified(int userId) {
userRepository.setUserEmailVerified(userId);
}
}
這裏我們使用 JWT 來作爲我們的 token,因此我們需要添加 JWT 依賴
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
然後我們寫一個用戶的服務類
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private EmailService emailService;
// 這是生成 token 的密鑰
private String tokenSecret = "my-secret-string";
// 添加新用戶
public void addUser(String email, String password) {
User user = userRepository.addUser(email, password);
int userId = user.getId();
String token = token = JWT.create().sign(Algorithm.HMAC256(tokenSecret));
emailService.addEmailVerifier(userId, email, token);
emailService.sendVerificationEmail(userId);
}
}
5. 編寫 REST 接口
我們還需要寫 REST 接口來處理用戶註冊與驗證郵箱的請求。
@CrossOrigin
@RestController
@RequestMapping(value = "/api/v1/user")
public class UserController {
@Autowired
private EmailService emailService;
@Autowired
private UserService userService;
// 用戶註冊 REST 接口
@PostMapping(value = "/signup")
public ResponseEntity<Object> signUp(@RequestBody Map<String, String> payload) {
JSONObject result = new JSONObject();
String email = payload.get("email");
String password = payload.get("password");
JSONObject addUserResult = userService.addUser(email, password);
return ResponseEntity.ok().build();
}
// 用戶驗證郵箱 REST 接口
@GetMapping(value = "/email_verification")
public ResponseEntity<Object> verifyEmail(HttpServletRequest request) {
String token = request.getParameter("token");
EmailVerifier emailVerifier = emailService.getEmailVerifierByToken(token);
if (emailVerifier != null) {
int userId = emailVerifier.getUserId();
emailService.setUserEmailVerified(userId);
return ResponseEntity.ok().body("郵箱驗證成功");
} else {
return ResponseEntity.badRequest().build();
}
}
}
大功告成!讓我們來測測實際效果。
實際效果
首先通過註冊 REST API 註冊一個賬戶。
可以使用 Postman 發送註冊請求:
// 你的主機地址/api/v1/user/signup
{
"email": "填寫你的郵箱",
"password": "123123"
}
接着就會收到一封郵箱驗證郵件。
點擊鏈接之後就會看到
接着我們到 Users 表查看用戶的郵箱驗證情況,已經從 false 變爲 true了。
至此,一個簡單的郵箱驗證系統就搭建完了。