春季启动异步OTP生成和电子邮件发送
#java #springboot #learning #async

在现代Web应用程序中,安全至关重要。一次性密码(OTP)广泛用于安全用户身份验证和交易验证。但是,生成OTP并通过电子邮件发送它们可能是一个耗时的过程,可能会影响您的应用程序的响应能力。为了解决这个问题,我们将学习如何在Spring Boot应用程序中实现异步OTP生成和电子邮件发送。

通过使用CompletableFuture@Async注释,我们可以实现非阻滞行为,允许我们的应用程序在生成OTP并在后台发送电子邮件时处理其他任务。

先决条件:

  • Java和Spring Boot的基本知识。
  • 一个有效的春季启动项目,带有用于发送电子邮件的Javamailsender的配置Javamailsender。
  • 对RESTFUL API和弹簧控制器的理解。

这是博客是此银行门户API https://github.com/abhi9720/BankingPortal-API/

的一部分

1。项目设置:在开始之前,请确保您已经设置了一个春季启动项目并对Spring Boot配置有基本的了解。

2。实施异步OTP生成:在此步骤中,我们将创建负责OTP生成和数据库操作的服务类。

创建OtpService接口:

public interface OTPService {
    String generateOTP(String accountNumber);
    CompletableFuture<Boolean> sendOTPByEmail(String email, String name, String accountNumber, String otp);
    boolean validateOTP(String accountNumber, String otp);
}

实现OtpServiceImpl类:

@Service
public class OTPServiceImpl implements OTPService {

    @Autowired
     private EmailService emailService;

    @Override
    public String generateOTP(String accountNumber) {
      Random random = new Random();
      int otpValue = 100_000 + random.nextInt(900_000);
      String otp = String.valueOf(otpValue);

      // Save the new OTP information in the database
      OtpInfo otpInfo = new OtpInfo();
      otpInfo.setAccountNumber(accountNumber);
      otpInfo.setOtp(otp);
      otpInfo.setGeneratedAt(LocalDateTime.now());
      otpInfoRepository.save(otpInfo);
      return otp;
 }

    @Override
    @Async
    public CompletableFuture<Boolean> sendOTPByEmail(String email, String name, String accountNumber, String otp) {
        // Compose the email content
        String subject = "OTP Verification";
        String emailText = emailService.getOtpLoginEmailTemplate(name, "xxx" + accountNumber.substring(3), otp);

        CompletableFuture<Void> emailSendingFuture = emailService.sendEmail(email, subject, emailText);

        return emailSendingFuture.thenApplyAsync(result -> true)
                                .exceptionally(ex -> {
                                    ex.printStackTrace();
                                    return false;
                                });
    }


  // ... Other Code 
}

配置异步执行:确保在主应用程序类中存在@EnableAsync注释以启用异步处理。

@ SpringBootApplication 
@ EnableAsync 
public class YourApplication { 
  // Application setup and configuration code // ... 
}

3。通过异步发送OTP:现在,让我们专注于实施负责发送电子邮件的电子邮件服务。

Creating the EmailService Interface:

public interface EmailService {
    public CompletableFuture<Void> sendEmail(String to, String subject, String text);
    public String getOtpLoginEmailTemplate(String name,String accountNumber, String otp) ;
}
Implementing the EmailServiceImpl Class:
@Service
public class EmailServiceImpl implements EmailService {

      private final JavaMailSender mailSender;

     @Autowired
     public EmailServiceImpl(JavaMailSender mailSender) {
         this.mailSender = mailSender;
     }

     @Override
     @Async
     public CompletableFuture<Void> sendEmail(String to, String subject, String text) {
         CompletableFuture<Void> future = new CompletableFuture<>();

         try {
             MimeMessage message = mailSender.createMimeMessage();
             MimeMessageHelper helper = new MimeMessageHelper(message, true);
             helper.setTo(to);
             // No need to set the "from" address; it is automatically set by Spring Boot based on your properties
             helper.setSubject(subject);
             helper.setText(text, true); // Set the second parameter to true to send HTML content
             mailSender.send(message);

             future.complete(null); // Indicate that the email sending is successful
         } catch (MessagingException e) {
             e.printStackTrace();
             future.completeExceptionally(e); // Indicate that the email sending failed
         }

         return future;
     }

     @Override
     public String getOtpLoginEmailTemplate(String name, String accountNumber, String otp) {
         // Create the formatted email template with the provided values
         String emailTemplate = "<div style=\"font-family: Helvetica,Arial,sans-serif;min-width:1000px;overflow:auto;line-height:2\">"
                 + "<div style=\"margin:50px auto;width:70%;padding:20px 0\">"
                 + "<div style=\"border-bottom:1px solid #eee\">"
                 + "<a href=\"https://piggybank.netlify.app/\" style=\"font-size:1.4em;color: #00466a;text-decoration:none;font-weight:600\">piggybank</a>"
                 + "</div>"
                 + "<p style=\"font-size:1.1em\">Hi, " + name + "</p>"
                 + "<p style=\"font-size:0.9em;\">Account Number: " + accountNumber + "</p>"
                 + "<p>Thank you for choosing OneStopBank. Use the following OTP to complete your Log In procedures. OTP is valid for 5 minutes</p>"
                 + "<h2 style=\"background: #00466a;margin: 0 auto;width: max-content;padding: 0 10px;color: #fff;border-radius: 4px;\">" + otp + "</h2>"
                 + "<p style=\"font-size:0.9em;\">Regards,<br />OneStopBank</p>"
                 + "<hr style=\"border:none;border-top:1px solid #eee\" />"
                 + "<p>piggybank Inc</p>"
                 + "<p>1600 Amphitheatre Parkway</p>"
                 + "<p>California</p>"
                 + "</div>"
                 + "</div>";

         return emailTemplate;
     }
}
// application.properties
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=example@gmail.com
spring.mail.password=xxxxxxxxxxxxxxxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

4。创建RESTFUL API:现在,让我们创建一个RESTFUL API来演示异步的OTP生成和电子邮件发送过程。

// DTO For Receving OTP request
public class OtpRequest {
    private String accountNumber;

 public String getAccountNumber() {
  return accountNumber;
 }

 public void setAccountNumber(String accountNumber) {
  this.accountNumber = accountNumber;
 }


}

@RestController
public class OTPController {

    @Autowired
    private OTPService otpService;

    @PostMapping("/generate-otp")
    public ResponseEntity<?> generateOtp(@RequestBody OtpRequest otpRequest) {
        String accountNumber = otpRequest.getAccountNumber();

        // Fetch the user by account number to get the associated email
        User user = userService.getUserByAccountNumber(accountNumber);
        if (user == null) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("User not found for the given account number");
        }

        // Generate OTP and save it in the database
        String otp = otpService.generateOTP(accountNumber);

        // Send the OTP to the user's email address asynchronously
        CompletableFuture<Boolean> emailSendingFuture = otpService.sendOTPByEmail(user.getEmail(), user.getName(), accountNumber, otp);

        // Wait for the email sending process to complete and handle the response
        try {
            boolean otpSent = emailSendingFuture.get(); // This will block until the email sending is complete

            if (otpSent) {
                // Return JSON response with success message
                return ResponseEntity.ok().body("{\"message\": \"OTP sent successfully\"}");
            } else {
                // Return JSON response with error message
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{\"message\": \"Failed to send OTP\"}");
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            // Return JSON response with error message
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{\"message\": \"Failed to send OTP\"}");
        }
    }
}

5。测试异步OTP生成和电子邮件发送:现在我们已经实施了API,您可以使用Postman之类的工具将POST请求发送到/生成所需数据。 API将返回指示OTP生成和电子邮件发送状态的响应。

对于任何参考检查此存储库:https://github.com/abhi9720/BankingPortal-API/