基于james实现在物理隔离环境下邮件的传输

基于james实现在物理隔离环境下邮件的传输

背景

为了保护高安全级别的网络安全,国家保密局于1999年12月27日发布了涉密网络的物理隔离要求,并于2000年1月1日颁布实施的《计算机信息系统国际联网保密管理规定》,该规定中第二章保密制度第六条规定;“涉及国家秘密的计算机信息系统,不得直接或间接地与国际互联网或其他公共信息网络相连接,必须实行物理隔离。”

物理隔离通常是通过部署网闸来切断内网和外网的物理连接和逻辑连接,网闸只摆渡原始数据,而不容许任何连接或者协议经过网闸。在这种环境下,内外网邮件的传输成了难题。本文以探究的方式尝试提供一套思路和实现解决该场景下的邮件传输,本文包含的代码都是demo级别代码,秉着对新领域的探究,但不确定该方法是不是合适该场景的解决方案。

环境与配置

本文采用的环境如下:

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>

架构思路

大致的架构如下图:

物理隔离环境下邮件传输架构图.jpg
  • mailet程序片段主要包含DivestitureAgreementMatcher和DivestitureAgreementMailet两个程序,DivestitureAgreementMatcher主要用于匹配内外网用户,如果是内网用户则放行,如果是外网用户,则将该邮件交给DivestitureAgreementMailet进行处理;DivestitureAgreementMailet将邮件的原始内容抽出,必要时可对原始内容进行切割,发送到消息队列。
  • 消息队列主要存储将要摆渡的消息,将内部的消息经由消息API取出,交给网闸进行摆渡
  • 网闸负责消息的摆渡,本质是对一块共享内存的分时读写实现内外网在物理隔离和逻辑隔离环境下的数据传输
  • 网闸另一端的消息API将网闸摆渡过来的内网消息发送给消息队列
  • 邮件转发程序从消息队列中取出待转发的消息,必要时重组消息,并进行转发。

由于重重困难,所以这个demo将跳过网闸部分,消息发送到消息队列之后,消费者直接将消息进行转发,虽然没有实际在网闸和内外网的环境下测试,但是!我觉得是可行的!

代码实现

mailet程序片段

mailet程序片段的实现思路如下:

mailet程序片段实现思路.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 判断接收人是否是内网用户,是则匹配给Mailet进行原始内容抽取,否则放行
 * @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("截取到到发送给%s@%s的邮件\r\n", userName, host);
            return !dbOperation.isInnerUser(userName + "@" + host);
        } catch (Exception e) {
            System.out.printf("发生异常 异常信息: %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 数据库操作
 * @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;

    /*
   连接数据库
    */
    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("开始连接数据库");
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, userName, password);
            logger.info("数据库连接成功");
        } catch (Exception e) {
            logger.warn("数据库连接出现异常 异常信息: {}", e.getMessage());
        }
    }

    /**
     * 判断该用户是否为内网用户
     *
     * @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();
        }
    }

    /*
    关闭连接
     */
    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("结果为: %s\r\n", "true");
        test.closeConnection();
    }
}

本例中搭建的james邮件服务器将用户信息存储在数据库中,因此可以直接通过查询数据库的手段判断是否是内网用户。此外,也可以直接截取域名进行判断。

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 截取邮件,剥离协议,并放入队列等待网闸摆渡
 * @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("将截取邮件发送到消息队列中...");
        DefaultMQProducer producer = new DefaultMQProducer("DivestitureAgreementGroup");
        producer.setNamesrvAddr("localhost:9876");
        producer.setInstanceName("rmq-instance");
        try {
            producer.start();
            System.out.println("开启消息队列");
        } 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("消息成功转发到消息队列中");
            System.out.printf("转发内容如下: %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("关闭消息队列");
        }
    }
}


ForwardMail对象如下:

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包,粘贴复制到james服务器下..\james-2.3.2.1\apps\james\SAR-INF\lib,如果SAR-INF目录下没有lib目录,则手动新增该目录,在..\james-2.3.2.1\apps\james\SAR-INF\config.xml中增加如下配置:

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

重启james服务器,mailet程序片段就可以生效了。下面是消费端消息的转发。

邮件转发程序

邮件的转发其实是我考虑比较久的,内网与外网无法建立连接的情况下,要将消息原封不动地进行转发,且消息发送人依旧标识是内网的用户,这是我期望实现的,但是我没有找到好的解决办法。我尝试将邮件的from设置为内网用户,或者将displayname设置为内网用户,在QQ邮箱中似乎都没有得到比较好的结果。因此最后我取巧实现,在外网统一由一个外网用户进行转发,转发时在subject中标识该邮件转发自内网的某个用户。

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("成功代理转发%s的邮件\r\n", recipient);
                        MailSender.sendHtml(forwardMail.getFrom(), "979831398@qq.com", "xxxxx", "smtp.qq.com", recipient, "转发自代理服务器由" + forwardMail.getFrom().split("@")[0] + "发出的邮件:" + 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 {
    /**
     * 服务邮箱
     */
    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;
    }
    /**
     * 使用默认的用户名和密码发送邮件
     * @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);
    }
    /**
     * 使用指定的用户名和密码发送邮件
     * @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 = "这是一封来自公司内网的测试邮件,收到请勿回复!";
            sendHtml(null, "test@xxxxx.com","test","localhost", "979831398@qq.com", "测试邮件", 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;

/**
 * 简单邮件发送器,可单发,群发。
 * 
 * @author humingfeng
 * 
 */
public class MailServer {
 
    /**
     * 发送邮件的props文件
     */
    private final transient Properties props = System.getProperties();
    /**
     * 邮件服务器登录验证
     */
    private transient MailAuthenticator authenticator;
 
    /**
     * 邮箱session
     */
    private transient Session session;
 
    /**
     * 初始化邮件发送器
     * 
     * @param smtpHostName
     *            SMTP邮件服务器地址
     * @param username
     *            发送邮件的用户名(地址)
     * @param password
     *            发送邮件的密码
     */
    public MailServer(final String smtpHostName, final String username,
            final String password) {
        init(username, password, smtpHostName);
    }
 
    /**
     * 初始化邮件发送器
     * 
     * @param username
     *            发送邮件的用户名(地址),并以此解析SMTP服务器地址
     * @param password
     *            发送邮件的密码
     */
    public MailServer(final String username, final String password) {
        // 通过邮箱地址解析出smtp服务器,对大多数邮箱都管用
        final String smtpHostName = "smtp." + username.split("@")[1];
        init(username, password, smtpHostName);
 
    }
 
    /**
     * 初始化
     * 
     * @param username
     *            发送邮件的用户名(地址)
     * @param password
     *            密码
     * @param smtpHostName
     *            SMTP主机地址
     */
    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);
        // 验证
        authenticator = new MailAuthenticator(username, password);
        // 创建session
        session = Session.getInstance(props, authenticator);
    }
 
    /**
     * 发送邮件
     * 
     * @param recipient
     *            收件人邮箱地址
     * @param subject
     *            邮件主题
     * @param content
     *            邮件内容
     * @throws AddressException
     * @throws MessagingException
     */
    public void send(String recipient, String subject, Object content, String fromname)
            throws AddressException, MessagingException, UnsupportedEncodingException {
        // 创建mime类型邮件
        final MimeMessage message = new MimeMessage(session);
        // 设置发信人
        if(StringUtils.isBlank(fromname)) {
            message.setFrom(new InternetAddress(authenticator.username, fromname));
        } else {
            message.setFrom(new InternetAddress(authenticator.username));
        }
        // 设置收件人
        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));
        }
        // 设置主题
        message.setSubject(subject);
        // 设置邮件内容
        message.setContent(content.toString(), "text/html;charset=utf-8");
//      message.setText(content.toString(), "GBK");
        // 发送
        Transport.send(message);
    }
 
    /**
     * 群发邮件
     * 
     * @param recipients
     *            收件人们
     * @param subject
     *            主题
     * @param content
     *            内容
     * @throws AddressException
     * @throws MessagingException
     */
    public void send(List<String> recipients, String subject, Object content, String fromname)
            throws AddressException, MessagingException, UnsupportedEncodingException {
        // 创建mime类型邮件
        final MimeMessage message = new MimeMessage(session);
        // 设置发信人
        if(StringUtils.isBlank(fromname)) {
            message.setFrom(new InternetAddress(authenticator.username, fromname));
        } else {
            message.setFrom(new InternetAddress(authenticator.username));
        }
        // 设置收件人们
        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);
        // 设置主题
        message.setSubject(subject);
        // 设置邮件内容
        message.setContent(content.toString(), "text/html;charset=utf-8");
        // 发送
        Transport.send(message);
    }
 
    /**
     * 服务器邮箱登录验证
     * 
     * @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;
        }
 
    }
}

最后启动james服务和Comsumer服务,测试邮件转发结果如下:

测试结果.png

通过某一对外用户转发的方式转发到外网的邮件不免存在一个问题,抵赖。某个用户明明发送了邮件,却抵赖自己未曾发过。关于这点我考虑可以通过数字签名的方式解决,某个用户在内网创建时生成公私钥对,通过对邮件内容签名的方式,保证邮件的不可篡改性和不可抵赖性。

最后,上面所有代码均已上传至github仓库

上面的思路和实现仅仅是我在这个新领域的摸索,可能与实际落地的方案相差甚远,但也算是我对这个新领域的一次微弱的攻击。以后希望能在数据交换领域拓宽我的视野,结识更多在这个领域中兢兢业业的大佬们,为我的许多迷茫指引方向。

参考

物理隔离环境中的电子邮件安全交换

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容