Sending Emails with Spring Boot and Gmail SMTP
This document details the process of configuring and sending emails in a Spring Boot application using the Gmail SMTP server.
1. Enabling Gmail SMTP and Generating an App Password
To use Gmail's SMTP in an external application, you need an "App Password" if you have 2-Step Verification enabled. It is highly recommended to have 2-Step Verification enabled for greater security.
Step-by-Step to Generate an App Password:
- Access your Google Account: Go to myaccount.google.com.
- Go to Security: In the left-side menu, click on "Security".
- 2-Step Verification: Scroll down to the "How you sign in to Google" section and click on "2-Step Verification". If it's not already enabled, follow the instructions to activate it.
- App passwords: After enabling 2-Step Verification, return to the "Security" screen and look for "App passwords". Click on it.
- Generate New App Password:
- Select "Mail" from the "Select app" dropdown menu.
- Select "Other (Custom name)" from the "Select device" dropdown menu and type a name like "Spring Boot Email" (optional, for identification purposes only).
- Click "Generate".
- Copy the App Password: A 16-character password will be displayed in a yellow box. Copy this password and save it in a secure place. This will be the password you use in your Spring Boot application, not your Google account password.
Note: If you do not want to enable 2-Step Verification, you would have to enable the "Less secure app access" option in your Google security settings. However, this option has been discontinued for personal accounts as of May 30, 2022, and is not recommended for security reasons. Always prefer using "App passwords".
2. Configuring the Spring Boot Project
2.1. Add Spring Mail Dependency
Add the spring-boot-starter-mail dependency to your pom.xml file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.2. Configure Gmail Properties in application.properties
In the src/main/resources/application.properties file, add the following settings:
application.properties:
# Gmail SMTP Server Properties
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=${EMAIL_USER} # Your Gmail address in an environment variable
spring.mail.password=${EMAIL_SENHA} # The previously generated app password in an environment variable
# Additional properties for secure SMTP (TLS)
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=5000
spring.mail.properties.mail.smtp.writetimeout=5000
organize your environment variables
3. Creating the Email Service
Create a service class to handle email sending.
// src/main/java/com/example/emaildemo/service/EmailService.java
package com.example.emaildemo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Autowired
private JavaMailSender mailSender;
/**
* Sends a simple email.
* @param to The recipient of the email.
* @param subject The subject of the email.
* @param text The body of the email.
*/
public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("your_email@gmail.com"); // Can be read from application.properties configuration
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
System.out.println("Email sent successfully to: " + to);
}
// You can add methods to send emails with HTML, attachments, etc.
// Example of sending an email with HTML (requires MimeMessageHelper)
/*
import jakarta.mail.internet.MimeMessage;
import org.springframework.mail.javamail.MimeMessageHelper;
public void sendHtmlEmail(String to, String subject, String htmlContent) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); // true for multipart/mixed
helper.setFrom("your_email@gmail.com");
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true); // true indicates the content is HTML
mailSender.send(message);
System.out.println("HTML email sent successfully to: " + to);
} catch (jakarta.mail.MessagingException e) {
e.printStackTrace();
// Handle the exception
}
}
*/
}
ThymeLeaf: Additionally, if you want to send rich HTML emails, you can use a template engine like Thymeleaf.
5. Important Considerations and Best Practices
- Credential Security:
- Never hardcode credentials directly in the source code in a production environment.
- For production environments, use environment variables, Spring Cloud Config, HashiCorp Vault, or other secret management services to inject credentials.
- If you are using Docker, you can use Docker secrets.
- Exception Handling: Implement robust exception handling to deal with email sending failures (e.g., network, authentication, etc.).
- Logs: Configure logs to record the status of email sending, including successes and failures, for debugging and auditing purposes.
- Asynchronous Processing: To avoid blocking your application's main thread (especially in web requests), consider sending emails asynchronously using
@Asyncand@EnableAsyncin your main configuration class.- Add
@EnableAsyncto your main class (the one with@SpringBootApplication). - Add
@Asyncto thesendSimpleEmailmethod inEmailService.
- Add
- Email Templates: For more complex emails or those with HTML formatting, use template libraries like Thymeleaf, FreeMarker, or Mustache to create email templates and populate them dynamically.
- Production Settings: In production, ensure your firewall allows outgoing connections on port 587 (or 465 for SSL).
- Gmail Limits: Be aware of Gmail's daily sending limits (usually 500 emails per day for personal accounts). For larger volumes, consider transactional email services like SendGrid, Mailgun, AWS SES, etc.
Setting up Environment Variables
Setting environment variables is crucial for keeping credentials secure and making your Spring Boot application's configurations flexible across different environments. Below is a concise explanation of how to do this for EMAIL_USER and EMAIL_SENHA on Windows, macOS, and in Docker Compose.
Windows
On Windows, you can set environment variables temporarily via the command line or permanently through the system settings.
Temporarily (for the current terminal session):
Open Command Prompt or PowerShell and use:
set EMAIL_USER=your_email@gmail.com
set EMAIL_SENHA=your_app_password
Or in PowerShell:
$env:EMAIL_USER="your_email@gmail.com"
$env:EMAIL_SENHA="your_app_password"
Then, run your Spring Boot application from the same terminal.
Permanently (System-wide):
- Search for "Environment Variables" in the Start Menu and select "Edit the system environment variables".
- In the "System Properties" window, click on "Environment Variables...".
- In the "User variables for
" or "System variables" section, click "New..." to add EMAIL_USERandEMAIL_SENHAwith their respective values. - Click "OK" on all windows to save the changes. You may need to restart your system or terminal for the changes to take effect.
macOS
On macOS, environment variables can be set temporarily in the terminal session or added to shell configuration files to make them persistent.
Temporarily (for the current terminal session):
Open Terminal and use:
export EMAIL_USER="your_email@gmail.com"
export EMAIL_SENHA="your_app_password"
Then, run your Spring Boot application from the same terminal.
Permanently (User Profile):
To have the variables load automatically with each new terminal session, add the export lines to one of the following files in your home directory:
~/.bash_profile(if you use Bash as your default shell)~/.zshrc(if you use Zsh as your default shell, which is the default on macOS Catalina and later)
Example for ~/.zshrc:
echo 'export EMAIL_USER="your_email@gmail.com"' >> ~/.zshrc
echo 'export EMAIL_SENHA="your_app_password"' >> ~/.zshrc
source ~/.zshrc # To apply the changes immediately
Docker Compose
In Docker Compose, you can define environment variables in a few ways in your docker-compose.yml file. The most common and recommended way for credentials is to use an .env file.
Using an .env file (Recommended for credentials):
- Create a file named
.envin the same directory as yourdocker-compose.yml. -
Add your environment variables to this file, one per line:
# .env EMAIL_USER=your_email@gmail.com EMAIL_SENHA=your_app_password -
In your
docker-compose.ymlfile, reference these variables. Docker Compose will automatically load variables from an.envfile in the same directory.# docker-compose.yml version: '3.8' services: my-spring-app: image: your-spring-boot-image:latest # Or use build: . if building the image ports: - "8080:8080" environment: # These variables will be read from the .env file - EMAIL_USER=${EMAIL_USER} - EMAIL_SENHA=${EMAIL_SENHA} -
Run your Docker Compose service:
docker-compose up -d.
Alternative (less recommended for sensitive credentials in version control):
You can also define the variables directly in the docker-compose.yml under the environment section, but this exposes the values in the file.
# docker-compose.yml
services:
my-spring-app:
image: your-spring-boot-image:latest
ports:
- "8080:8080"
environment:
EMAIL_USER: your_email@gmail.com
EMAIL_SENHA: your_app_password # Avoid this in production
Always prefer the .env file method and make sure to add it to your .gitignore to prevent your credentials from being committed to version control.