Java REST服务Gmail API Client Credential Flow实现配置求助
Google邮件API无人工干预对接方案(Java REST实现)
核心授权模式说明
你需要的无人工干预授权对应Google的服务账号+域范围委派模式(仅适用于Google Workspace/G Suite账户)。如果是普通Google个人账户,无法实现完全无人工干预,只能通过OAuth2授权码流让用户手动授权一次后,存储刷新令牌自动续用。以下方案优先针对Workspace账户,同时补充个人账户的替代实现。
一、Google Cloud平台配置
- 创建Google Cloud项目,搜索并启用
Gmail API - 生成服务账号:在「IAM与管理」→「服务账号」中创建账号,下载JSON格式的密钥文件(包含
client_id、client_email、private_key等核心信息) - 域范围委派:
- 打开服务账号详情页,点击「编辑」→「显示域范围委派」,勾选「启用G Suite域范围委派」
- 登录Google Workspace管理控制台,进入「安全」→「API控制」→「域范围委派」,添加服务账号的
client_id,授权范围填写https://www.googleapis.com/auth/gmail.send(遵循最小权限原则,仅申请发送邮件权限)
二、Java实现细节
1. 依赖配置(Maven)
<dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client</artifactId> <version>1.34.1</version> </dependency> <dependency> <groupId>com.google.apis</groupId> <artifactId>google-api-services-gmail</artifactId> <version>v1-rev20230515-2.0.0</version> </dependency> <dependency> <groupId>com.google.auth</groupId> <artifactId>google-auth-library-oauth2-http</artifactId> <version>1.19.0</version> </dependency>
2. 从数据库加载凭据
假设你的数据库为每个客户端存储以下加密后的信息:
- 服务账号JSON密钥字符串(或密钥文件路径)
- 目标发送邮箱(如
user@client-domain.com)
3. 无人工干预获取令牌并发送邮件
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.gmail.Gmail; import com.google.api.services.gmail.model.Message; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.ServiceAccountCredentials; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Properties; public class GoogleMailSender { // 从数据库读取并解密后的服务账号JSON字符串 private static final String SERVICE_ACCOUNT_JSON = "数据库中存储的服务账号JSON内容"; // 从数据库读取的目标发送邮箱 private static final String TARGET_USER_EMAIL = "client-user@example.com"; private static final String GMAIL_SCOPE = "https://www.googleapis.com/auth/gmail.send"; public static void sendEmail(String to, String subject, String body) throws IOException, MessagingException { // 初始化服务账号凭据,模拟用户身份 GoogleCredentials credentials = ServiceAccountCredentials.fromStream( new StringReader(SERVICE_ACCOUNT_JSON)) .createDelegated(TARGET_USER_EMAIL) .createScoped(Collections.singletonList(GMAIL_SCOPE)); // 构建Gmail API客户端 final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); Gmail service = new Gmail.Builder(httpTransport, GsonFactory.getDefaultInstance(), new HttpCredentialsAdapter(credentials)) .setApplicationName("你的通知服务名称") .build(); // 创建MIME格式邮件 Properties props = new Properties(); Session session = Session.getDefaultInstance(props, null); MimeMessage mimeMessage = new MimeMessage(session); mimeMessage.setFrom(new InternetAddress(TARGET_USER_EMAIL)); mimeMessage.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); mimeMessage.setSubject(subject); mimeMessage.setText(body); // 转换为Gmail API兼容的Message格式 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); mimeMessage.writeTo(buffer); byte[] rawMessageBytes = buffer.toByteArray(); String encodedEmail = java.util.Base64.getUrlEncoder().encodeToString(rawMessageBytes); Message message = new Message(); message.setRaw(encodedEmail); // 发送邮件 service.users().messages().send("me", message).execute(); } public static void main(String[] args) { try { sendEmail("recipient@example.com", "测试通知", "这是通过Google API自动发送的邮件"); } catch (Exception e) { e.printStackTrace(); } } }
三、普通Google个人账户的替代实现
若客户端使用普通Google账户,无法使用服务账号模式,需先引导用户手动授权一次,存储刷新令牌后自动续用:
- 生成授权URL,引导用户完成授权,获取授权码
- 用授权码交换访问令牌和刷新令牌,将刷新令牌加密存储到数据库
- 后续通过刷新令牌自动获取新的访问令牌,无需人工干预
核心刷新令牌代码示例:
import com.google.auth.oauth2.UserCredentials; import java.io.IOException; public String getAccessToken(String clientId, String clientSecret, String refreshToken) throws IOException { UserCredentials credentials = UserCredentials.newBuilder() .setClientId(clientId) .setClientSecret(clientSecret) .setRefreshToken(refreshToken) .build(); credentials.refresh(); return credentials.getAccessToken().getTokenValue(); }
四、关键注意事项
- 令牌缓存:访问令牌有效期约1小时,建议缓存令牌避免重复请求API
- 凭据加密:数据库中存储的服务账号密钥、刷新令牌必须加密,防止泄露
- 权限控制:仅申请必要的API权限,避免过度授权带来的安全风险
- Workspace验证:确保客户端的Workspace管理员已完成域范围委派的授权操作
内容的提问来源于stack exchange,提问作者Hari
相关产品推荐
相关产品推荐

