0%

Spring Boot2 实战系列之登录注册(三) - 邮件激活账号和密码重置

前言

在前面的博文 Spring Boot2 实战系列之登录注册(二) - 登录实现 中实现了登录功能。这次来增加一个功能,就是在注册的时候可以向注册邮箱发送一个链接,打开该链接才能激活该账户。

项目架构

项目结构图如下:

pom 依赖如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>top.yekongle</groupId>
<artifactId>springboot-activate-account-sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-activate-account-sample</name>
<description>Activate account by email for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<passay.version>1.5.0</passay.version>
<guava.version>29.0-jre</guava.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.passay</groupId>
<artifactId>passay</artifactId>
<version>${passay.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

代码编写

application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring.mail.host=smtp.163.com
spring.mail.username=your_username
spring.mail.password=your_password
spring.mail.default-encoding=UTF-8
spring.mail.protocol=smtp
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.ssl.enable=true
support.email=your_username

# 国际化i18n配置,(包名.基础名)
spring.messages.basename=i18n.messages
spring.messages.encoding=UTF-8


# Thymeleaf 配置
# 禁止缓存
spring.thymeleaf.cache=false

这里主要写出改动或新增的类,其他的则和登录实现篇基本一致, 具体请查看仓库

PasswordResetToken.java, 密码重置校验 Token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package top.yekongle.activate.entity;

import java.util.Calendar;
import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

@Entity
public class PasswordResetToken {

private static final int EXPIRATION = 60 * 24;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String token;

@OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)
@JoinColumn(nullable = false, name = "user_id")
private User user;

private Date expiryDate;

public PasswordResetToken() {
super();
}

public PasswordResetToken(final String token) {
super();

this.token = token;
this.expiryDate = calculateExpiryDate(EXPIRATION);
}

public PasswordResetToken(final String token, final User user) {
super();

this.token = token;
this.user = user;
this.expiryDate = calculateExpiryDate(EXPIRATION);
}

//
public Long getId() {
return id;
}

public String getToken() {
return token;
}

public void setToken(final String token) {
this.token = token;
}

public User getUser() {
return user;
}

public void setUser(final User user) {
this.user = user;
}

public Date getExpiryDate() {
return expiryDate;
}

public void setExpiryDate(final Date expiryDate) {
this.expiryDate = expiryDate;
}

private Date calculateExpiryDate(final int expiryTimeInMinutes) {
final Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(new Date().getTime());
cal.add(Calendar.MINUTE, expiryTimeInMinutes);
return new Date(cal.getTime().getTime());
}

public void updateToken(final String token) {
this.token = token;
this.expiryDate = calculateExpiryDate(EXPIRATION);
}

//

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((expiryDate == null) ? 0 : expiryDate.hashCode());
result = prime * result + ((token == null) ? 0 : token.hashCode());
result = prime * result + ((user == null) ? 0 : user.hashCode());
return result;
}

@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PasswordResetToken other = (PasswordResetToken) obj;
if (expiryDate == null) {
if (other.expiryDate != null) {
return false;
}
} else if (!expiryDate.equals(other.expiryDate)) {
return false;
}
if (token == null) {
if (other.token != null) {
return false;
}
} else if (!token.equals(other.token)) {
return false;
}
if (user == null) {
if (other.user != null) {
return false;
}
} else if (!user.equals(other.user)) {
return false;
}
return true;
}

@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Token [String=").append(token).append("]").append("[Expires").append(expiryDate).append("]");
return builder.toString();
}

}

UserController.java, 注册用户,激活账号,重置密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
package top.yekongle.activate.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.MessageSource;
import org.springframework.core.env.Environment;
import org.springframework.mail.MailAuthenticationException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ModelAndView;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
import top.yekongle.activate.dto.PasswordDto;
import top.yekongle.activate.dto.UserDTO;
import top.yekongle.activate.entity.PasswordResetToken;
import top.yekongle.activate.entity.User;
import top.yekongle.activate.entity.VerificationToken;
import top.yekongle.activate.event.OnRegistrationCompleteEvent;
import top.yekongle.activate.exception.UserAlreadyExistException;
import top.yekongle.activate.service.UserService;
import top.yekongle.activate.util.GenericResponse;

import java.util.Calendar;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;

/**
* @Author: Yekongle
* @Date: 2020年5月5日
*/
@Slf4j
@Controller
public class UserController {

@Autowired UserService userService;

@Autowired
private MessageSource messages;

@Autowired
LocaleResolver localeResolver;

@Autowired
ApplicationEventPublisher eventPublisher;

@Autowired
private JavaMailSender mailSender;

@Autowired
private Environment env;

@Autowired
private UserDetailsService userDetailsService;

// 注册页面
@GetMapping("/registration")
public String registration(Model model) {
model.addAttribute("formTitle", "注册");
return "registration";
}

// 用户注册
@PostMapping("/user/registration")
@ResponseBody
public GenericResponse registerUserAccount(@Valid UserDTO userDTO, HttpServletRequest request) {
User registered = userService.registerNewUserAccount(userDTO);
eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registered, request.getLocale(), getAppUrl(request)));
return new GenericResponse("success");
}

// 激活用户账户
@GetMapping("/registrationConfirm.html")
public String confirmRegistration(HttpServletRequest request, HttpServletResponse response, RedirectAttributesModelMap model
, @RequestParam("token") String token) {
log.info("confirmRegistration");
Locale locale = localeResolver.resolveLocale(request);
log.info("token:{}" + token);
VerificationToken verificationToken = userService.getVerificationToken(token);
if (verificationToken == null) {
String message = messages.getMessage("auth.message.invalidToken", null, locale);
log.info("message:" + message);
model.addFlashAttribute("errMsg", message);
return "redirect:/badUser.html";
}

User user = verificationToken.getUser();
Calendar cal = Calendar.getInstance();
if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) {
model.addFlashAttribute("message", messages.getMessage("auth.message.expired", null, locale));
model.addFlashAttribute("expired", true);
model.addFlashAttribute("token", token);
return "redirect:/badUser.html";
}

user.setEnabled(true);
userService.saveRegisteredUser(user);
model.addFlashAttribute("message", messages.getMessage("message.accountVerified", null, locale));

return "redirect:/login";
}

// 重新发送注册令牌
@GetMapping("/user/resendRegistrationToken")
@ResponseBody
public GenericResponse resendRegistrationToken(final HttpServletRequest request, final RedirectAttributesModelMap model, @RequestParam("token") final String existingToken) {
log.info("resendRegistrationToken");
final Locale locale = request.getLocale();
final VerificationToken newToken = userService.generateNewVerificationToken(existingToken);
final User user = userService.getUser(newToken.getToken());
final SimpleMailMessage email = constructResetVerificationTokenEmail(getAppUrl(request), request.getLocale(), newToken, user);
mailSender.send(email);
log.info("message: {}", messages.getMessage("message.resendToken", null, locale));
return new GenericResponse(messages.getMessage("message.resendToken", null, locale));
}

// 重置密码
@PostMapping("/user/resetPassword")
@ResponseBody
public GenericResponse resetPassword(final HttpServletRequest request, final RedirectAttributesModelMap model
, @RequestParam("email") final String userEmail) {
log.info("resetPassword: {}", userEmail);
final User user = userService.findUserByEmail(userEmail);
if (user == null) {
return new GenericResponse(messages.getMessage("message.userNotFound", null, request.getLocale()));
}

final String token = UUID.randomUUID().toString();
userService.createPasswordResetTokenForUser(user, token);
final SimpleMailMessage email = constructResetTokenEmail(getAppUrl(request), request.getLocale(), token, user);
mailSender.send(email);
return new GenericResponse(messages.getMessage("message.resetPasswordEmail", null, request.getLocale()));
}

// 更换密码页面
@GetMapping("/user/changePassword")
public String showChangePassword(final HttpServletRequest request, final RedirectAttributesModelMap model, @RequestParam("id") final long id, @RequestParam("token") final String token) {
log.info("showChangePassword, id:{}, token:{}", id, token);
final Locale locale = request.getLocale();
String result = userService.validatePasswordResetToken(token);
log.info("result:{}", result);
if(result != null) {
String message = messages.getMessage("auth.message." + result, null, locale);
log.info("message:{}", message);
model.addFlashAttribute("errMsg", message);
return "redirect:/login.html?";
} else {
model.addFlashAttribute("token", token);
return "redirect:/updatePassword.html";
}
}

@PostMapping("/user/savePassword")
@ResponseBody
public GenericResponse savePassword(final Locale locale, @Valid PasswordDto passwordDto) {
log.info("savePassword");
String result = userService.validatePasswordResetToken(passwordDto.getToken());

if(result != null) {
return new GenericResponse(messages.getMessage(
"auth.message." + result, null, locale));
}

Optional<User> user = userService.getUserByPasswordResetToken(passwordDto.getToken());
if(user.isPresent()) {
userService.changeUserPassword(user.get(), passwordDto.getNewPassword());
String message = messages.getMessage(
"message.resetPasswordSuc", null, locale);
log.info("message:{}", message);
return new GenericResponse(message);
} else {
return new GenericResponse(messages.getMessage(
"auth.message.invalid", null, locale));
}
}


private String getAppUrl(HttpServletRequest request) {
String appUrl = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
return appUrl;
}

private final SimpleMailMessage constructResetVerificationTokenEmail(final String contextPath, final Locale locale
, final VerificationToken newToken, final User user) {
final String confirmationUrl = contextPath + "/registrationConfirm.html?token=" + newToken.getToken();
log.info("Url: {}", confirmationUrl);
final String message = messages.getMessage("message.resendToken", null, locale);
final SimpleMailMessage email = new SimpleMailMessage();
email.setSubject("Resend Registration Token");
email.setText(message + " \r\n" + confirmationUrl);
email.setTo(user.getEmail());
email.setFrom(env.getProperty("support.email"));
log.info("support.email:{}", env.getProperty("support.email"));
return email;
}

private final SimpleMailMessage constructResetTokenEmail(final String contextPath, final Locale locale, final String token, final User user) {
final String url = contextPath + "/user/changePassword?id=" + user.getId() + "&token=" + token;
log.info("url:{}", url);
final String message = messages.getMessage("message.resetPassword", null, locale);
final SimpleMailMessage email = new SimpleMailMessage();
email.setTo(user.getEmail());
email.setSubject("Reset Password");
email.setText(message + " \r\n" + url);
email.setFrom(env.getProperty("support.email"));
return email;
}
}

UserServiceImpl.java, 用户账号操作业务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package top.yekongle.activate.service.impl;

import java.util.Arrays;
import java.util.Calendar;
import java.util.Optional;
import java.util.UUID;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;
import top.yekongle.activate.dto.UserDTO;
import top.yekongle.activate.entity.PasswordResetToken;
import top.yekongle.activate.entity.User;
import top.yekongle.activate.entity.UserAuthority;
import top.yekongle.activate.entity.VerificationToken;
import top.yekongle.activate.exception.UserAlreadyExistException;
import top.yekongle.activate.repository.PasswordResetTokenRepository;
import top.yekongle.activate.repository.UserAuthorityRepository;
import top.yekongle.activate.repository.UserRepository;
import top.yekongle.activate.repository.VerificationTokenRepository;
import top.yekongle.activate.service.UserService;

/**
* @Description:
* @Author: Yekongle
* @Date: 2020年5月5日
*/
@Slf4j
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepository;

@Autowired
private UserAuthorityRepository userAuthorityRepository;

@Autowired
private VerificationTokenRepository tokenRepository;

@Autowired
private PasswordEncoder passwordEncoder;

@Autowired
private PasswordResetTokenRepository passwordTokenRepository;

@Override
public User registerNewUserAccount(UserDTO userDTO) throws UserAlreadyExistException {
if (emailExists(userDTO.getEmail())) {
throw new UserAlreadyExistException("该邮箱已被注册:" + userDTO.getEmail());
}
log.info("UserDTO:" + userDTO.toString());
User user = new User();
user.setEmail(userDTO.getEmail());
user.setPassword(passwordEncoder.encode(userDTO.getPassword()));
userRepository.save(user);

UserAuthority userAuthority = new UserAuthority();
userAuthority.setUsername(userDTO.getEmail());
userAuthority.setRole("ROLE_USER");
userAuthorityRepository.save(userAuthority);

return user;
}

@Override
public VerificationToken getVerificationToken(String verificationToken) {
return tokenRepository.findByToken(verificationToken);
}

@Override
public VerificationToken generateNewVerificationToken(String token) {
VerificationToken vToken = tokenRepository.findByToken(token);
vToken.updateToken(UUID.randomUUID()
.toString());
vToken = tokenRepository.save(vToken);
return vToken;
}

@Override
public void saveRegisteredUser(User user) {
userRepository.save(user);
}

@Override
public void createVerificationToken(User user, String token) {
VerificationToken myToken = new VerificationToken(token, user);
tokenRepository.save(myToken);
}

@Override
public User getUser(String verificationToken) {
User user = tokenRepository.findByToken(verificationToken).getUser();
return user;
}

@Override
public PasswordResetToken getPasswordResetToken(String token) {
return passwordTokenRepository.findByToken(token);
}

@Override
public User findUserByEmail(String email) {
return userRepository.findByEmail(email);
}

@Override
public void createPasswordResetTokenForUser(User user, String token) {
final PasswordResetToken myToken = new PasswordResetToken(token, user);
passwordTokenRepository.save(myToken);
}

public String validatePasswordResetToken(String token) {
final PasswordResetToken passToken = passwordTokenRepository.findByToken(token);

return !isTokenFound(passToken) ? "invalidToken"
: isTokenExpired(passToken) ? "expired"
: null;
}

@Override
public void changeUserPassword(User user, String password) {
user.setPassword(passwordEncoder.encode(password));
userRepository.save(user);
}

@Override
public Optional<User> getUserByPasswordResetToken(String token) {
return Optional.ofNullable(passwordTokenRepository.findByToken(token).getUser());
}

private boolean emailExists(String email) {
return userRepository.findByEmail(email) != null;
}

private boolean isTokenFound(PasswordResetToken passToken) {
return passToken != null;
}

private boolean isTokenExpired(PasswordResetToken passToken) {
final Calendar cal = Calendar.getInstance();
return passToken.getExpiryDate().before(cal.getTime());
}


}

OnRegistrationCompleteEvent.java, 注册监听事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package top.yekongle.activate.event;

import java.util.Locale;

import lombok.*;
import org.springframework.context.ApplicationEvent;

import top.yekongle.activate.entity.User;

/**
* @Description:
* @Author: Yekongle
* @Date: 2020年6月6日
*/
@Getter
@Setter
public class OnRegistrationCompleteEvent extends ApplicationEvent {

private static final long serialVersionUID = 1L;

private String appUrl;
private Locale locale;
private User user;

public OnRegistrationCompleteEvent(User user, Locale locale, String appUrl) {
super(user);
this.user = user;
this.locale = locale;
this.appUrl = appUrl;
}
}

RegistrationListener.java, 注册事件监听, 发送激活邮件到用户邮箱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package top.yekongle.activate.listener;

import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.MessageSource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;
import top.yekongle.activate.entity.User;
import top.yekongle.activate.event.OnRegistrationCompleteEvent;
import top.yekongle.activate.service.UserService;;
/**
* @Description:
* @Author: Yekongle
* @Date: 2020年6月6日
*/
@Slf4j
@Component
public class RegistrationListener implements ApplicationListener<OnRegistrationCompleteEvent> {

@Autowired
private UserService service;

@Autowired
private MessageSource messages;

@Autowired
private JavaMailSender mailSender;

@Value("${spring.mail.username}")
private String emailFrom;

@Override
public void onApplicationEvent(OnRegistrationCompleteEvent event) {
this.confirmRegistration(event);
}

private void confirmRegistration(OnRegistrationCompleteEvent event) {
User user = event.getUser();
String token = UUID.randomUUID().toString();
service.createVerificationToken(user, token);

String recipientAddress = user.getEmail();
String subject = "Registration Confirmation";
String confirmationUrl = event.getAppUrl() + "/registrationConfirm.html?token=" + token;
log.info("confirmationUrl: {}" + confirmationUrl);
String message = messages.getMessage("message.regSucc", null, event.getLocale());
log.info("recipientAddress: {}", user.getEmail());
SimpleMailMessage email = new SimpleMailMessage();
email.setFrom(emailFrom);
email.setTo(recipientAddress);
email.setSubject(subject);
email.setText(message + "\r\n" + confirmationUrl);
mailSender.send(email);
}
}

运行演示

启动项目

  1. 访问 http://localhost:8080,会自动跳到登录页面,先点击跳到注册页面

  2. 点击注册,将发送激活邮件

  3. 打开注册邮箱查看激活邮件

  4. 在浏览器打开该链接,激活该账户

  5. 登录该账户

  6. 如果需要重置密码,则点击重置密码

  7. 邮箱中查看重置密码链接

  8. 在浏览器中打开重置密码链接

  9. 点击更改密码

项目已上传至 Github: https://github.com/yekongle/springboot-code-samples/tree/master/springboot-activate-account-sample , 希望对小伙伴们有帮助哦。

参考链接:

坚持原创技术分享,您的支持将鼓励我继续创作!