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

Spring Boot整合Spring Integration Mail收邮件报错求助

解决Spring Integration Mail连接Gmail IMAP认证失败问题

问题描述

从Git复制多套Spring Boot整合Spring Integration Mail读取邮件的代码,运行时控制台持续出现认证失败错误,已确认用户名和密码配置正确,相关配置与代码如下:

Application.yaml配置

logging:
  level:
    root: INFO
    org.ygaros: DEBUG

spring:
  mail:
    username: arunofficial302@gmail.com
    password: ***************
    properties:
      protocol: imaps
      host: imap.gmail.com
      port: 993

MailConfig配置类

package com.htc.mailOperation.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Poller;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.mail.ImapMailReceiver;
import org.springframework.integration.mail.MailReceiver;
import org.springframework.integration.mail.MailReceivingMessageSource;
import org.springframework.messaging.Message;

import com.htc.mailOperation.service.MailReceiverService;

import javax.mail.internet.MimeMessage;

@Configuration
@EnableIntegration
public class MailConfig {

    private static final Logger log = LoggerFactory.getLogger(MailConfig.class);
    
    @Autowired
    private MailReceiverService receiverService;

    @Value("${spring.mail.username}")
    private String USERNAME;

    @Value("${spring.mail.password}")
    private String PASSWORD;

    @Value("${spring.mail.properties.port}")
    private int IMAP_PORT;

    @Value("${spring.mail.properties.protocol}")
    private String IMAP_PROTOCOL;

    @Value("${spring.mail.properties.host}")
    private String IMAP_HOST;

    @Bean("incomingEmailsChannel")
    public DirectChannel defaultChannel() {
        DirectChannel directChannel = new DirectChannel();
        directChannel.setDatatypes(MimeMessage.class);
        return directChannel;
    }
    @Bean
    public MailReceiver imapMailReceiver() {
        String url = String.format(
                "%s://%s:%s@%s:%s/INBOX", IMAP_PROTOCOL, USERNAME, PASSWORD, IMAP_HOST, IMAP_PORT
        );
        log.debug("IMAP url: {}", url.replace(PASSWORD, "x".repeat(8)));

        ImapMailReceiver imapMailReceiver = new ImapMailReceiver(url);
        imapMailReceiver.setShouldMarkMessagesAsRead(true);
        imapMailReceiver.setShouldDeleteMessages(false);
        imapMailReceiver.setSimpleContent(true);

        return imapMailReceiver;
    }

    @Bean
    @InboundChannelAdapter(
            channel = "incomingEmailsChannel",
            poller = @Poller(fixedDelay = "5000")

    )
    public MailReceivingMessageSource mailMessageSource(MailReceiver mailReceiver) {
        return new MailReceivingMessageSource(mailReceiver);

    }

    @ServiceActivator(inputChannel = "incomingEmailsChannel")
    public void receive(Message<MimeMessage> message) {
        receiverService.handleReceivedMail(message.getPayload());
    }
}

MailReceiverService服务类

package com.htc.mailOperation.service;

import org.apache.commons.mail.util.MimeMessageParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;

import javax.activation.DataSource;
import javax.mail.internet.MimeMessage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

@Service
public class MailReceiverService {

    private static final Logger log = LoggerFactory.getLogger(MailReceiverService.class);
    private static final String ATTACHMENTS_FOLDER_STORE = "data";

    public void handleReceivedMail(MimeMessage receivedMessage){
        try {
            MimeMessageParser mimeMessageParser = new MimeMessageParser(receivedMessage);
            mimeMessageParser.parse();

            logMail(mimeMessageParser);
            saveAttachments(mimeMessageParser);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private void logMail(MimeMessageParser mimeMessageParser) throws Exception {
        log.debug("From: {} To: {} Subject: {}",
                mimeMessageParser.getFrom(), mimeMessageParser.getTo(), mimeMessageParser.getSubject());
        log.debug("Mail content: {}", mimeMessageParser.getPlainContent());

    }

    private void saveAttachments(MimeMessageParser parser){
        List<DataSource> attachments = parser.getAttachmentList();
        log.debug("Email has {} attachment files", attachments.size());
        attachments.forEach(dataSource -> {
            String fileName = dataSource.getName();
            if(fileName != null && fileName.length() > 0){
                String rootDirPath = new FileSystemResource("").getFile().getAbsolutePath();

                createDirectoryIfNotExists(rootDirPath);

                String attachmentFilePath = rootDirPath +
                        File.separator +
                        ATTACHMENTS_FOLDER_STORE +
                        File.separator +
                        fileName;
                File attachmentFile = new File(attachmentFilePath);

                log.info("Attachment file saved to: {}", attachmentFilePath);

                try(
                        InputStream in = dataSource.getInputStream();
                        OutputStream out = new FileOutputStream(attachmentFile)
                        ){
                    copyStream(in, out);
                }catch(IOException e){
                    log.error("Failed to save file.", e);
                }
            }
        });
    }

    private void createDirectoryIfNotExists(String directoryPath) {
        Path of = Path.of(directoryPath);
        if (!Files.exists(of)) {
            try {
                Files.createDirectories(of);
            } catch (IOException e) {
                log.error("An error: {} on creating: {}", e, directoryPath);
            }
        }
    }

    private void copyStream(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[8192];
        int readByteCount;
        while(-1 != (readByteCount = in.read(buffer))){
            out.write(buffer, 0, readByteCount);
        }
    }
}

排查与解决方案

1. 修正Gmail认证方式

Gmail已不再支持普通密码登录IMAP,必须使用应用密码:

  • 确保你的Gmail账号开启了两步验证
  • 登录Google账号安全设置,创建一个专门用于此应用的应用密码
  • 将spring.mail.password替换为这个应用密码,原密码不再生效

2. 修正Spring Mail配置结构

你的application.yaml中spring.mail的配置项位置错误,正确的结构应该把host、protocol、port直接放在spring.mail下,而非properties子节点:

logging:
  level:
    root: INFO
    org.ygaros: DEBUG

spring:
  mail:
    username: arunofficial302@gmail.com
    password: 你的应用密码
    host: imap.gmail.com
    port: 993
    protocol: imaps
    properties:
      mail:
        imap:
          ssl:
            enable: true
          auth: true

3. 优化IMAP接收器配置

可以直接注入Spring的MailProperties来构建IMAP URL,避免手动拼接错误:

@Autowired
private MailProperties mailProperties;

@Bean
public MailReceiver imapMailReceiver() {
    String url = String.format(
            "%s://%s:%s@%s:%s/INBOX", 
            mailProperties.getProtocol(), 
            mailProperties.getUsername(), 
            mailProperties.getPassword(), 
            mailProperties.getHost(), 
            mailProperties.getPort()
    );
    log.debug("IMAP url: {}", url.replace(mailProperties.getPassword(), "x".repeat(8)));

    ImapMailReceiver imapMailReceiver = new ImapMailReceiver(url);
    imapMailReceiver.setShouldMarkMessagesAsRead(true);
    imapMailReceiver.setShouldDeleteMessages(false);
    imapMailReceiver.setSimpleContent(true);
    // 增加SSL配置,确保连接安全
    imapMailReceiver.setJavaMailProperties(mailProperties.getProperties());

    return imapMailReceiver;
}

4. 验证防火墙与网络

  • 确认服务器或本地网络未屏蔽993端口(IMAPS默认端口)
  • 测试是否能telnet到imap.gmail.com 993,确保网络连通性

内容的提问来源于stack exchange,提问作者ARUN KUMAR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 22:30:49