You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oracle 19c中JavaMail调用SendGrid未触发STARTTLS的问题求助

问题:Oracle数据库中调用JavaMail发送邮件未触发STARTTLS

环境信息

  • Oracle 19c
  • Java 1.8.0_451
  • JavaMail 1.6.7

数据库中部署的Java类代码

CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED SECURITY."SendMail" as
import java.util.*;
import java.io.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
 
public class SendMail {
 
    static {
        MailcapCommandMap mailcap = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
        mailcap.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
        mailcap.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
        mailcap.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
        mailcap.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
        CommandMap.setDefaultCommandMap(mailcap);
    }
 
    public static int Send(String smtpServer,
                           String sender,
                           String recipient,
                           String ccRecipient,
                           String bccRecipient,
                           String subject,
                           String body,
                           String[] errorMessage,
                           String attachments,
                           final String username,
                           final String password) {
 
        int errorStatus = 0;
  
        System.setProperty("javax.net.ssl.trustStore", "/u02/sendgrid/wallet/mytruststore.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
        System.setProperty("javax.net.ssl.trustStoreType", "JKS");
        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.starttls.required", "true");
        props.put("mail.smtp.ssl.protocols", "TLSv1.2");
        props.put("mail.smtp.host", smtpServer);
        props.put("mail.smtp.port", "587");
        props.put("mail.debug", "true");
 
        Session session = Session.getInstance(props, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(username, password);
            }
        });
        session.setDebug(true);
 
        try {
            MimeMessage msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress(sender));
 
            // To
            InternetAddress[] toAddresses = InternetAddress.parse(recipient);
            msg.setRecipients(Message.RecipientType.TO, toAddresses);
 
            // CC
            if (ccRecipient != null && !ccRecipient.trim().isEmpty()) {
                InternetAddress[] ccAddresses = InternetAddress.parse(ccRecipient);
                msg.setRecipients(Message.RecipientType.CC, ccAddresses);
            }
 
            // BCC
            if (bccRecipient != null && !bccRecipient.trim().isEmpty()) {
                InternetAddress[] bccAddresses = InternetAddress.parse(bccRecipient);
                msg.setRecipients(Message.RecipientType.BCC, bccAddresses);
            }
 
            msg.setSubject(subject, "UTF-8");
            msg.setSentDate(new Date());
 
            Multipart mp = new MimeMultipart();
 
            // Body part with HTML or plain text detection
            MimeBodyPart mbpText = new MimeBodyPart();
            if (body == null || body.trim().isEmpty()) {
                body = "[No message body provided]";
            }
 
            if (body.toLowerCase().contains("<html") || body.toLowerCase().contains("<body")) {
                mbpText.setContent(body, "text/html; charset=UTF-8");
            } else {
                mbpText.setContent(body, "text/plain; charset=UTF-8");
            }
 
            mp.addBodyPart(mbpText);
 
            // Attachments
            if (attachments != null && !attachments.trim().isEmpty()) {
                int startIndex = 0, posIndex;
                while ((posIndex = attachments.indexOf("///", startIndex)) != -1) {
                    String filePath = attachments.substring(startIndex, posIndex).trim();
                    addAttachment(mp, filePath);
                    startIndex = posIndex + 3;
                }
                if (startIndex < attachments.length()) {
                    String filePath = attachments.substring(startIndex).trim();
                    addAttachment(mp, filePath);
                }
            }
 
            msg.setContent(mp);
 
            Transport.send(msg);
 
        } catch (Exception e) {
            errorMessage[0] = e.toString();
            errorStatus = 1;
        }
 
        return errorStatus;
    }
 
    private static void addAttachment(Multipart mp, String filePath) throws MessagingException {
        File file = new File(filePath);
        if (!file.exists()) return;
 
        MimeBodyPart mbp = new MimeBodyPart();
        FileDataSource fds = new FileDataSource(file);
        mbp.setDataHandler(new DataHandler(fds));
        mbp.setFileName(fds.getName());
        mp.addBodyPart(mbp);
    }
}

调试日志

DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Sun Microsystems, Inc]
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: useEhlo true, useAuth true
DEBUG: SMTPTransport trying to connect to host "smtp.sendgrid.net", port 587
DEBUG SMTP RCVD: 220 SG ESMTP service ready at geopod-ismtpd-36
DEBUG: SMTPTransport connected to host "smtp.sendgrid.net", port: 587
DEBUG SMTP SENT: EHLO database_server
DEBUG SMTP RCVD: 250-smtp.sendgrid.net
250-8BITMIME
250-PIPELINING
250-SIZE 31457280
250-SMTPUTF8
250-STARTTLS
250-AUTH PLAIN LOGIN
250 AUTH=PLAIN LOGIN
DEBUG SMTP Found extension "8BITMIME", arg ""
DEBUG SMTP Found extension "PIPELINING", arg ""
DEBUG SMTP Found extension "SIZE", arg "31457280"
DEBUG SMTP Found extension "SMTPUTF8", arg ""
DEBUG SMTP Found extension "STARTTLS", arg ""
DEBUG SMTP Found extension "AUTH", arg "PLAIN LOGIN"
DEBUG SMTP Found extension "AUTH=PLAIN", arg "LOGIN"
DEBUG SMTP: Attempt to authenticate
DEBUG SMTP SENT: AUTH LOGIN
DEBUG SMTP RCVD: 334 VXNlcm5hbWU6
DEBUG SMTP SENT: YXBpa2V5
DEBUG SMTP RCVD: 334 UGFzc3dvcmQ6
DEBUG SMTP SENT: encoded password
DEBUG SMTP RCVD: 235 Authentication successful
DEBUG SMTP: use8bit false
DEBUG SMTP SENT: MAIL FROM:<myapp@example.net>
DEBUG SMTP RCVD: 250 Sender address accepted
DEBUG SMTP SENT: RCPT TO:<myrecipient@example.net>
DEBUG SMTP RCVD: 250 Recipient address accepted
Verified Addresses
  myrecipient@example.net
DEBUG SMTP SENT: DATA
DEBUG SMTP RCVD: 354 Continue
DEBUG SMTP SENT:
.
 *** 2025-06-10T07:58:54.209486-04:00
DEBUG SMTP RCVD: 250 Ok: queued as jYJnb995RBipUUlY_z2R5Q
DEBUG SMTP SENT: QUIT

问题描述

代码在本地Java环境运行正常,但在Oracle数据库中通过存储过程调用时,调试日志中未出现STARTTLS命令,也未收到SendGrid返回的"220 Begin TLS negotiation now"响应,直接跳过TLS协商阶段进行了SMTP认证和邮件发送。

原因分析

  1. OJVM自带JavaMail版本冲突:Oracle 19c内置JVM(OJVM)默认包含旧版本JavaMail库,会优先加载自带版本而非部署的JavaMail 1.6.7,旧版本对starttls.enable属性的处理逻辑不同,导致TLS协商未触发。
  2. 属性设置未生效:OJVM环境中,通过Properties对象设置的JavaMail属性可能被系统级属性覆盖,或OJVM安全策略限制了属性修改。
  3. SSL信任存储不兼容:OJVM对自定义JKS信任存储支持有限,无法正确加载指定的mytruststore.jks,导致JavaMail跳过TLS协商以规避SSL验证错误。

解决方法

1. 替换OJVM中的JavaMail版本

使用loadjava命令将JavaMail 1.6.7的mail.jar和activation.jar加载到Oracle数据库,覆盖自带旧版本:

loadjava -user <用户名>/<密码>@<数据库实例> -resolve -force -schema SECURITY mail.jar activation.jar

通过以下SQL验证类加载情况:

SELECT owner, object_name, object_type FROM all_objects WHERE object_name LIKE '%SMTPTransport%';

确保返回com.sun.mail.smtp.SMTPTransport且所属schema为部署的目标schema(如SECURITY)。

2. 修改属性设置方式

将STARTTLS相关属性通过System.setProperty全局设置,确保在OJVM中生效:

// 在创建props对象前添加
System.setProperty("mail.smtp.starttls.enable", "true");
System.setProperty("mail.smtp.starttls.required", "true");
System.setProperty("mail.smtp.ssl.protocols", "TLSv1.2");

3. 使用Oracle Wallet管理SSL证书

OJVM更适配Oracle Wallet,将SendGrid根证书导入Wallet并修改代码配置:

  1. 生成Oracle Wallet:
    orapki wallet create -wallet /u02/sendgrid/wallet -pwd <wallet密码> -auto_login
    
  2. 导入SendGrid根证书:
    orapki wallet add -wallet /u02/sendgrid/wallet -trusted_cert -cert sendgrid_root.crt -pwd <wallet密码>
    
  3. 修改代码中的SSL属性:
    System.setProperty("javax.net.ssl.trustStoreType", "OraclePKI");
    System.setProperty("javax.net.ssl.trustStore", "/u02/sendgrid/wallet/cwallet.sso");
    
  4. 配置数据库使用该Wallet:
    ALTER SYSTEM SET WALLET_LOCATION='(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/u02/sendgrid/wallet)))' SCOPE=SPFILE;
    ALTER SYSTEM SET SSL_CLIENT_AUTHENTICATION=FALSE SCOPE=SPFILE;
    
    重启数据库后生效。

4. 验证TLS协商

修改后重新执行存储过程,正常调试日志会包含以下流程:

DEBUG SMTP SENT: EHLO database_server
DEBUG SMTP RCVD: ...250-STARTTLS...
DEBUG SMTP SENT: STARTTLS
DEBUG SMTP RCVD: 220 Begin TLS negotiation now
DEBUG: SMTPTransport converting to TLS mode

内容的提问来源于stack exchange,提问作者Nathan Russell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:45:54