基于james實(shí)現(xiàn)在物理隔離環(huán)境下郵件的傳輸

基于james實(shí)現(xiàn)在物理隔離環(huán)境下郵件的傳輸

背景

為了保護(hù)高安全級(jí)別的網(wǎng)絡(luò)安全,國(guó)家保密局于1999年12月27日發(fā)布了涉密網(wǎng)絡(luò)的物理隔離要求,并于2000年1月1日頒布實(shí)施的《計(jì)算機(jī)信息系統(tǒng)國(guó)際聯(lián)網(wǎng)保密管理規(guī)定》,該規(guī)定中第二章保密制度第六條規(guī)定;“涉及國(guó)家秘密的計(jì)算機(jī)信息系統(tǒng),不得直接或間接地與國(guó)際互聯(lián)網(wǎng)或其他公共信息網(wǎng)絡(luò)相連接,必須實(shí)行物理隔離把曼〗低”

物理隔離通常是通過(guò)部署網(wǎng)閘來(lái)切斷內(nèi)網(wǎng)和外網(wǎng)的物理連接和邏輯連接黎烈,網(wǎng)閘只擺渡原始數(shù)據(jù)旭绒,而不容許任何連接或者協(xié)議經(jīng)過(guò)網(wǎng)閘俱箱。在這種環(huán)境下寇壳,內(nèi)外網(wǎng)郵件的傳輸成了難題日丹。本文以探究的方式嘗試提供一套思路和實(shí)現(xiàn)解決該場(chǎng)景下的郵件傳輸贰您,本文包含的代碼都是demo級(jí)別代碼鲤桥,秉著對(duì)新領(lǐng)域的探究揍拆,但不確定該方法是不是合適該場(chǎng)景的解決方案。

環(huán)境與配置

本文采用的環(huán)境如下:

Apache James-2.3.2.1

Apache RocketMQ-4.2.0

pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>MailInPIE</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.8</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.2.0</version>
        </dependency>
    </dependencies>

</project>

架構(gòu)思路

大致的架構(gòu)如下圖:

物理隔離環(huán)境下郵件傳輸架構(gòu)圖.jpg
  • mailet程序片段主要包含DivestitureAgreementMatcher和DivestitureAgreementMailet兩個(gè)程序茶凳,DivestitureAgreementMatcher主要用于匹配內(nèi)外網(wǎng)用戶,如果是內(nèi)網(wǎng)用戶則放行贮喧,如果是外網(wǎng)用戶筒狠,則將該郵件交給DivestitureAgreementMailet進(jìn)行處理;DivestitureAgreementMailet將郵件的原始內(nèi)容抽出箱沦,必要時(shí)可對(duì)原始內(nèi)容進(jìn)行切割辩恼,發(fā)送到消息隊(duì)列。
  • 消息隊(duì)列主要存儲(chǔ)將要擺渡的消息谓形,將內(nèi)部的消息經(jīng)由消息API取出运挫,交給網(wǎng)閘進(jìn)行擺渡
  • 網(wǎng)閘負(fù)責(zé)消息的擺渡,本質(zhì)是對(duì)一塊共享內(nèi)存的分時(shí)讀寫實(shí)現(xiàn)內(nèi)外網(wǎng)在物理隔離和邏輯隔離環(huán)境下的數(shù)據(jù)傳輸
  • 網(wǎng)閘另一端的消息API將網(wǎng)閘擺渡過(guò)來(lái)的內(nèi)網(wǎng)消息發(fā)送給消息隊(duì)列
  • 郵件轉(zhuǎn)發(fā)程序從消息隊(duì)列中取出待轉(zhuǎn)發(fā)的消息套耕,必要時(shí)重組消息谁帕,并進(jìn)行轉(zhuǎn)發(fā)。

由于重重困難冯袍,所以這個(gè)demo將跳過(guò)網(wǎng)閘部分匈挖,消息發(fā)送到消息隊(duì)列之后,消費(fèi)者直接將消息進(jìn)行轉(zhuǎn)發(fā)康愤,雖然沒(méi)有實(shí)際在網(wǎng)閘和內(nèi)外網(wǎng)的環(huán)境下測(cè)試儡循,但是!我覺得是可行的征冷!

代碼實(shí)現(xiàn)

mailet程序片段

mailet程序片段的實(shí)現(xiàn)思路如下:

mailet程序片段實(shí)現(xiàn)思路.jpg

DivestitureAgreementMatcher參考代碼:

package com.xxxxx.pie.mail.matcher;

import com.jlszkxa.pie.mail.db.DbOperation;
import org.apache.mailet.GenericRecipientMatcher;
import org.apache.mailet.MailAddress;
import java.sql.SQLException;

/**
 * @ClassName DivestitureAgreementMatcher
 * @Description 判斷接收人是否是內(nèi)網(wǎng)用戶择膝,是則匹配給Mailet進(jìn)行原始內(nèi)容抽取,否則放行
 * @Author chenwj
 * @Date 2020/2/20 14:53
 * @Version 1.0
 **/
public class DivestitureAgreementMatcher extends GenericRecipientMatcher {

    @Override
    public boolean matchRecipient(MailAddress mailAddress) {

        DbOperation dbOperation = new DbOperation();

        try {
            dbOperation.connectDatabase();
            String userName = mailAddress.getUser();
            String host = mailAddress.getHost();
            System.out.printf("截取到到發(fā)送給%s@%s的郵件\r\n", userName, host);
            return !dbOperation.isInnerUser(userName + "@" + host);
        } catch (Exception e) {
            System.out.printf("發(fā)生異常 異常信息: %s\r\n", e.getMessage());
            e.printStackTrace();
            return true;
        } finally {
            try {
                dbOperation.closeConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

dbOperation參考代碼:

package com.xxxxxx.pie.mail.db;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;

/**
 * @ClassName DbOperation
 * @Description 數(shù)據(jù)庫(kù)操作
 * @Author chenwj
 * @Date 2020/2/20 17:34
 * @Version 1.0
 **/

public class DbOperation {

    private static final Logger logger = LoggerFactory.getLogger(DbOperation.class);

    private Connection connection;

    /*
   連接數(shù)據(jù)庫(kù)
    */
    public void connectDatabase() {

        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/mail?characterEncoding=UTF-8";
        String userName = "root";
        String password = "123456";
        logger.info("開始連接數(shù)據(jù)庫(kù)");
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, userName, password);
            logger.info("數(shù)據(jù)庫(kù)連接成功");
        } catch (Exception e) {
            logger.warn("數(shù)據(jù)庫(kù)連接出現(xiàn)異常 異常信息: {}", e.getMessage());
        }
    }

    /**
     * 判斷該用戶是否為內(nèi)網(wǎng)用戶
     *
     * @param userName
     * @return
     * @throws Exception
     */
    public boolean isInnerUser(String userName) throws Exception {
        String sql = "select USER_NAME from james_user where USER_NAME = \'" + userName + "\';";
        PreparedStatement pstmt = connection.prepareStatement(sql);
        ResultSet rs = pstmt.executeQuery();
        try {
            if (rs.next()) {
                return true;
            }
            return false;
        } finally {
            rs.close();
            pstmt.close();
        }
    }

    /*
    關(guān)閉連接
     */
    public void closeConnection() throws SQLException {
        if (null != connection) {
            connection.close();
        }
    }

    public static void main(String[] args) throws Exception {
        DbOperation test = new DbOperation();
        test.connectDatabase();
        boolean innerUser = test.isInnerUser("97983398@qq.com");
        System.out.printf("結(jié)果為: %s\r\n", "true");
        test.closeConnection();
    }
}

本例中搭建的james郵件服務(wù)器將用戶信息存儲(chǔ)在數(shù)據(jù)庫(kù)中检激,因此可以直接通過(guò)查詢數(shù)據(jù)庫(kù)的手段判斷是否是內(nèi)網(wǎng)用戶肴捉。此外,也可以直接截取域名進(jìn)行判斷叔收。

DivestitureAgreementMailet參考代碼:

package com.xxxxxx.pie.mail.mailet;


import com.alibaba.fastjson.JSONObject;
import com.jlszkxa.pie.mail.entity.ForwardMail;
import org.apache.mailet.GenericMailet;
import org.apache.mailet.Mail;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import javax.mail.MessagingException;
import java.io.IOException;

/**
 * @author chenwj
 * @version 1.0
 * @className DivestitureAgreementMailet
 * @description 截取郵件齿穗,剝離協(xié)議,并放入隊(duì)列等待網(wǎng)閘擺渡
 * @date 2020/2/20 14:53
 **/

public class DivestitureAgreementMailet extends GenericMailet {
    @Override
    public void service(Mail mail) throws MessagingException {
        String sender = mail.getSender().toString();
        String name = mail.getName();
        String subject = mail.getMessage().getSubject();
        String content = null;
        try {
            content = (String) mail.getMessage().getContent();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.printf("截取到%s的郵件 name:%s subject:%s content:%s\r\n", sender, name, subject, content);
        System.out.println("將截取郵件發(fā)送到消息隊(duì)列中...");
        DefaultMQProducer producer = new DefaultMQProducer("DivestitureAgreementGroup");
        producer.setNamesrvAddr("localhost:9876");
        producer.setInstanceName("rmq-instance");
        try {
            producer.start();
            System.out.println("開啟消息隊(duì)列");
        } catch (MQClientException e) {
            e.printStackTrace();
        }
        try {
            ForwardMail forwardMail = ForwardMail.newBuilder()
                    .content(mail.getMessage().getContent())
                    .from(mail.getSender().getUser() + "@" + mail.getSender().getHost())
                    .hostName(mail.getRemoteHost())
                    .recipients(mail.getRecipients().iterator().next())
                    .subject(mail.getMessage().getSubject())
                    .build();
            Message message = new Message("demo-topic", "demo-tag", JSONObject.toJSONString(forwardMail).getBytes("UTF-8"));
            producer.send(message);
            System.out.println("消息成功轉(zhuǎn)發(fā)到消息隊(duì)列中");
            System.out.printf("轉(zhuǎn)發(fā)內(nèi)容如下: %s\r\n", JSONObject.toJSONString(forwardMail));
        } catch (MQClientException e) {
            e.printStackTrace();
        } catch (RemotingException e) {
            e.printStackTrace();
        } catch (MQBrokerException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            producer.shutdown();
            System.out.println("關(guān)閉消息隊(duì)列");
        }
    }
}


ForwardMail對(duì)象如下:

package com.xxxxxx.pie.mail.entity;

/**
 * @ClassName Mail
 * @Description TODO
 * @Author chenwj
 * @Date 2020/2/21 13:42
 * @Version 1.0
 **/

public class ForwardMail {

    private String hostName;

    private String from;

    private String subject;

    private Object recipients;

    private Object content;

    public ForwardMail() {
    }

    private ForwardMail(Builder builder) {
        setHostName(builder.hostName);
        setFrom(builder.from);
        setSubject(builder.subject);
        setRecipients(builder.recipients);
        setContent(builder.content);
    }

    public static Builder newBuilder() {
        return new Builder();
    }


    public String getHostName() {
        return hostName;
    }

    public void setHostName(String hostName) {
        this.hostName = hostName;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public Object getRecipients() {
        return recipients;
    }

    public void setRecipients(Object recipients) {
        this.recipients = recipients;
    }

    public Object getContent() {
        return content;
    }

    public void setContent(Object content) {
        this.content = content;
    }

    public static final class Builder {
        private String hostName;
        private String from;
        private String subject;
        private Object recipients;
        private Object content;

        private Builder() {
        }

        public Builder hostName(String val) {
            hostName = val;
            return this;
        }

        public Builder from(String val) {
            from = val;
            return this;
        }

        public Builder subject(String val) {
            subject = val;
            return this;
        }

        public Builder recipients(Object val) {
            recipients = val;
            return this;
        }

        public Builder content(Object val) {
            content = val;
            return this;
        }

        public ForwardMail build() {
            return new ForwardMail(this);
        }
    }
}

將上述的mailet程序片段打成jar包饺律,粘貼復(fù)制到j(luò)ames服務(wù)器下..\james-2.3.2.1\apps\james\SAR-INF\lib,如果SAR-INF目錄下沒(méi)有l(wèi)ib目錄窃页,則手動(dòng)新增該目錄,在..\james-2.3.2.1\apps\james\SAR-INF\config.xml中增加如下配置:

james配置1.png
james配置2.png

重啟james服務(wù)器,mailet程序片段就可以生效了脖卖。下面是消費(fèi)端消息的轉(zhuǎn)發(fā)乒省。

郵件轉(zhuǎn)發(fā)程序

郵件的轉(zhuǎn)發(fā)其實(shí)是我考慮比較久的,內(nèi)網(wǎng)與外網(wǎng)無(wú)法建立連接的情況下畦木,要將消息原封不動(dòng)地進(jìn)行轉(zhuǎn)發(fā)作儿,且消息發(fā)送人依舊標(biāo)識(shí)是內(nèi)網(wǎng)的用戶,這是我期望實(shí)現(xiàn)的馋劈,但是我沒(méi)有找到好的解決辦法。我嘗試將郵件的from設(shè)置為內(nèi)網(wǎng)用戶晾嘶,或者將displayname設(shè)置為內(nèi)網(wǎng)用戶妓雾,在QQ郵箱中似乎都沒(méi)有得到比較好的結(jié)果。因此最后我取巧實(shí)現(xiàn)垒迂,在外網(wǎng)統(tǒng)一由一個(gè)外網(wǎng)用戶進(jìn)行轉(zhuǎn)發(fā)械姻,轉(zhuǎn)發(fā)時(shí)在subject中標(biāo)識(shí)該郵件轉(zhuǎn)發(fā)自內(nèi)網(wǎng)的某個(gè)用戶。

Consumer參考代碼:

package com.xxxxxx.pie.mail.mq;

import com.alibaba.fastjson.JSONObject;
import com.jlszkxa.pie.mail.entity.ForwardMail;
import com.jlszkxa.pie.mail.mail.MailSender;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import javax.mail.MessagingException;
import java.io.UnsupportedEncodingException;
import java.util.List;

public class Consumer {
    public static void main(String[] args) throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("my-group");

        consumer.setNamesrvAddr("localhost:9876");
        consumer.setInstanceName("rmq-instance");
        consumer.subscribe("demo-topic", "demo-tag");

        consumer.registerMessageListener(new MessageListenerConcurrently() {
            public ConsumeConcurrentlyStatus consumeMessage(
                    List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    ForwardMail forwardMail = JSONObject.parseObject(new String(msg.getBody()), ForwardMail.class);
                    JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(forwardMail.getRecipients()));
                    String recipient = jsonObject.getString("user") + "@" + jsonObject.getString("host");
                    try {
                        System.out.printf("成功代理轉(zhuǎn)發(fā)%s的郵件\r\n", recipient);
                        MailSender.sendHtml(forwardMail.getFrom(), "979831398@qq.com", "xxxxx", "smtp.qq.com", recipient, "轉(zhuǎn)發(fā)自代理服務(wù)器由" + forwardMail.getFrom().split("@")[0] + "發(fā)出的郵件:" + forwardMail.getSubject(), forwardMail.getContent());
                    } catch (MessagingException e) {
                        e.printStackTrace();
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
        System.out.println("Consumer Started.");
    }
}

MailSender參考代碼:

package com.xxxxxx.pie.mail.mail;

import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import java.io.UnsupportedEncodingException;

/**
 * @author 
 *
 */
public class MailSender {
    /**
     * 服務(wù)郵箱
     */
    private static MailServer mailServer = null;
 
    //
    private static String userName;
 
    
    private static String password;
 
    
    private static String stmp;
 
    /**
     * @param userName the userName to set
     */
    public void setUserName(String userName) {
        if(MailSender.userName==null)
            MailSender.userName = userName;
    }
    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        if(MailSender.password==null)
            MailSender.password = password;
    }
    /**
     * @param stmp the stmp to set
     */
    public void setStmp(String stmp) {
        if(MailSender.stmp==null)
            MailSender.stmp = stmp;
    }
    /**
     * 使用默認(rèn)的用戶名和密碼發(fā)送郵件
     * @param recipient
     * @param subject
     * @param content
     * @throws MessagingException 
     * @throws AddressException 
     */
    public static void sendHtml(String recipient, String subject, Object content, String fromname) throws AddressException, MessagingException, UnsupportedEncodingException {
        if (mailServer == null) 
            mailServer = new MailServer(stmp,userName,password);
        mailServer.send(recipient, subject, content, fromname);
    }
    /**
     * 使用指定的用戶名和密碼發(fā)送郵件
     * @param server
     * @param password
     * @param recipient
     * @param subject
     * @param content
     * @throws MessagingException 
     * @throws AddressException 
     */
    public static void sendHtml(String fromname, String server,String password,String stmpIp, String recipient, String subject, Object content) throws AddressException, MessagingException, UnsupportedEncodingException {
         new MailServer(stmpIp,server,password).send(recipient, subject, content, fromname);
    }
    public static void main(String[] args) {
        try {
            String s = "這是一封來(lái)自公司內(nèi)網(wǎng)的測(cè)試郵件机断,收到請(qǐng)勿回復(fù)楷拳!";
            sendHtml(null, "test@xxxxx.com","test","localhost", "979831398@qq.com", "測(cè)試郵件", s);
        } catch (AddressException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (MessagingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

MailServer參考代碼:

package com.xxxxxx.pie.mail.mail;

import org.apache.commons.lang3.StringUtils;

import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Properties;

/**
 * 簡(jiǎn)單郵件發(fā)送器,可單發(fā)吏奸,群發(fā)欢揖。
 * 
 * @author humingfeng
 * 
 */
public class MailServer {
 
    /**
     * 發(fā)送郵件的props文件
     */
    private final transient Properties props = System.getProperties();
    /**
     * 郵件服務(wù)器登錄驗(yàn)證
     */
    private transient MailAuthenticator authenticator;
 
    /**
     * 郵箱session
     */
    private transient Session session;
 
    /**
     * 初始化郵件發(fā)送器
     * 
     * @param smtpHostName
     *            SMTP郵件服務(wù)器地址
     * @param username
     *            發(fā)送郵件的用戶名(地址)
     * @param password
     *            發(fā)送郵件的密碼
     */
    public MailServer(final String smtpHostName, final String username,
            final String password) {
        init(username, password, smtpHostName);
    }
 
    /**
     * 初始化郵件發(fā)送器
     * 
     * @param username
     *            發(fā)送郵件的用戶名(地址),并以此解析SMTP服務(wù)器地址
     * @param password
     *            發(fā)送郵件的密碼
     */
    public MailServer(final String username, final String password) {
        // 通過(guò)郵箱地址解析出smtp服務(wù)器奋蔚,對(duì)大多數(shù)郵箱都管用
        final String smtpHostName = "smtp." + username.split("@")[1];
        init(username, password, smtpHostName);
 
    }
 
    /**
     * 初始化
     * 
     * @param username
     *            發(fā)送郵件的用戶名(地址)
     * @param password
     *            密碼
     * @param smtpHostName
     *            SMTP主機(jī)地址
     */
    private void init(String username, String password, String smtpHostName) {
        // 初始化props
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.host", smtpHostName);
        if(smtpHostName==null)props.put("mail.smtp.host", smtpHostName);
        // 驗(yàn)證
        authenticator = new MailAuthenticator(username, password);
        // 創(chuàng)建session
        session = Session.getInstance(props, authenticator);
    }
 
    /**
     * 發(fā)送郵件
     * 
     * @param recipient
     *            收件人郵箱地址
     * @param subject
     *            郵件主題
     * @param content
     *            郵件內(nèi)容
     * @throws AddressException
     * @throws MessagingException
     */
    public void send(String recipient, String subject, Object content, String fromname)
            throws AddressException, MessagingException, UnsupportedEncodingException {
        // 創(chuàng)建mime類型郵件
        final MimeMessage message = new MimeMessage(session);
        // 設(shè)置發(fā)信人
        if(StringUtils.isBlank(fromname)) {
            message.setFrom(new InternetAddress(authenticator.username, fromname));
        } else {
            message.setFrom(new InternetAddress(authenticator.username));
        }
        // 設(shè)置收件人
        if(recipient!=null&&recipient.indexOf(";")!=-1){
            //多收件人
            String[] rec = recipient.split(";");
            int len = rec.length;
            InternetAddress[] iad = new InternetAddress[len];
            for(int i=0; i<len; i++){
                iad[i] =  new InternetAddress(rec[i]);
            }
            message.setRecipients(MimeMessage.RecipientType.TO, iad);
        }else{
            //單收件人
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(recipient));
        }
        // 設(shè)置主題
        message.setSubject(subject);
        // 設(shè)置郵件內(nèi)容
        message.setContent(content.toString(), "text/html;charset=utf-8");
//      message.setText(content.toString(), "GBK");
        // 發(fā)送
        Transport.send(message);
    }
 
    /**
     * 群發(fā)郵件
     * 
     * @param recipients
     *            收件人們
     * @param subject
     *            主題
     * @param content
     *            內(nèi)容
     * @throws AddressException
     * @throws MessagingException
     */
    public void send(List<String> recipients, String subject, Object content, String fromname)
            throws AddressException, MessagingException, UnsupportedEncodingException {
        // 創(chuàng)建mime類型郵件
        final MimeMessage message = new MimeMessage(session);
        // 設(shè)置發(fā)信人
        if(StringUtils.isBlank(fromname)) {
            message.setFrom(new InternetAddress(authenticator.username, fromname));
        } else {
            message.setFrom(new InternetAddress(authenticator.username));
        }
        // 設(shè)置收件人們
        final int num = recipients.size();
        InternetAddress[] addresses = new InternetAddress[num];
        for (int i = 0; i < num; i++) {
            addresses[i] = new InternetAddress(recipients.get(i));
        }
        message.setRecipients(MimeMessage.RecipientType.TO, addresses);
        // 設(shè)置主題
        message.setSubject(subject);
        // 設(shè)置郵件內(nèi)容
        message.setContent(content.toString(), "text/html;charset=utf-8");
        // 發(fā)送
        Transport.send(message);
    }
 
    /**
     * 服務(wù)器郵箱登錄驗(yàn)證
     * 
     * @author MZULE
     * 
     */
    public class MailAuthenticator extends Authenticator {
 
        /**
         * 用戶名(登錄郵箱)
         */
        private String username;
        /**
         * 密碼
         */
        private String password;
 
        /**
         * 初始化郵箱和密碼
         * 
         * @param username
         *            郵箱
         * @param password
         *            密碼
         */
        public MailAuthenticator(String username, String password) {
            this.username = username;
            this.password = password;
        }
 
        String getPassword() {
            return password;
        }
 
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(username, password);
        }
 
        String getUsername() {
            return username;
        }
 
        public void setPassword(String password) {
            this.password = password;
        }
 
        public void setUsername(String username) {
            this.username = username;
        }
 
    }
}

最后啟動(dòng)james服務(wù)和Comsumer服務(wù)她混,測(cè)試郵件轉(zhuǎn)發(fā)結(jié)果如下:

測(cè)試結(jié)果.png

通過(guò)某一對(duì)外用戶轉(zhuǎn)發(fā)的方式轉(zhuǎn)發(fā)到外網(wǎng)的郵件不免存在一個(gè)問(wèn)題,抵賴泊碑。某個(gè)用戶明明發(fā)送了郵件坤按,卻抵賴自己未曾發(fā)過(guò)。關(guān)于這點(diǎn)我考慮可以通過(guò)數(shù)字簽名的方式解決馒过,某個(gè)用戶在內(nèi)網(wǎng)創(chuàng)建時(shí)生成公私鑰對(duì)臭脓,通過(guò)對(duì)郵件內(nèi)容簽名的方式,保證郵件的不可篡改性和不可抵賴性腹忽。

最后来累,上面所有代碼均已上傳至github倉(cāng)庫(kù)

上面的思路和實(shí)現(xiàn)僅僅是我在這個(gè)新領(lǐng)域的摸索窘奏,可能與實(shí)際落地的方案相差甚遠(yuǎn)佃扼,但也算是我對(duì)這個(gè)新領(lǐng)域的一次微弱的攻擊。以后希望能在數(shù)據(jù)交換領(lǐng)域拓寬我的視野蔼夜,結(jié)識(shí)更多在這個(gè)領(lǐng)域中兢兢業(yè)業(yè)的大佬們兼耀,為我的許多迷茫指引方向。

參考

物理隔離環(huán)境中的電子郵件安全交換

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瘤运,隨后出現(xiàn)的幾起案子窍霞,更是在濱河造成了極大的恐慌,老刑警劉巖拯坟,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件但金,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡郁季,警方通過(guò)查閱死者的電腦和手機(jī)冷溃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)梦裂,“玉大人似枕,你說(shuō)我怎么就攤上這事∧昴” “怎么了凿歼?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冗恨。 經(jīng)常有香客問(wèn)我答憔,道長(zhǎng),這世上最難降的妖魔是什么掀抹? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任虐拓,我火速辦了婚禮,結(jié)果婚禮上傲武,老公的妹妹穿的比我還像新娘侯嘀。我一直安慰自己,他們只是感情好谱轨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布戒幔。 她就那樣靜靜地躺著,像睡著了一般土童。 火紅的嫁衣襯著肌膚如雪诗茎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天献汗,我揣著相機(jī)與錄音敢订,去河邊找鬼。 笑死罢吃,一個(gè)胖子當(dāng)著我的面吹牛楚午,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尿招,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼矾柜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼阱驾!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起怪蔑,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤里覆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后缆瓣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喧枷,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年弓坞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了隧甚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渡冻,死狀恐怖戚扳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情菩帝,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布茬腿,位于F島的核電站呼奢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏切平。R本人自食惡果不足惜握础,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悴品。 院中可真熱鬧禀综,春花似錦、人聲如沸苔严。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)届氢。三九已至欠窒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間退子,已是汗流浹背岖妄。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寂祥,地道東北人荐虐。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像丸凭,于是被迫代替她去往敵國(guó)和親福扬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腕铸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容