#Redis+AOP注解实现IP在单位时间内请求限流

现在好多商城类项目对接口限流做了限制,一来为了更好的营造一个健康的绿色的网站,而来防止对网站进行破坏比如黑客写个脚本不停的对服务器进行请求。
话不多说本需求的原来是:#####对每个访问的请求,使用 前缀+请求地址+ip+系统时间,存入 redis 里面,然后 访问之前先去 进行 泛读取key,就是省去最后的系统时间+"*" 进行匹配,看 key 有多少个,如果超过了,注解限制的,就直接返回 请求失败!这里之所以要加上 系统时间,是因为 如果第一次存1,第二次+1 的话可能会 大量的 修改操作,为了方便,所以这样写的#####

①pom.xml配置文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hytc.mall</groupId>
    <artifactId>hytcmall</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hytcmall</name>
    <description>弘毅天承商城</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 动态数据源 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>2.5.4</version>
        </dependency>

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

        <!-- mybatis MVC -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.3.2</version>
        </dependency>

        <!-- mybatisPlus 代码生成-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.2</version>
        </dependency>

        <!--执行main方法前请注意,在生成文件的时候需要有一个模板引擎的选择,MyBatis Plus的默认模板引擎是velocity。我们可以使用freemarker-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>

        <!--数据库链接    -->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>

        <!-- 非关系型数据库Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>

        <!-- 阿里巴巴连接池 -->
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.22</version>
        </dependency>
        <!-- 阿里巴巴fastjson序列化 -->
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>


        <!-- JWT -->
        <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.2</version>
        </dependency>

        <!-- 常用工具类 -->
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.10</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.0</version>
        </dependency>

        <!--工具类-->
        <dependency>
            <groupId>com.xiaoleilu</groupId>
            <artifactId>hutool-all</artifactId>
            <version>3.3.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!-- swagger2 -->
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--防止进入swagger页面报类型转换错误,排除2.9.2中的引用,手动增加1.5.21版本-->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.21</version>
        </dependency>

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.5.21</version>
        </dependency>


        <!-- 这里使用 swagger-bootstrap-ui 替代了原有丑陋的ui,拯救处女座~ -->
        <!-- https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>


        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.7</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>3.9</version>
        </dependency>



        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

②application.yml

server:
  port: 90
spring:
  application:
    name: hytc-crm-server
  #缓存配置
  cache:
    type: redis


  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    dynamic:
      #设置默认的数据源或者数据源组,默认值即为master
      primary: master
      datasource:
        #主库配置
        master:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/hytcmall_20200923?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
          username: root
          password: root

    druid:
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=50000
      filters: stat,slf4j
      initial-size: 10
      maxActive: 200
      maxPoolPreparedStatementPerConnectionSize: 200
      maxWait: 60000
      min-idle: 5
      minEvictableIdleTimeMillis: 180000
      poolPreparedStatements: true
      stat-view-servlet:
        allow: 127.0.0.1,192.168.163.1
        deny: 192.168.1.73
        #Druid 管理密码
        login-password: 123456
        #Druid 管理账号
        login-username: admin
        reset-enable: false
        url-pattern: /druid/*
      testOnBorrow: false
      testOnReturn: false
      testWhileIdle: true
      #配置一个连接在池中最小生存的时间,单位是毫秒
      timeBetweenEvictionRunsMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      web-stat-filter:
        enabled: true
        exclusions: '*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*'
        url-pattern: /*
      remove-abandoned: true
      remove-abandoned-timeout-millis: 300
      log-abandoned: true

  redis:
    # Redis数据库索引(默认为0)
    database: 15
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password:
    # 连接池最大连接数(使用负值表示没有限制)
    jedis:
      pool:
        ## 连接池最大连接数(使用负值表示没有限制)
        max-active: 20
        # 连接池最大阻塞等待时间(使用负值表示没有限制
        max-wait: -1
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0
    # 连接超时时间(毫秒)
    timeout: 5000

#mybatis
mybatis:
  mapper-locations: classpath:/mapper/*.xml
  type-aliases-package: com.hytc.admin.vo
  configuration:
    map-underscore-to-camel-case: true
    #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

#mybatisPlus
mybatis-plus:
  configuration:
    # 原生配置
    cache-enabled: false
    call-setters-on-nulls: true
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      column-underline: true
      #字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
      field-strategy: not_empty
      #数据库相关配置
      #主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
      id-type: AUTO
      #数据库大写下划线转换
      #capital-mode: true
      #逻辑删除配置
      logic-delete-value: 0
      logic-not-delete-value: 1

      #自定义填充策略接口实现
      #metaObjectHandler: com.hytc.admin.config.MetaObjectHandlerConfig
      #自定义SQL注入器
      #sql-injector: com.baomidou.springboot.xxx
      configuration:
        map-underscore-to-camel-case: true
        cache-enabled: false

    #刷新mapper 调试神器
    refresh: true
  #mybatis plus mapper文件路径
  mapperLocations: classpath:/mapper/*.xml
  #mybaits plus 实体类路径
  typeAliasesPackage: com.hytc.admin.vo
  typeEnumsPackage: ''

#  logging:
#    config: classpath:log/logback.xml
#    #com.hytc.mapper 该包打印DEBUG级别日志
#    level:
#      com:
#        hytc:
#          mapper: debug
#    path: E:hytcbusslog


logging:
  level:
    com.hytc.mall.hytcmall.mapper: debug

③自定义注解

package com.hytc.mall.hytcmall.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * 限制每个ip对每个方法的访问限制,加上时间限制
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequertIpLimit {

    /**
     * 时间类型,默认毫秒
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS ;

    /**
     * 多长时间内限制,默认 60
     * @return
     */
    long t () default 60;

    /**
     * 单位时间内能访问多少次,默认10次
     * @return
     */
    int count () default 10;

}

④aop切面

package com.hytc.mall.hytcmall.aspect;

import com.hytc.mall.hytcmall.annotation.RequertIpLimit;
import com.hytc.mall.hytcmall.cache.RequestIpLImitCache;
import com.hytc.mall.hytcmall.exception.RequestLimitException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @ClassName: RequestIpLimitAspect
 * @Description:https://blog.csdn.net/yali_aini/article/details/92653982
 * https://juejin.im/post/6844903985388716045
 * http://www.coder55.com/article/76465
 * @Author: BYP <502955177@qq.com>
 * @Date: 2020/9/25 17:37
 * @Copyright: 2019 www.tydic.com Inc. All rights reserved.
 * 注意:本内容仅限于弘毅天承信息技术股份有限公司内部传阅,禁止外泄以及用于其他的商业目
 */

@Aspect//首先,这个@Aspect注释告诉Spring这是个切面类
@Component//然后@Compoment将转换成Spring容器中的bean或者是代理bean
@Order(1)
@Slf4j
public class RequestIpLimitAspect {

    @Autowired
    private RequestIpLImitCache requestIpLImitCache;


    @Pointcut(value = "@annotation(com.hytc.mall.hytcmall.annotation.RequertIpLimit)" )
    public void requestLimitPointCut(){};

    @Around(value = "requestLimitPointCut())")
    public Object requestLimitAround(ProceedingJoinPoint pjp) {

        // 获取 request , 然后获取访问 ip
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//这个RequestContextHolder是Springmvc提供来获得请求的东西
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        String ip = request.getRemoteAddr();
        String Ip = RequestIpLimitAspect.getRequestIp(request);
        if(StringUtils.isEmpty(Ip)){
            throw new RequestLimitException("300","非法访问!ip不能为空");
        }
        log.info("访问的ip地址为:{}", ip);
        MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
        RequertIpLimit limit = methodSignature.getMethod().getAnnotation(RequertIpLimit.class);
        // 限制访问次数
        int count = limit.count();

        RequestMapping methodAnnotation = methodSignature.getMethod().getAnnotation(RequestMapping.class);
        String url = methodAnnotation.value()[0];
        String key = "_" + url + "_" + Ip +"_";
        if(requestIpLImitCache.count(key) > count ){
            log.info("当前请求次数为:{},该次请求已经超过了规定时间范围内请求的最大次数", requestIpLImitCache.count(key));
            throw new RequestLimitException("300","访问失败!超过访问限制!");
        }
        // 将访问存进缓存
        requestIpLImitCache.add(key+System.currentTimeMillis(), "1", limit.timeUnit(), limit.t());
        //记录开始时间
        long startTime = System.currentTimeMillis();
        //获取传入目标方法的参数
        Object[] args = pjp.getArgs();
        Object result = null;
        try {
            // 执行访问并返回数据
            result = pjp.proceed(args);
            return result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        } finally {
            log.info("当前请求的Key是:{}", key);
            //记录结束时间
            long endTime = System.currentTimeMillis();
            log.info("请求耗时为:{}", (endTime - startTime));
        }
    }




    public static String getRequestIp(HttpServletRequest request){
        // 获取请求IP
        String ip = request.getHeader("x-forwarded-for");
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)){
            ip = "" + request.getHeader("Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)){
            ip = "" + request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)){
            ip = "" + request.getRemoteAddr();
        }
        if("0.0.0.0".equals(ip) || "0.0.0.0.0.0.1".equals(ip) || "localhost".equals(ip) || "127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        return ip;
    }


}

⑤Redis工具类

package com.hytc.mall.hytcmall.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @ClassName: RedisConfig
 * @Description:https://www.cnblogs.com/lzhdonald/p/11560002.html
 * @Author: BYP <502955177@qq.com>
 * @Date: 2020/9/24 10:40
 * @Copyright: 2019 www.tydic.com Inc. All rights reserved.
 * 注意:本内容仅限于弘毅天承信息技术股份有限公司内部传阅,禁止外泄以及用于其他的商业目
 */

@Configuration
@EnableCaching
public class RedisConfig {

    //配置使用jedis作为redis的客户端
    @Bean
    public JedisConnectionFactory jedisConnectionFactory(){

        return new JedisConnectionFactory();
    }

    @Bean
    public RedisTemplate<Object,Object> redisTemplate(){
        // 创建RedisTemplate<String, Object>对象
        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
        // 配置连接工厂
        template.setConnectionFactory(jedisConnectionFactory());

        // 定义Jackson2JsonRedisSerializer序列化对象
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        StringRedisSerializer stringSerial = new StringRedisSerializer();
        // redis key 序列化方式使用stringSerial
        template.setKeySerializer(stringSerial);
        // redis value 序列化方式使用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // redis hash key 序列化方式使用stringSerial
        template.setHashKeySerializer(stringSerial);
        // redis hash value 序列化方式使用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;

    }
}

package com.hytc.mall.hytcmall.cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;


/**
 * @ClassName: RequestIpLImitCache
 * @Description:
 * @Author: BYP <502955177@qq.com>
 * @Date: 2020/9/25 17:48
 * @Copyright: 2019 www.tydic.com Inc. All rights reserved.
 * 注意:本内容仅限于弘毅天承信息技术股份有限公司内部传阅,禁止外泄以及用于其他的商业目
 */

@Component
public class RequestIpLImitCache {

    private static final String PREFIX = "REQUEST_IP_LIMIT:";

    @Autowired
    private RedisTemplate redisTemplate;

    public void add(String key , String value , TimeUnit timeUnit , long t){
        redisTemplate.opsForValue().set(PREFIX + key, value, t, timeUnit);
    }

    public int count(String key ){
        return redisTemplate.keys(PREFIX + key + "*").size();
    }

}

⑦spring全局事务GlobalExceptionHandler

package com.hytc.mall.hytcmall.exception;

import com.google.common.collect.Maps;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * @ClassName: GlobalExceptionHandler
 * @Description:全局异常处理
 * @Author: BYP <502955177@qq.com>
 * @Date: 2020/9/27 10:03
 * @Copyright: 2019 www.tydic.com Inc. All rights reserved.
 * 注意:本内容仅限于弘毅天承信息技术股份有限公司内部传阅,禁止外泄以及用于其他的商业目
 */

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RequestLimitException.class)
    public Map defaultExceptionHandler(HttpServletRequest request,Exception e){
        Map<String,Object>map = Maps.newConcurrentMap();
        if(e instanceof RequestLimitException){
            RequestLimitException limit = (RequestLimitException) e;
            map.put("code", limit.getCode());
            map.put("msg", limit.getMsg());
        }else{
            map.put("code", -1);
            map.put("msg", "系统异常");
        }
        //未知错误
        return map;
    }
}

⑧请求限制异常

package com.hytc.mall.hytcmall.exception;

import lombok.Data;

/**
 * @ClassName: RequestLimit
 * @Description:
 * @Author: BYP <502955177@qq.com>
 * @Date: 2020/9/27 9:59
 * @Copyright: 2019 www.tydic.com Inc. All rights reserved.
 * 注意:本内容仅限于弘毅天承信息技术股份有限公司内部传阅,禁止外泄以及用于其他的商业目
 */

@Data
public class RequestLimitException extends RuntimeException{

    /*错误码*/
    private String code;

    /*错误提示*/
    private String msg;

    public RequestLimitException(){

    }

    public RequestLimitException(String code,String msg){
        this.code = code;
        this.msg = msg;

    }
}

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