preface
Forgetting your password and resetting your password via email is a common business requirement. I also need to use this service in the process of developing my personal small project. Today, I will give you a practical business practice.
The development environment
- Springboot: 1.5.16) RELEASE
The business process
According to the controller function is divided into two parts:
- User applies to reset email:
- The user enters a mailbox on the page
- The server checks whether resets are allowed (whether the user the mailbox points to exists, whether resets are too frequent, whether resets have reached the daily request limit)
- After the verification is successful, the validate table is written into the application record, including the token, user email address, and user ID
- Send mail (including links with tokens)
- The user clicks on the in-mail link
- Jump to the new password input page
- Submit password reset request (POST contains token, new password)
- User reset password
- The server validates the token (whether the token has expired, whether the user has initiated other tokens)
- Find the user ID from the VALIDATE table and change the user password
In actual combat
- Pom.xml adds email dependencies
<! - mail: email--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>Copy the code
- Add the PM_VALIDATE table structure
Reset_token is generated by UUID, type is resetPassword by default, and user_id is the user ID of the user table
-- ----------------------------
-- Table structure for pm_validate
-- ----------------------------
DROP TABLE IF EXISTS `pm_validate`;
CREATE TABLE `pm_validate` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`email` varchar(40) NOT NULL,
`reset_token` varchar(40) NOT NULL,
`type` varchar(20) NOT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code
Generate or write corresponding POJOs and Mapper. Since I used mybatis -Generator plug-in, I need to run the plug-in to generate the corresponding POJO and Mapper.
- Modify application.properties to add mailbox configuration
Send mail configuration
spring.mail.host=smtp.gmail.com
[email protected]
spring.mail.password=xxxxxxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
Copy the code
- Write controllers and services
- ValidateController
@RestController
@RequestMapping(value = "/validate")
public class ValidateController {
@Autowired
private ValidateService validateService;
@Autowired
private UserService userService;
@Value("${spring.mail.username}") private String from; /** * The request for forgetting the password should be sent no more than five times a day and the interval for each request should be no less than one minute * @param Email * @param request * @return
*/
@ApiOperation(value = "Send forgotten password email", notes = "Send forgotten password email")
@RequestMapping(value = "/sendValidationEmail", method = {RequestMethod.POST})
public ResponseData<String> sendValidationEmail(@ApiParam("Email address") @RequestParam("email") String email,
HttpServletRequest request){
ResponseData<String> responseData = new ResponseData<>();
List<User> users = userService.findUserByEmail(email);
if (users == null){
responseData.jsonFill(2, "The user of this mailbox does not exist", null);
}else {
if(validateService sendValidateLimitation (email, 5, 1)) {/ / if allowed to reset the password, then in pm_validate insert a row in the table, Validate = new Validate(); validateService.insertNewResetRecord(validate, users.get(0), UUID.randomUUID().toString()); // Set email content String appUrl = request.getScheme() +": / /" + request.getServerName();
SimpleMailMessage passwordResetEmail = new SimpleMailMessage();
passwordResetEmail.setFrom(from);
passwordResetEmail.setTo(email);
passwordResetEmail.setSubject("[E-commerce price Monitoring] Forget your password");
passwordResetEmail.setText("You are requesting to reset your password, please click this link to reset your password: \n" + appUrl + "/validate/reset? token=" + validate.getResetToken());
validateService.sendPasswordResetEmail(passwordResetEmail);
responseData.jsonFill(1, null, null);
}else {
responseData.jsonFill(2,"Operation too frequent, please try again later!",null); }}returnresponseData; } /** ** Match url token to database token, if successful, can change the password, token validity period is 60 minutes * @param token * @param password * @param confirmPassword * @return
*/
@ApiOperation(value = "Reset password", notes = "Reset password")
@RequestMapping(value = "/resetPassword", method = RequestMethod.POST)
public ResponseData<String> resetPassword(@ApiParam("token") @RequestParam("token") String token,
@ApiParam("Password") @RequestParam("password") String password,
@ApiParam("Password confirmation") @RequestParam("confirmPassword") String confirmPassword){ ResponseData<String> responseData = new ResponseData<>(); / / find the validate record through token List < validate > validates. = validateService findUserByResetToken (token);if (validates == null){
responseData.jsonFill(2,"The reset request does not exist",null);
}else {
Validate validate = validates.get(0);
if (validateService.validateLimitation(validate.getEmail(), Long.MAX_VALUE, 60, token)){
Integer userId = validate.getUserId();
if (password.equals(confirmPassword)) {
userService.updatePassword(password, userId);
responseData.jsonFill(1, null,null);
}else {
responseData.jsonFill(2,"Confirm password is inconsistent with password, please re-enter", null); }}else {
responseData.jsonFill(2,"This link is invalid",null); }}returnresponseData; }}Copy the code
- ValidateService
public interface ValidateService {
void sendPasswordResetEmail(SimpleMailMessage email);
int insertNewResetRecord(Validate validate, User users, String token);
List<Validate> findUserByResetToken(String resetToken);
boolean validateLimitation(String email, long requestPerDay, long interval, String token);
boolean sendValidateLimitation(String email, long requestPerDay, long interval);
}
Copy the code
- ValidateServiceImpl
@Service public class ValidateServiceImpl implements ValidateService { @Autowired private JavaMailSender javaMailSender; @Autowired private ValidateMapper validateMapper; /** * Send email: @override @async public void sendPasswordResetEmail(SimpleMailMessage email){@override @async public void sendPasswordResetEmail(SimpleMailMessage email){ javaMailSender.send(email); } /** * insert a validate entry into the pM_user table with the email attribute from the userID. Tokens are generated by UUID * @param validate * @param users * @param token * @return
*/
@Override
public int insertNewResetRecord(Validate validate, User users, String token){
validate.setUserId(users.getId());
validate.setEmail(users.getEmail());
validate.setResetToken(token);
validate.setType("passwordReset");
validate.setGmtCreate(new Date());
validate.setGmtModified(new Date());
returnvalidateMapper.insert(validate); } /** * resets the request record * @param token * @ in the pM_VALIDATE tablereturn
*/
@Override
public List<Validate> findUserByResetToken(String token){
ValidateExample validateExample = new ValidateExample();
ValidateExample.Criteria criteria = validateExample.createCriteria();
criteria.andResetTokenEqualTo(token);
returnvalidateMapper.selectByExample(validateExample); } /** * Verify whether to send a reset email: The maximum number of requests for a new password per email is requestPerDay, and the interval between the last request and the last one is interval minutes. * @param email * @param requestPerDay * @param interval * @return*/ @Override public boolean sendValidateLimitation(String email, long requestPerDay, long interval){ ValidateExample validateExample = new ValidateExample(); ValidateExample.Criteria criteria= validateExample.createCriteria(); criteria.andEmailEqualTo(email); List<Validate> validates = validateMapper.selectByExample(validateExample); // If no record is found, it means that the first application is directly releasedif (validates.isEmpty()) {
return true; } // There are records, Long countTodayValidation = validates.stream().filter(x-> dateutils.issameDay (x.gottgmtModified ()), new Date())).count(); Optional validate = validates.stream().map(Validate::getGmtModified).max(Date::compareTo); Date dateOfLastRequest = new Date();if (validate.isPresent()) dateOfLastRequest = (Date) validate.get();
long intervalForLastRequest = new Date().getTime() - dateOfLastRequest.getTime();
returncountTodayValidation <= requestPerDay && intervalForLastRequest >= interval * 60 * 1000; } /** * Verify that the connection is invalid: the connection is invalid in two ways: 1. Timeout 2. The last requested link automatically overwrites the previous link (see code) * @param email * @param requestPerDay * @param interval * @return
*/
@Override
public boolean validateLimitation(String email, long requestPerDay, long interval, String token){
ValidateExample validateExample = new ValidateExample();
ValidateExample.Criteria criteria= validateExample.createCriteria();
criteria.andEmailEqualTo(email);
List<Validate> validates = validateMapper.selectByExample(validateExample);
// 有记录才会调用该函数,只需判断是否超时
Optional validate = validates.stream().map(Validate::getGmtModified).max(Date::compareTo);
Date dateOfLastRequest = new Date();
if (validate.isPresent()) dateOfLastRequest = (Date) validate.get();
long intervalForLastRequest = new Date().getTime() - dateOfLastRequest.getTime();
Optional lastRequestToken = validates.stream().filter(x-> x.getResetToken().equals(token)).map(Validate::getGmtModified).findAny();
Date dateOfLastRequestToken = new Date();
if (lastRequestToken.isPresent()) {
dateOfLastRequestToken = (Date) lastRequestToken.get();
}
returnintervalForLastRequest <= interval * 60 * 1000 && dateOfLastRequest == dateOfLastRequestToken; }}Copy the code
conclusion
The above realization of the whole reset password process, front-end web design to achieve.
Pay attention to my
I am man three dao dao, currently for the background development engineer. Focus on backend development, network security, Python crawler and other technologies.
Come and chat with me on wechat: yangzd1102
Github:github.com/qqxx6661
Original blog main content
- Written interview review knowledge handbook
- Leetcode algorithm
- Sword point offer algorithm analysis
- Python crawler related technical analysis and practice
- Background development related technical analysis and actual combat
Update the following blogs
1. Csdn
blog.csdn.net/qqxx6661
Have columns: Leetcode problem solving (Java/Python), Python crawler development, interview assist manual
2. Zhihu
www.zhihu.com/people/yang…
Have column: code farmer interview assist manual
3. The nuggets
Juejin. Cn/user / 211951…
4. Jane books
www.jianshu.com/u/b5f225ca2…
Personal public account: Rude3Knife
If this article is helpful to you, save it and send it to your friends
Personal project: e-commerce price monitoring website
I long-term maintenance of personal projects, completely free, please support.
Realize the function
- Jingdong commodity monitoring: Set the commodity ID and expected price, and automatically send an email to remind users when the commodity price is [lower] than the set expected price. (within 1 hour)
- Jingdong Commodity Category Monitoring: After users subscribe to a specific category, [self-operated commodities] with a discount of more than 30% will be selected and sent to users via email to remind them.
- Category commodity browsing, commodity history price curve, commodity history highest and lowest price
- Continuously updated…
Web site address
pricemonitor.online/