springboot進(jìn)行數(shù)據(jù)庫連接配置的自定義加密

經(jīng)驗(yàn)備忘,僅供參考

1.場景

配置文件的數(shù)據(jù)庫連接的用戶名和密碼需要改成密文,提高安全性.
加解密使用一個工具類來實(shí)現(xiàn),并非是泛用的加解密過程,不方便用插件.

2.思路

1.找到從配置文件獲取用戶名和密碼的邏輯,或者使用用戶名和密碼創(chuàng)建數(shù)據(jù)源的邏輯
2.重寫對應(yīng)邏輯的源碼類,增加解密的步驟

3.項目環(huán)境

Spring Boot 2.4.2

        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!-- 動態(tài)數(shù)據(jù)源 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        </dependency>

        <!-- 阿里數(shù)據(jù)庫連接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!-- postgresql驅(qū)動包 -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>

項目實(shí)際使用了多數(shù)據(jù)源功能

#默認(rèn)數(shù)據(jù)源
spring.datasource.dynamic.datasource.postgresql.url=jdbc:postgresql://0.0.0.0:0/demo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.postgresql.username=81c5f00b41ee312
spring.datasource.dynamic.datasource.postgresql.password=3bbb7b271bf65ddda9cba27dad0bf20e23e84f85d3f7fb4

spring.datasource.dynamic.datasource.prod_01.url=jdbc:postgresql://0.0.0.0:0/demo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.prod_01.username=81c5f00b41ee312
spring.datasource.dynamic.datasource.prod_01.password=3bbb7b271bf65ddda9cba27dad0bf20e23e84f85d3f7fb4

spring.datasource.dynamic.datasource.prod_02.url=jdbc:postgresql://10.0.0.0:0/demo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
spring.datasource.dynamic.datasource.prod_02.username=6e4398db9ca4d1d
spring.datasource.dynamic.datasource.prod_02.password=c79d198ea20e940c8d0fcb495cef9ab

4.確認(rèn)創(chuàng)建數(shù)據(jù)源邏輯

將數(shù)據(jù)庫連接改成錯誤的
查看異常報錯信息,進(jìn)入報錯的類
報錯節(jié)點(diǎn)增加斷點(diǎn),debug進(jìn)入斷點(diǎn),由斷點(diǎn)向調(diào)用鏈的上方回溯
在疑似地方繼續(xù)增加斷點(diǎn),反復(fù)debug
最終找到的類是(也可以去改其他類的邏輯):
com.baomidou.dynamic.datasource.creator.DruidDataSourceCreator

*
 * Copyright ? 2018 organization baomidou
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.baomidou.dynamic.datasource.creator;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.logging.Slf4jLogFilter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.baomidou.dynamic.datasource.exception.ErrorCreateDataSourceException;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidSlf4jConfig;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidWallConfigUtil;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import static com.baomidou.dynamic.datasource.support.DdConstants.DRUID_DATASOURCE;

/**
 * Druid數(shù)據(jù)源創(chuàng)建器
 *
 * @author TaoYu
 * @since 2020/1/21
 */
@Data
public class DruidDataSourceCreator implements DataSourceCreator {

    private static Boolean druidExists = false;

    static {
        try {
            Class.forName(DRUID_DATASOURCE);
            druidExists = true;
        } catch (ClassNotFoundException ignored) {
        }
    }

    private DruidConfig gConfig;

    @Autowired(required = false)
    private ApplicationContext applicationContext;

    public DruidDataSourceCreator(DruidConfig gConfig) {
        this.gConfig = gConfig;
    }

    @Override
    public DataSource createDataSource(DataSourceProperty dataSourceProperty) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername(dataSourceProperty.getUsername());
        dataSource.setPassword(dataSourceProperty.getPassword());
        dataSource.setUrl(dataSourceProperty.getUrl());
        dataSource.setName(dataSourceProperty.getPoolName());
        String driverClassName = dataSourceProperty.getDriverClassName();
        if (!StringUtils.isEmpty(driverClassName)) {
            dataSource.setDriverClassName(driverClassName);
        }
        DruidConfig config = dataSourceProperty.getDruid();
        Properties properties = config.toProperties(gConfig);

        List<Filter> proxyFilters = this.initFilters(dataSourceProperty, properties);
        dataSource.setProxyFilters(proxyFilters);

        dataSource.configFromPropety(properties);
        //連接參數(shù)單獨(dú)設(shè)置
        dataSource.setConnectProperties(config.getConnectionProperties());
        //設(shè)置druid內(nèi)置properties不支持的的參數(shù)
        this.setParam(dataSource, config);

        if (!dataSourceProperty.getLazy()) {
            try {
                dataSource.init();
            } catch (SQLException e) {
                throw new ErrorCreateDataSourceException("druid create error", e);
            }
        }
        return dataSource;
    }

    private List<Filter> initFilters(DataSourceProperty dataSourceProperty, Properties properties) {
        List<Filter> proxyFilters = new ArrayList<>(2);
        String filters = properties.getProperty("druid.filters");
        if (!StringUtils.isEmpty(filters)) {
            if (filters.contains("stat")) {
                StatFilter statFilter = new StatFilter();
                statFilter.configFromProperties(properties);
                proxyFilters.add(statFilter);
            }
            if (filters.contains("wall")) {
                WallConfig wallConfig = DruidWallConfigUtil.toWallConfig(dataSourceProperty.getDruid().getWall(), gConfig.getWall());
                WallFilter wallFilter = new WallFilter();
                wallFilter.setConfig(wallConfig);
                proxyFilters.add(wallFilter);
            }
            if (filters.contains("slf4j")) {
                Slf4jLogFilter slf4jLogFilter = new Slf4jLogFilter();
                // 由于properties上面被用了问词,LogFilter不能使用configFromProperties方法,這里只能一個個set了。
                DruidSlf4jConfig slf4jConfig = gConfig.getSlf4j();
                slf4jLogFilter.setStatementLogEnabled(slf4jConfig.getEnable());
                slf4jLogFilter.setStatementExecutableSqlLogEnable(slf4jConfig.getStatementExecutableSqlLogEnable());
                proxyFilters.add(slf4jLogFilter);
            }
        }
        if (this.applicationContext != null) {
            for (String filterId : gConfig.getProxyFilters()) {
                proxyFilters.add(this.applicationContext.getBean(filterId, Filter.class));
            }
        }
        return proxyFilters;
    }

    private void setParam(DruidDataSource dataSource, DruidConfig config) {
        String defaultCatalog = config.getDefaultCatalog() == null ? gConfig.getDefaultCatalog() : config.getDefaultCatalog();
        if (defaultCatalog != null) {
            dataSource.setDefaultCatalog(defaultCatalog);
        }
        Boolean defaultAutoCommit = config.getDefaultAutoCommit() == null ? gConfig.getDefaultAutoCommit() : config.getDefaultAutoCommit();
        if (defaultAutoCommit != null && !defaultAutoCommit) {
            dataSource.setDefaultAutoCommit(false);
        }
        Boolean defaultReadOnly = config.getDefaultReadOnly() == null ? gConfig.getDefaultReadOnly() : config.getDefaultReadOnly();
        if (defaultReadOnly != null) {
            dataSource.setDefaultReadOnly(defaultReadOnly);
        }
        Integer defaultTransactionIsolation = config.getDefaultTransactionIsolation() == null ? gConfig.getDefaultTransactionIsolation() : config.getDefaultTransactionIsolation();
        if (defaultTransactionIsolation != null) {
            dataSource.setDefaultTransactionIsolation(defaultTransactionIsolation);
        }

        Boolean testOnReturn = config.getTestOnReturn() == null ? gConfig.getTestOnReturn() : config.getTestOnReturn();
        if (testOnReturn != null && testOnReturn) {
            dataSource.setTestOnReturn(true);
        }
        Integer validationQueryTimeout =
                config.getValidationQueryTimeout() == null ? gConfig.getValidationQueryTimeout() : config.getValidationQueryTimeout();
        if (validationQueryTimeout != null && !validationQueryTimeout.equals(-1)) {
            dataSource.setValidationQueryTimeout(validationQueryTimeout);
        }

        Boolean sharePreparedStatements =
                config.getSharePreparedStatements() == null ? gConfig.getSharePreparedStatements() : config.getSharePreparedStatements();
        if (sharePreparedStatements != null && sharePreparedStatements) {
            dataSource.setSharePreparedStatements(true);
        }
        Integer connectionErrorRetryAttempts =
                config.getConnectionErrorRetryAttempts() == null ? gConfig.getConnectionErrorRetryAttempts()
                        : config.getConnectionErrorRetryAttempts();
        if (connectionErrorRetryAttempts != null && !connectionErrorRetryAttempts.equals(1)) {
            dataSource.setConnectionErrorRetryAttempts(connectionErrorRetryAttempts);
        }
        Boolean breakAfterAcquireFailure =
                config.getBreakAfterAcquireFailure() == null ? gConfig.getBreakAfterAcquireFailure() : config.getBreakAfterAcquireFailure();
        if (breakAfterAcquireFailure != null && breakAfterAcquireFailure) {
            dataSource.setBreakAfterAcquireFailure(true);
        }

        Integer timeout = config.getRemoveAbandonedTimeoutMillis() == null ? gConfig.getRemoveAbandonedTimeoutMillis()
                : config.getRemoveAbandonedTimeoutMillis();
        if (timeout != null) {
            dataSource.setRemoveAbandonedTimeoutMillis(timeout);
        }

        Boolean abandoned = config.getRemoveAbandoned() == null ? gConfig.getRemoveAbandoned() : config.getRemoveAbandoned();
        if (abandoned != null) {
            dataSource.setRemoveAbandoned(abandoned);
        }

        Boolean logAbandoned = config.getLogAbandoned() == null ? gConfig.getLogAbandoned() : config.getLogAbandoned();
        if (logAbandoned != null) {
            dataSource.setLogAbandoned(logAbandoned);
        }

        Integer queryTimeOut = config.getQueryTimeout() == null ? gConfig.getQueryTimeout() : config.getQueryTimeout();
        if (queryTimeOut != null) {
            dataSource.setQueryTimeout(queryTimeOut);
        }

        Integer transactionQueryTimeout =
                config.getTransactionQueryTimeout() == null ? gConfig.getTransactionQueryTimeout() : config.getTransactionQueryTimeout();
        if (transactionQueryTimeout != null) {
            dataSource.setTransactionQueryTimeout(transactionQueryTimeout);
        }
    }

    @Override
    public boolean support(DataSourceProperty dataSourceProperty) {
        Class<? extends DataSource> type = dataSourceProperty.getType();
        return (type == null && druidExists) || (type != null && DRUID_DATASOURCE.equals(type.getName()));
    }
}

要修改的地方在createDataSource方法

5.重寫源碼

在項目下新建一個包c(diǎn)om.baomidou.dynamic.datasource.creator(與DruidDataSourceCreator的所在包一致)
包下新建一個類DruidDataSourceCreator
將源碼的內(nèi)容原封不動的復(fù)制到新建的類
對createDataSource方法進(jìn)行修改
修改后:

 @Override
    public DataSource createDataSource(DataSourceProperty dataSourceProperty) {
        String username=dataSourceProperty.getUsername();
        String password=dataSourceProperty.getPassword();
        try {
            DesCode des = new DesCode("這是密鑰");
            username= des.decrypt(username);
            password= des.decrypt(password);
        }catch (Exception e){
            e.printStackTrace();
        }
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setUrl(dataSourceProperty.getUrl());
        dataSource.setName(dataSourceProperty.getPoolName());

        String driverClassName = dataSourceProperty.getDriverClassName();
        if (!StringUtils.isEmpty(driverClassName)) {
            dataSource.setDriverClassName(driverClassName);
        }
        DruidConfig config = dataSourceProperty.getDruid();
        Properties properties = config.toProperties(gConfig);

        List<Filter> proxyFilters = this.initFilters(dataSourceProperty, properties);
        dataSource.setProxyFilters(proxyFilters);

        dataSource.configFromPropety(properties);
        //連接參數(shù)單獨(dú)設(shè)置
        dataSource.setConnectProperties(config.getConnectionProperties());
        //設(shè)置druid內(nèi)置properties不支持的的參數(shù)
        this.setParam(dataSource, config);

        if (!dataSourceProperty.getLazy()) {
            try {
                dataSource.init();
            } catch (SQLException e) {
                throw new ErrorCreateDataSourceException("druid create error", e);
            }
        }
        return dataSource;
    }

6.完成測試

在maven先執(zhí)行clean再執(zhí)行install,讓重寫的代碼編譯,替換原有的代碼
啟動項目,測試驗(yàn)證功能.

7.其他

使用的IDEA自帶的反編譯進(jìn)行的源碼查看
建議進(jìn)行源碼下載,這樣可以看到作者的注釋,變量名也會更準(zhǔn)確
非多數(shù)據(jù)源的加密:

/*
 * Copyright (C) 2013, 2014 Brett Wooldridge
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.zaxxer.hikari.util;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Enumeration;
import java.util.Map.Entry;
import java.util.Properties;

import javax.sql.DataSource;

import com.xxl.job.admin.util.DesCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class DriverDataSource implements DataSource
{
    private static final Logger LOGGER = LoggerFactory.getLogger(DriverDataSource.class);
    private static final String PASSWORD = "password";
    private static final String USER = "user";

    private final String jdbcUrl;
    private final Properties driverProperties;
    private Driver driver;

    public DriverDataSource(String jdbcUrl, String driverClassName, Properties properties, String username, String password)
    {
        this.jdbcUrl = jdbcUrl;
        this.driverProperties = new Properties();

        for (Entry<Object, Object> entry : properties.entrySet()) {
            driverProperties.setProperty(entry.getKey().toString(), entry.getValue().toString());
        }

        if (username != null) {
            driverProperties.put(USER, driverProperties.getProperty("user", username));
        }
        if (password != null) {
            driverProperties.put(PASSWORD, driverProperties.getProperty("password", password));
        }

        if (driverClassName != null) {
            Enumeration<Driver> drivers = DriverManager.getDrivers();
            while (drivers.hasMoreElements()) {
                Driver d = drivers.nextElement();
                if (d.getClass().getName().equals(driverClassName)) {
                    driver = d;
                    break;
                }
            }

            if (driver == null) {
                LOGGER.warn("Registered driver with driverClassName={} was not found, trying direct instantiation.", driverClassName);
                Class<?> driverClass = null;
                ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader();
                try {
                    if (threadContextClassLoader != null) {
                        try {
                            driverClass = threadContextClassLoader.loadClass(driverClassName);
                            LOGGER.debug("Driver class {} found in Thread context class loader {}", driverClassName, threadContextClassLoader);
                        }
                        catch (ClassNotFoundException e) {
                            LOGGER.debug("Driver class {} not found in Thread context class loader {}, trying classloader {}",
                                    driverClassName, threadContextClassLoader, this.getClass().getClassLoader());
                        }
                    }

                    if (driverClass == null) {
                        driverClass = this.getClass().getClassLoader().loadClass(driverClassName);
                        LOGGER.debug("Driver class {} found in the HikariConfig class classloader {}", driverClassName, this.getClass().getClassLoader());
                    }
                } catch (ClassNotFoundException e) {
                    LOGGER.debug("Failed to load driver class {} from HikariConfig class classloader {}", driverClassName, this.getClass().getClassLoader());
                }

                if (driverClass != null) {
                    try {
                        driver = (Driver) driverClass.newInstance();
                    } catch (Exception e) {
                        LOGGER.warn("Failed to create instance of driver class {}, trying jdbcUrl resolution", driverClassName, e);
                    }
                }
            }
        }

        final String sanitizedUrl = jdbcUrl.replaceAll("([?&;]password=)[^&#;]*(.*)", "$1<masked>$2");
        try {
            if (driver == null) {
                driver = DriverManager.getDriver(jdbcUrl);
                LOGGER.debug("Loaded driver with class name {} for jdbcUrl={}", driver.getClass().getName(), sanitizedUrl);
            }
            else if (!driver.acceptsURL(jdbcUrl)) {
                throw new RuntimeException("Driver " + driverClassName + " claims to not accept jdbcUrl, " + sanitizedUrl);
            }
        }
        catch (SQLException e) {
            throw new RuntimeException("Failed to get driver instance for jdbcUrl=" + sanitizedUrl, e);
        }
    }

    @Override
    public Connection getConnection() throws SQLException
    {
        return driver.connect(jdbcUrl, driverProperties);
    }

    @Override
    public Connection getConnection(final String username, final String password) throws SQLException
    {
        String u=username;
        String p=password;
        try {
            DesCode des = new DesCode("這是密鑰");
            u= des.decrypt(username);
            p= des.decrypt(password);
        }catch (Exception e){
            e.printStackTrace();
        }

        final Properties cloned = (Properties) driverProperties.clone();
        if (username != null) {
            cloned.put("user", u);
            if (cloned.containsKey("username")) {
                cloned.put("username", u);
            }
        }
        if (password != null) {
            cloned.put("password", p);
        }

        return driver.connect(jdbcUrl, cloned);
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException
    {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setLogWriter(PrintWriter logWriter) throws SQLException
    {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException
    {
        DriverManager.setLoginTimeout(seconds);
    }

    @Override
    public int getLoginTimeout() throws SQLException
    {
        return DriverManager.getLoginTimeout();
    }

    @Override
    public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
    {
        return driver.getParentLogger();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException
    {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException
    {
        return false;
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末菠赚,一起剝皮案震驚了整個濱河市宁否,隨后出現(xiàn)的幾起案子骗奖,更是在濱河造成了極大的恐慌蓝晒,老刑警劉巖少态,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惰聂,死亡現(xiàn)場離奇詭異疆偿,居然都是意外死亡咱筛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門杆故,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迅箩,“玉大人,你說我怎么就攤上這事处铛∷乔鳎” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵撤蟆,是天一觀的道長奕塑。 經(jīng)常有香客問我,道長家肯,這世上最難降的妖魔是什么龄砰? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮讨衣,結(jié)果婚禮上换棚,老公的妹妹穿的比我還像新娘。我一直安慰自己值依,他們只是感情好圃泡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愿险,像睡著了一般颇蜡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辆亏,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天风秤,我揣著相機(jī)與錄音,去河邊找鬼扮叨。 笑死缤弦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的彻磁。 我是一名探鬼主播碍沐,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衷蜓!你這毒婦竟也來了累提?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤磁浇,失蹤者是張志新(化名)和其女友劉穎斋陪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡无虚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年缔赠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片友题。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡嗤堰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咆爽,到底是詐尸還是另有隱情梁棠,我是刑警寧澤置森,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布斗埂,位于F島的核電站,受9級特大地震影響凫海,放射性物質(zhì)發(fā)生泄漏呛凶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一行贪、第九天 我趴在偏房一處隱蔽的房頂上張望漾稀。 院中可真熱鬧,春花似錦建瘫、人聲如沸崭捍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽殷蛇。三九已至,卻和暖如春橄浓,著一層夾襖步出監(jiān)牢的瞬間粒梦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工荸实, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匀们,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓准给,卻偏偏與公主長得像泄朴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子露氮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

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