SpringBoot+shiro整合學(xué)習(xí)之登錄認(rèn)證和權(quán)限控制

學(xué)習(xí)任務(wù)目標(biāo)

  1. 用戶必須要登陸之后才能訪問(wèn)定義鏈接书闸,否則跳轉(zhuǎn)到登錄頁(yè)面紊选。

  2. 對(duì)鏈接進(jìn)行權(quán)限控制,只有當(dāng)當(dāng)前登錄用戶有這個(gè)鏈接訪問(wèn)權(quán)限才可以訪問(wèn)姥敛,否則跳轉(zhuǎn)到指定頁(yè)面奸焙。

  3. 輸入錯(cuò)誤密碼用戶名或則用戶被設(shè)置為靜止登錄,返回相應(yīng)json串信息。

我是用的是之前搭建的一個(gè)springboot+mybatisplus+jsp的一個(gè)基礎(chǔ)框架与帆。在這之上進(jìn)行shiro的整合金顿。需要的同學(xué)可以去我的碼云下載。

個(gè)人博客:http://z77z.oschina.io/

此項(xiàng)目下載地址:https://git.oschina.net/z77z/springboot_mybatisplus

導(dǎo)入shiro依賴包到pom.xml

<!-- shiro權(quán)限控制框架 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>

采用RBAC模式建立數(shù)據(jù)庫(kù)

RBAC 是基于角色的訪問(wèn)控制(Role-Based Access Control )在 RBAC 中鲤桥,權(quán)限與角色相關(guān)聯(lián),用戶通過(guò)成為適當(dāng)角色的成員而得到這些角色的權(quán)限渠概。這就極大地簡(jiǎn)化了權(quán)限的管理茶凳。這樣管理都是層級(jí)相互依賴的,權(quán)限賦予給角色播揪,而把角色又賦予用戶贮喧,這樣的權(quán)限設(shè)計(jì)很清楚,管理起來(lái)很方便猪狈。

/*表結(jié)構(gòu)插入*/
DROP TABLE IF EXISTS `u_permission`;

CREATE TABLE `u_permission` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `url` varchar(256) DEFAULT NULL COMMENT 'url地址',
  `name` varchar(64) DEFAULT NULL COMMENT 'url描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;

/*Table structure for table `u_role` */

DROP TABLE IF EXISTS `u_role`;

CREATE TABLE `u_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL COMMENT '角色名稱',
  `type` varchar(10) DEFAULT NULL COMMENT '角色類(lèi)型',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

/*Table structure for table `u_role_permission` */

DROP TABLE IF EXISTS `u_role_permission`;

CREATE TABLE `u_role_permission` (
  `rid` bigint(20) DEFAULT NULL COMMENT '角色I(xiàn)D',
  `pid` bigint(20) DEFAULT NULL COMMENT '權(quán)限ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Table structure for table `u_user` */

DROP TABLE IF EXISTS `u_user`;

CREATE TABLE `u_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `nickname` varchar(20) DEFAULT NULL COMMENT '用戶昵稱',
  `email` varchar(128) DEFAULT NULL COMMENT '郵箱|登錄帳號(hào)',
  `pswd` varchar(32) DEFAULT NULL COMMENT '密碼',
  `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `last_login_time` datetime DEFAULT NULL COMMENT '最后登錄時(shí)間',
  `status` bigint(1) DEFAULT '1' COMMENT '1:有效箱沦,0:禁止登錄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

/*Table structure for table `u_user_role` */

DROP TABLE IF EXISTS `u_user_role`;

CREATE TABLE `u_user_role` (
  `uid` bigint(20) DEFAULT NULL COMMENT '用戶ID',
  `rid` bigint(20) DEFAULT NULL COMMENT '角色I(xiàn)D'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Dao層代碼的編寫(xiě)

Dao層的entity,service雇庙,mapper等我是采用mybatisplus的代碼自動(dòng)生成工具生成的谓形,具備了單表的增刪改查功能和分頁(yè)功能,比較方便疆前,這里我就不貼代碼了寒跳。

配置shiro

ShiroConfig.java

/**
 * @author 作者 z77z
 * @date 創(chuàng)建時(shí)間:2017年2月10日 下午1:16:38
 * 
 */
@Configuration
public class ShiroConfig {
    /**
     * ShiroFilterFactoryBean 處理攔截資源文件問(wèn)題。
     * 注意:?jiǎn)为?dú)一個(gè)ShiroFilterFactoryBean配置是或報(bào)錯(cuò)的竹椒,以為在
     * 初始化ShiroFilterFactoryBean的時(shí)候需要注入:SecurityManager
     *
     * Filter Chain定義說(shuō)明 1童太、一個(gè)URL可以配置多個(gè)Filter,使用逗號(hào)分隔 2胸完、當(dāng)設(shè)置多個(gè)過(guò)濾器時(shí)书释,全部驗(yàn)證通過(guò),才視為通過(guò)
     * 3赊窥、部分過(guò)濾器可指定參數(shù)爆惧,如perms,roles
     *
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必須設(shè)置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁(yè)面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登錄成功后要跳轉(zhuǎn)的鏈接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授權(quán)界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        // 攔截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不會(huì)被攔截的鏈接 順序判斷
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/ajaxLogin", "anon");

        // 配置退出過(guò)濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實(shí)現(xiàn)了
        filterChainDefinitionMap.put("/logout", "logout");

        filterChainDefinitionMap.put("/add", "perms[權(quán)限添加]");

        // <!-- 過(guò)濾鏈定義锨能,從上向下順序執(zhí)行检激,一般將 /**放在最為下邊 -->:這是一個(gè)坑呢,一不小心代碼就不好使了;
        // <!-- authc:所有url都必須認(rèn)證通過(guò)才可以訪問(wèn); anon:所有url都都可以匿名訪問(wèn)-->
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        System.out.println("Shiro攔截器工廠類(lèi)注入成功");
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 設(shè)置realm.
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    /**
     * 身份認(rèn)證realm; (這個(gè)需要自己寫(xiě)腹侣,賬號(hào)密碼校驗(yàn)叔收;權(quán)限等)
     * 
     * @return
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        return myShiroRealm;
    }
}

登錄認(rèn)證實(shí)現(xiàn)

在認(rèn)證、授權(quán)內(nèi)部實(shí)現(xiàn)機(jī)制中都有提到傲隶,最終處理都將交給Real進(jìn)行處理饺律。因?yàn)樵赟hiro中,最終是通過(guò)Realm來(lái)獲取應(yīng)用程序中的用戶跺株、角色及權(quán)限信息的复濒。通常情況下脖卖,在Realm中會(huì)直接從我們的數(shù)據(jù)源中獲取Shiro需要的驗(yàn)證信息∏删保可以說(shuō)畦木,Realm是專用于安全框架的DAO.

Shiro的認(rèn)證過(guò)程最終會(huì)交由Realm執(zhí)行,這時(shí)會(huì)調(diào)用Realm的getAuthenticationInfo(token)方法砸泛。
該方法主要執(zhí)行以下操作:

1十籍、檢查提交的進(jìn)行認(rèn)證的令牌信息

2、根據(jù)令牌信息從數(shù)據(jù)源(通常為數(shù)據(jù)庫(kù))中獲取用戶信息

3唇礁、對(duì)用戶信息進(jìn)行匹配驗(yàn)證勾栗。

4、驗(yàn)證通過(guò)將返回一個(gè)封裝了用戶信息的AuthenticationInfo實(shí)例盏筐。

5围俘、驗(yàn)證失敗則拋出AuthenticationException異常信息。

而在我們的應(yīng)用程序中要做的就是自定義一個(gè)Realm類(lèi)琢融,繼承AuthorizingRealm抽象類(lèi)界牡,重載doGetAuthenticationInfo
(),重寫(xiě)獲取用戶信息的方法漾抬。

doGetAuthenticationInfo的重寫(xiě)

/**
* 認(rèn)證信息.(身份驗(yàn)證) : Authentication 是用來(lái)驗(yàn)證用戶身份
 * 
 * @param token
 * @return
 * @throws AuthenticationException
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
        AuthenticationToken authcToken) throws AuthenticationException {
    System.out.println("身份認(rèn)證方法:MyShiroRealm.doGetAuthenticationInfo()");

    ShiroToken token = (ShiroToken) authcToken;
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("nickname", token.getUsername());
    map.put("pswd", token.getPswd());
    SysUser user = null;
    // 從數(shù)據(jù)庫(kù)獲取對(duì)應(yīng)用戶名密碼的用戶
    List<SysUser> userList = sysUserService.selectByMap(map);
    if(userList.size()!=0){
        user = userList.get(0);
    }
    if (null == user) {
        throw new AccountException("帳號(hào)或密碼不正確欢揖!");
    }else if(user.getStatus()==0){
        /**
         * 如果用戶的status為禁用。那么就拋出<code>DisabledAccountException</code>
         */
        throw new DisabledAccountException("帳號(hào)已經(jīng)禁止登錄奋蔚!");
    }else{
        //更新登錄時(shí)間 last login time
        user.setLastLoginTime(new Date());
        sysUserService.updateById(user);
    }
    return new SimpleAuthenticationInfo(user, user.getPswd(), getName());
}

通俗的說(shuō)她混,這個(gè)的重寫(xiě)就是我們第一個(gè)學(xué)習(xí)目標(biāo)的實(shí)現(xiàn)。

鏈接權(quán)限的實(shí)現(xiàn)

shiro的權(quán)限授權(quán)是通過(guò)繼承AuthorizingRealm抽象類(lèi)泊碑,重載doGetAuthorizationInfo();

當(dāng)訪問(wèn)到頁(yè)面的時(shí)候坤按,鏈接配置了相應(yīng)的權(quán)限或者shiro標(biāo)簽才會(huì)執(zhí)行此方法否則不會(huì)執(zhí)行,所以如果只是簡(jiǎn)單的身份認(rèn)證沒(méi)有權(quán)限的控制的話馒过,那么這個(gè)方法可以不進(jìn)行實(shí)現(xiàn)臭脓,直接返回null即可。

在這個(gè)方法中主要是使用類(lèi):SimpleAuthorizationInfo

進(jìn)行角色的添加和權(quán)限的添加腹忽。

authorizationInfo.addRole(role.getRole());

authorizationInfo.addStringPermission(p.getPermission());

當(dāng)然也可以添加set集合:roles是從數(shù)據(jù)庫(kù)查詢的當(dāng)前用戶的角色来累,stringPermissions是從數(shù)據(jù)庫(kù)查詢的當(dāng)前用戶對(duì)應(yīng)的權(quán)限

authorizationInfo.setRoles(roles);

authorizationInfo.setStringPermissions(stringPermissions);

就是說(shuō)如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "perms[權(quán)限添加]");
就說(shuō)明訪問(wèn)/add這個(gè)鏈接必須要有“權(quán)限添加”這個(gè)權(quán)限才可以訪問(wèn),

如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "roles[100002]窘奏,perms[權(quán)限添加]");
就說(shuō)明訪問(wèn)/add這個(gè)鏈接必須要有“權(quán)限添加”這個(gè)權(quán)限和具有“100002”這個(gè)角色才可以訪問(wèn)嘹锁。

/**
* 授權(quán)
 */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
        PrincipalCollection principals) {
    System.out.println("權(quán)限認(rèn)證方法:MyShiroRealm.doGetAuthenticationInfo()");
    SysUser token = (SysUser)SecurityUtils.getSubject().getPrincipal();
    String userId = token.getId();
    SimpleAuthorizationInfo info =  new SimpleAuthorizationInfo();
    //根據(jù)用戶ID查詢角色(role),放入到Authorization里着裹。
    /*Map<String, Object> map = new HashMap<String, Object>();
    map.put("user_id", userId);
    List<SysRole> roleList = sysRoleService.selectByMap(map);
    Set<String> roleSet = new HashSet<String>();
    for(SysRole role : roleList){
        roleSet.add(role.getType());
    }*/
    //實(shí)際開(kāi)發(fā)领猾,當(dāng)前登錄用戶的角色和權(quán)限信息是從數(shù)據(jù)庫(kù)來(lái)獲取的,我這里寫(xiě)死是為了方便測(cè)試
    Set<String> roleSet = new HashSet<String>();
    roleSet.add("100002");
    info.setRoles(roleSet);
    //根據(jù)用戶ID查詢權(quán)限(permission),放入到Authorization里摔竿。
    /*List<SysPermission> permissionList = sysPermissionService.selectByMap(map);
    Set<String> permissionSet = new HashSet<String>();
    for(SysPermission Permission : permissionList){
        permissionSet.add(Permission.getName());
    }*/
    Set<String> permissionSet = new HashSet<String>();
    permissionSet.add("權(quán)限添加");
    info.setStringPermissions(permissionSet);
       return info;
}

這個(gè)類(lèi)的實(shí)現(xiàn)是完成了我們學(xué)習(xí)目標(biāo)的第二個(gè)任務(wù)面粮。

編寫(xiě)web層的代碼

登錄頁(yè)面:

controller

//跳轉(zhuǎn)到登錄表單頁(yè)面
@RequestMapping(value="login")
public String login() {
    return "login";
}

/**
 * ajax登錄請(qǐng)求
 * @param username
 * @param password
 * @return
 */
@RequestMapping(value="ajaxLogin",method=RequestMethod.POST)
@ResponseBody
public Map<String,Object> submitLogin(String username, String password,Model model) {
    Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
    try {
        
        ShiroToken token = new ShiroToken(username, password);
        SecurityUtils.getSubject().login(token);
        resultMap.put("status", 200);
        resultMap.put("message", "登錄成功");

    } catch (Exception e) {
        resultMap.put("status", 500);
        resultMap.put("message", e.getMessage());
    }
    return resultMap;
}

jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + path;
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript"
    src="<%=basePath%>/static/js/jquery-1.11.3.js"></script>
<title>登錄</title>
</head>
<body>
    錯(cuò)誤信息:
    <h4 id="erro"></h4>
    <form>
        <p>
            賬號(hào):<input type="text" name="username" id="username" value="admin" />
        </p>
        <p>
            密碼:<input type="text" name="password" id="password" value="123" />
        </p>
        <p>
            <input type="button" id="ajaxLogin" value="登錄" />
        </p>
    </form>
</body>
<script>
    var username = $("#username").val();
    var password = $("#password").val();
    $("#ajaxLogin").click(function() {
        $.post("/ajaxLogin", {
            "username" : username,
            "password" : password
        }, function(result) {
            if (result.status == 200) {
                location.href = "/index";
            } else {
                $("#erro").html(result.message);
            }
        });
    });
</script>
</html>

主頁(yè)頁(yè)面

controller

//跳轉(zhuǎn)到主頁(yè)
@RequestMapping(value="index")
public String index() {
    return "index";
}

/**
* 退出
 * @return
 */
@RequestMapping(value="logout",method =RequestMethod.GET)
@ResponseBody
public Map<String,Object> logout(){
    Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
    try {
        //退出
        SecurityUtils.getSubject().logout();
    } catch (Exception e) {
        System.err.println(e.getMessage());
    }
    return resultMap;
}

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + path;
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript"
    src="<%=basePath%>/static/js/jquery-1.11.3.js"></script>
<title>Insert title here</title>
</head>
<body>
    helloJsp
    <input type="button" id="logout" value="退出登錄" />
</body>
<script type="text/javascript">
    $("#logout").click(function(){
        location.href="/logout";
    });
</script>
</html>

添加操作頁(yè)面

controller

@RequestMapping(value="add")
public String add() {
    return "add";
}

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + path;
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript"
    src="<%=basePath%>/static/js/jquery-1.11.3.js"></script>
<title>Insert title here</title>
</head>
<body>
具有添加權(quán)限
</body>
</html>

測(cè)試

任務(wù)一

編寫(xiě)好后就可以啟動(dòng)程序,訪問(wèn)index頁(yè)面继低,由于沒(méi)有登錄就會(huì)跳轉(zhuǎn)到login頁(yè)面熬苍。

登錄之后就會(huì)跳轉(zhuǎn)到index頁(yè)面,點(diǎn)擊退出登錄后袁翁,有直接在瀏覽器中輸入index頁(yè)面訪問(wèn)柴底,又會(huì)跳轉(zhuǎn)到login頁(yè)面

上面這些操作時(shí)候觸發(fā)MyShiroRealm.doGetAuthenticationInfo()這個(gè)方法,也就是登錄認(rèn)證的方法梦裂。


任務(wù)二

登錄之后訪問(wèn)add頁(yè)面成功訪問(wèn),在shiro配置文件中改變add的訪問(wèn)權(quán)限為

filterChainDefinitionMap.put("/add","perms[權(quán)限刪除]");

再重新啟動(dòng)程序盖淡,登錄后訪問(wèn)年柠,會(huì)重定向到/403頁(yè)面,由于沒(méi)有編寫(xiě)403頁(yè)面褪迟,報(bào)404錯(cuò)誤冗恨。

上面這些操作,會(huì)觸發(fā)權(quán)限認(rèn)證方法:MyShiroRealm.doGetAuthorizationInfo()味赃,每訪問(wèn)一次就會(huì)觸發(fā)一次掀抹。


任務(wù)三

輸入錯(cuò)誤的用戶名或則密碼,返回“帳號(hào)或密碼不正確心俗!”的錯(cuò)誤信息傲武,在數(shù)據(jù)庫(kù)中把一個(gè)用戶的狀態(tài)改為被禁用,再登陸城榛,提示“帳號(hào)已經(jīng)禁止登錄揪利!”的錯(cuò)誤信息

上面的操作,是在MyShiroRealm.doGetAuthenticationInfo()登錄認(rèn)證的方法中實(shí)現(xiàn)的狠持,通過(guò)查詢數(shù)據(jù)庫(kù)判斷當(dāng)前登錄用戶是否被禁用疟位,具體可以去看源碼。

總結(jié)

當(dāng)然shiro很強(qiáng)大喘垂,這僅僅是完成了登錄認(rèn)證和權(quán)限管理這兩個(gè)功能甜刻,接下來(lái)我會(huì)繼續(xù)學(xué)習(xí)和分享,說(shuō)說(shuō)接下來(lái)的學(xué)習(xí)路線吧:

  1. shiro+redis集成正勒,避免每次訪問(wèn)有權(quán)限的鏈接都會(huì)去執(zhí)行MyShiroRealm.doGetAuthenticationInfo()方法來(lái)查詢當(dāng)前用戶的權(quán)限得院,因?yàn)閷?shí)際情況中權(quán)限是不會(huì)經(jīng)常變得,這樣就可以使用redis進(jìn)行權(quán)限的緩存章贞。

  2. 實(shí)現(xiàn)shiro鏈接權(quán)限的動(dòng)態(tài)加載尿招,之前要添加一個(gè)鏈接的權(quán)限,要在shiro的配置文件中添加filterChainDefinitionMap.put("/add", "roles[100002],perms[權(quán)限添加]")就谜,這樣很不方便管理怪蔑,一種方法是將鏈接的權(quán)限使用數(shù)據(jù)庫(kù)進(jìn)行加載,另一種是通過(guò)init配置文件的方式讀取丧荐。

  3. Shiro 自定義權(quán)限校驗(yàn)Filter定義缆瓣,及功能實(shí)現(xiàn)。

  4. Shiro Ajax請(qǐng)求權(quán)限不滿足虹统,攔截后解決方案弓坞。這里有一個(gè)前提,我們知道Ajax不能做頁(yè)面redirect和forward跳轉(zhuǎn)车荔,所以Ajax請(qǐng)求假如沒(méi)登錄渡冻,那么這個(gè)請(qǐng)求給用戶的感覺(jué)就是沒(méi)有任何反應(yīng),而用戶又不知道用戶已經(jīng)退出了忧便。

  5. Shiro JSP標(biāo)簽使用族吻。

  6. Shiro 登錄后跳轉(zhuǎn)到最后一個(gè)訪問(wèn)的頁(yè)面

  7. 在線顯示,在線用戶管理(踢出登錄)珠增。

  8. 登錄注冊(cè)密碼加密傳輸超歌。

  9. 集成驗(yàn)證碼。

  10. 記住我的功能蒂教。關(guān)閉瀏覽器后還是登錄狀態(tài)巍举。

  11. 還有沒(méi)有想到的后面再說(shuō),歡迎大家提出一些建議凝垛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末懊悯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子梦皮,更是在濱河造成了極大的恐慌定枷,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件届氢,死亡現(xiàn)場(chǎng)離奇詭異欠窒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)退子,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)岖妄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人寂祥,你說(shuō)我怎么就攤上這事荐虐。” “怎么了丸凭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵福扬,是天一觀的道長(zhǎng)腕铸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铛碑,這世上最難降的妖魔是什么狠裹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮汽烦,結(jié)果婚禮上涛菠,老公的妹妹穿的比我還像新娘。我一直安慰自己撇吞,他們只是感情好俗冻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著牍颈,像睡著了一般迄薄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上煮岁,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天讥蔽,我揣著相機(jī)與錄音,去河邊找鬼人乓。 笑死勤篮,一個(gè)胖子當(dāng)著我的面吹牛都毒,可吹牛的內(nèi)容都是我干的色罚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼账劲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼戳护!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起瀑焦,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤腌且,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后榛瓮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體铺董,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年禀晓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了精续。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粹懒,死狀恐怖重付,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凫乖,我是刑警寧澤确垫,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布弓颈,位于F島的核電站,受9級(jí)特大地震影響删掀,放射性物質(zhì)發(fā)生泄漏翔冀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一爬迟、第九天 我趴在偏房一處隱蔽的房頂上張望橘蜜。 院中可真熱鬧,春花似錦付呕、人聲如沸计福。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)象颖。三九已至,卻和暖如春姆钉,著一層夾襖步出監(jiān)牢的瞬間说订,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工潮瓶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陶冷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓毯辅,卻偏偏與公主長(zhǎng)得像埂伦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子思恐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

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