Laravel 5.5 使用 Jwt-Auth 實(shí)現(xiàn) API 用戶認(rèn)證以及無(wú)痛刷新訪問(wèn)令牌

最近在做一個(gè)公司的項(xiàng)目,前端使用 Vue.js捶障,后端使用 Laravel 構(gòu)建 Api 服務(wù)饥追,用戶認(rèn)證的包本來(lái)是想用 Laravel Passport 的,但是感覺(jué)有點(diǎn)麻煩雅潭,于是使用了 jwt-auth 揭厚。

安裝

jwt-auth 最新版本是 1.0.0 rc.1 版本,已經(jīng)支持了 Laravel 5.5扶供。如果你使用的是 Laravel 5.5 版本筛圆,可以使用如下命令安裝。根據(jù)評(píng)論區(qū) @tradzero 兄弟的建議椿浓,如果你是 Laravel 5.5 以下版本太援,也推薦使用最新版本,RC.1 前的版本都存在多用戶token認(rèn)證的安全問(wèn)題扳碍。

$ composer require tymon/jwt-auth 1.0.0-rc.1

配置

添加服務(wù)提供商

將下面這行添加至 config/app.php 文件 providers 數(shù)組中:

app.php

'providers' => [

    ...

    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
]

發(fā)布配置文件

在你的 shell 中運(yùn)行如下命令發(fā)布 jwt-auth 的配置文件:

shell

$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

此命令會(huì)在 config 目錄下生成一個(gè) jwt.php 配置文件提岔,你可以在此進(jìn)行自定義配置。

生成密鑰

jwt-auth 已經(jīng)預(yù)先定義好了一個(gè) Artisan 命令方便你生成 Secret笋敞,你只需要在你的 shell 中運(yùn)行如下命令即可:

shell

$ php artisan jwt:secret

此命令會(huì)在你的 .env 文件中新增一行 JWT_SECRET=secret碱蒙。

配置 Auth guard

config/auth.php 文件中,你需要將 guards/driver 更新為 jwt

auth.php

'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],

...

'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

只有在使用 Laravel 5.2 及以上版本的情況下才能使用夯巷。

更改 Model

如果需要使用 jwt-auth 作為用戶認(rèn)證赛惩,我們需要對(duì)我們的 User 模型進(jìn)行一點(diǎn)小小的改變,實(shí)現(xiàn)一個(gè)接口趁餐,變更后的 User 模型如下:

User.php

<?php

namespace App;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject
{
   use Notifiable;

   // Rest omitted for brevity

   /**
    * Get the identifier that will be stored in the subject claim of the JWT.
    *
    * @return mixed
    */
   public function getJWTIdentifier()
   {
       return $this->getKey();
   }

   /**
    * Return a key value array, containing any custom claims to be added to the JWT.
    *
    * @return array
    */
   public function getJWTCustomClaims()
   {
       return [];
   }
}

配置項(xiàng)詳解

jwt.php

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Secret
    |--------------------------------------------------------------------------
    |
    | 用于加密生成 token 的 secret
    |
    */

    'secret' => env('JWT_SECRET'),

    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Keys
    |--------------------------------------------------------------------------
    |
    | 如果你在 .env 文件中定義了 JWT_SECRET 的隨機(jī)字符串
    | 那么 jwt 將會(huì)使用 對(duì)稱算法 來(lái)生成 token
    | 如果你沒(méi)有定有喷兼,那么jwt 將會(huì)使用如下配置的公鑰和私鑰來(lái)生成 token
    |
    */

    'keys' => [

        /*
        |--------------------------------------------------------------------------
        | Public Key
        |--------------------------------------------------------------------------
        |
        | 公鑰
        |
        */

        'public' => env('JWT_PUBLIC_KEY'),

        /*
        |--------------------------------------------------------------------------
        | Private Key
        |--------------------------------------------------------------------------
        |
        | 私鑰
        |
        */

        'private' => env('JWT_PRIVATE_KEY'),

        /*
        |--------------------------------------------------------------------------
        | Passphrase
        |--------------------------------------------------------------------------
        |
        | 私鑰的密碼。 如果沒(méi)有設(shè)置后雷,可以為 null季惯。
        |
        */

        'passphrase' => env('JWT_PASSPHRASE'),

    ],

    /*
    |--------------------------------------------------------------------------
    | JWT time to live
    |--------------------------------------------------------------------------
    |
    | 指定 access_token 有效的時(shí)間長(zhǎng)度(以分鐘為單位)吠各,默認(rèn)為1小時(shí),您也可以將其設(shè)置為空星瘾,以產(chǎn)生永不過(guò)期的標(biāo)記
    |
    */

    'ttl' => env('JWT_TTL', 60),

    /*
    |--------------------------------------------------------------------------
    | Refresh time to live
    |--------------------------------------------------------------------------
    |
    | 指定 access_token 可刷新的時(shí)間長(zhǎng)度(以分鐘為單位)走孽。默認(rèn)的時(shí)間為 2 周惧辈。
    | 大概意思就是如果用戶有一個(gè) access_token琳状,那么他可以帶著他的 access_token 
    | 過(guò)來(lái)領(lǐng)取新的 access_token,直到 2 周的時(shí)間后盒齿,他便無(wú)法繼續(xù)刷新了念逞,需要重新登錄。
    |
    */

    'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),

    /*
    |--------------------------------------------------------------------------
    | JWT hashing algorithm
    |--------------------------------------------------------------------------
    |
    | 指定將用于對(duì)令牌進(jìn)行簽名的散列算法边翁。
    |
    */

    'algo' => env('JWT_ALGO', 'HS256'),

    /*
    |--------------------------------------------------------------------------
    | Required Claims
    |--------------------------------------------------------------------------
    |
    | 指定必須存在于任何令牌中的聲明翎承。
    | 
    |
    */

    'required_claims' => [
        'iss',
        'iat',
        'exp',
        'nbf',
        'sub',
        'jti',
    ],

    /*
    |--------------------------------------------------------------------------
    | Persistent Claims
    |--------------------------------------------------------------------------
    |
    | 指定在刷新令牌時(shí)要保留的聲明密鑰。
    |
    */

    'persistent_claims' => [
        // 'foo',
        // 'bar',
    ],

    /*
    |--------------------------------------------------------------------------
    | Blacklist Enabled
    |--------------------------------------------------------------------------
    |
    | 為了使令牌無(wú)效符匾,您必須啟用黑名單叨咖。
    | 如果您不想或不需要此功能,請(qǐng)將其設(shè)置為 false啊胶。
    |
    */

    'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),

    /*
    | -------------------------------------------------------------------------
    | Blacklist Grace Period
    | -------------------------------------------------------------------------
    |
    | 當(dāng)多個(gè)并發(fā)請(qǐng)求使用相同的JWT進(jìn)行時(shí)甸各,
    | 由于 access_token 的刷新 ,其中一些可能會(huì)失敗
    | 以秒為單位設(shè)置請(qǐng)求時(shí)間以防止并發(fā)的請(qǐng)求失敗焰坪。
    |
    */

    'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),

    /*
    |--------------------------------------------------------------------------
    | Providers
    |--------------------------------------------------------------------------
    |
    | 指定整個(gè)包中使用的各種提供程序趣倾。
    |
    */

    'providers' => [

        /*
        |--------------------------------------------------------------------------
        | JWT Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于創(chuàng)建和解碼令牌的提供程序。
        |
        */

        'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class,

        /*
        |--------------------------------------------------------------------------
        | Authentication Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于對(duì)用戶進(jìn)行身份驗(yàn)證的提供程序某饰。
        |
        */

        'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,

        /*
        |--------------------------------------------------------------------------
        | Storage Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于在黑名單中存儲(chǔ)標(biāo)記的提供程序儒恋。
        |
        */

        'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,

    ],

];

自定義認(rèn)證中間件

先來(lái)說(shuō)明一下我想要達(dá)成的效果,我希望用戶提供賬號(hào)密碼前來(lái)登錄黔漂。如果登錄成功诫尽,那么我會(huì)給前端頒發(fā)一個(gè) access _token ,設(shè)置在 header 中以請(qǐng)求需要用戶認(rèn)證的路由炬守。

同時(shí)我希望如果用戶的令牌如果過(guò)期了牧嫉,可以暫時(shí)通過(guò)此次請(qǐng)求,并在此次請(qǐng)求中刷新該用戶的 access _token劳较,最后在響應(yīng)頭中將新的 access _token 返回給前端驹止,這樣子可以無(wú)痛的刷新 access _token ,用戶可以獲得一個(gè)很良好的體驗(yàn)观蜗,所以開(kāi)始動(dòng)手寫代碼臊恋。

執(zhí)行如下命令以新建一個(gè)中間件:

php artisan make:middleware RefreshToken

中間件代碼如下:

RefreshToken.php

<?php

namespace App\Http\Middleware;

use Auth;
use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

// 注意,我們要繼承的是 jwt 的 BaseMiddleware
class RefreshToken extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     *
     * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // 檢查此次請(qǐng)求中是否帶有 token墓捻,如果沒(méi)有則拋出異常抖仅。 
        $this->checkForToken($request);

       // 使用 try 包裹坊夫,以捕捉 token 過(guò)期所拋出的 TokenExpiredException  異常
        try {
            // 檢測(cè)用戶的登錄狀態(tài),如果正常則通過(guò)
            if ($this->auth->parseToken()->authenticate()) {
                return $next($request);
            }
            throw new UnauthorizedHttpException('jwt-auth', '未登錄');
        } catch (TokenExpiredException $exception) {
          // 此處捕獲到了 token 過(guò)期所拋出的 TokenExpiredException 異常撤卢,我們?cè)谶@里需要做的是刷新該用戶的 token 并將它添加到響應(yīng)頭中
            try {
                // 刷新用戶的 token
                $token = $this->auth->refresh();
               // 使用一次性登錄以保證此次請(qǐng)求的成功
                Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
            } catch (JWTException $exception) {
               // 如果捕獲到此異常环凿,即代表 refresh 也過(guò)期了,用戶無(wú)法刷新令牌放吩,需要重新登錄智听。
                throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
            }
        }

        // 在響應(yīng)頭中返回新的 token
        return $this->setAuthenticationHeader($next($request), $token);
    }
}

設(shè)置 Axios 攔截器

我選用的 HTTP 請(qǐng)求套件是 axios。為了達(dá)到無(wú)痛刷新 token 的效果渡紫,我們需要對(duì) axios 定義一個(gè)攔截器到推,用以接收我們刷新的 Token,代碼如下:

app.js

import Vue from 'vue'
import router from './router'
import store from './store'
import iView from 'iview'
import 'iview/dist/styles/iview.css'

Vue.use(iView)

new Vue({
    el: '#app',
    router,
    store,
    created() {
        // 自定義的 axios 響應(yīng)攔截器
        this.$axios.interceptors.response.use((response) => {
            // 判斷一下響應(yīng)中是否有 token惕澎,如果有就直接使用此 token 替換掉本地的 token莉测。你可以根據(jù)你的業(yè)務(wù)需求自己編寫更新 token 的邏輯
            var token = response.headers.authorization
            if (token) {
                // 如果 header 中存在 token,那么觸發(fā) refreshToken 方法唧喉,替換本地的 token
                this.$store.dispatch('refreshToken', token)
            }
            return response
        }, (error) => {
            switch (error.response.status) {

                // 如果響應(yīng)中的 http code 為 401捣卤,那么則此用戶可能 token 失效了之類的,我會(huì)觸發(fā) logout 方法八孝,清除本地的數(shù)據(jù)并將用戶重定向至登錄頁(yè)面
                case 401:
                    return this.$store.dispatch('logout')
                    break
                // 如果響應(yīng)中的 http code 為 400董朝,那么就彈出一條錯(cuò)誤提示給用戶
                case 400:
                    return this.$Message.error(error.response.data.error)
                    break
            }
            return Promise.reject(error)
        })
    }
})

Vuex 內(nèi)的代碼如下:

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
   state: {
       name: null,
       avatar: null,
       mobile: null,
       token: null,
       remark: null,
       auth: false,
   },
   mutations: {
       // 用戶登錄成功,存儲(chǔ) token 并設(shè)置 header 頭
       logined(state, token) {
           state.auth = true
           state.token = token
           localStorage.token = token
       },
       // 用戶刷新 token 成功唆阿,使用新的 token 替換掉本地的token
       refreshToken(state, token) {
           state.token = token
           localStorage.token = token
           axios.defaults.headers.common['Authorization'] = state.token
       },
       // 登錄成功后拉取用戶的信息存儲(chǔ)到本地
       profile(state, data) {
           state.name = data.name
           state.mobile = data.mobile
           state.avatar = data.avatar
           state.remark = data.remark
       },
       // 用戶登出益涧,清除本地?cái)?shù)據(jù)
       logout(state){
           state.name = null
           state.mobile = null
           state.avatar = null
           state.remark = null
           state.auth = false
           state.token = null

           localStorage.removeItem('token')
       }
   },
   actions: {
        // 登錄成功后保存用戶信息
       logined({dispatch,commit}, token) {
           return new Promise(function (resolve, reject) {
               commit('logined', token)
               axios.defaults.headers.common['Authorization'] = token
               dispatch('profile').then(() => {
                   resolve()
               }).catch(() => {
                   reject()
               })
           })
       },
       // 登錄成功后使用 token 拉取用戶的信息
       profile({commit}) {
           return new Promise(function (resolve, reject) {
               axios.get('profile', {}).then(respond => {
                   if (respond.status == 200) {
                       commit('profile', respond.data)
                       resolve()
                   } else {
                       reject()
                   }
               })
           })
       },
       // 用戶登出,清除本地?cái)?shù)據(jù)并重定向至登錄頁(yè)面
       logout({commit}) {
           return new Promise(function (resolve, reject) {
               commit('logout')
               axios.post('auth/logout', {}).then(respond => {
                   Vue.$router.push({name:'login'})
               })
           })
       },
       // 將刷新的 token 保存至本地
       refreshToken({commit},token) {
           return new Promise(function (resolve, reject) {
               commit('refreshToken', token)
           })
       },
   }
})

更新異常處理的 Handler

由于我們構(gòu)建的是 api 服務(wù)驯鳖,所以我們需要更新一下 app/Exceptions/Handler.php 中的 render

方法闲询,自定義處理一些異常。

Handler.php

<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

class Handler extends ExceptionHandler
{
   ...

   /**
    * Render an exception into an HTTP response.
    *
    * @param  \Illuminate\Http\Request $request
    * @param  \Exception $exception
    * @return \Illuminate\Http\Response
    */
   public function render($request, Exception $exception)
   {
       // 參數(shù)驗(yàn)證錯(cuò)誤的異常浅辙,我們需要返回 400 的 http code 和一句錯(cuò)誤信息
       if ($exception instanceof ValidationException) {
           return response(['error' => array_first(array_collapse($exception->errors()))], 400);
       }
       // 用戶認(rèn)證的異常扭弧,我們需要返回 401 的 http code 和錯(cuò)誤信息
       if ($exception instanceof UnauthorizedHttpException) {
           return response($exception->getMessage(), 401);
       }

       return parent::render($request, $exception);
   }
}

更新完此方法后,我們上面自定義的中間件里拋出的異常和我們下面參數(shù)驗(yàn)證錯(cuò)誤拋出的異常都會(huì)被轉(zhuǎn)為指定的格式拋出记舆。

使用

現(xiàn)在鸽捻,我們可以在我們的 routes/api.php 路由文件中新增幾條路由來(lái)測(cè)試一下了:

api.php

Route::prefix('auth')->group(function($router) {
    $router->post('login', 'AuthController@login');
    $router->post('logout', 'AuthController@logout');

});

Route::middleware('refresh.token')->group(function($router) {
    $router->get('profile','UserController@profile');
});

在你的 shell 中運(yùn)行如下命令以新增一個(gè)控制器:

$ php artisan make:controller AuthController

打開(kāi)此控制器,寫入如下內(nèi)容

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Transformers\UserTransformer;

class AuthController extends Controller
{

    /**
     * Get a JWT token via given credentials.
     *
     * @param  \Illuminate\Http\Request $request
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(Request $request)
    {
        // 驗(yàn)證規(guī)則泽腮,由于業(yè)務(wù)需求御蒲,這里我更改了一下登錄的用戶名,使用手機(jī)號(hào)碼登錄
        $rules = [
            'mobile'   => [
                'required',
                'exists:users',
            ],
            'password' => 'required|string|min:6|max:20',
         ];

        // 驗(yàn)證參數(shù)诊赊,如果驗(yàn)證失敗厚满,則會(huì)拋出 ValidationException 的異常
        $params = $this->validate($request, $rules);

       // 使用 Auth 登錄用戶,如果登錄成功碧磅,則返回 201 的 code 和 token碘箍,如果登錄失敗則返回
        return ($token = Auth::guard('api')->attempt($params))
            ? response(['token' => 'bearer ' . $token], 201)
            : response(['error' => '賬號(hào)或密碼錯(cuò)誤'], 400);
    }

    /**
     * 處理用戶登出邏輯
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        Auth::guard('api')->logout();

        return response(['message' => '退出成功']);
    }
}

然后我們進(jìn)入 tinker

$ php artisan tinker

執(zhí)行以下命令來(lái)創(chuàng)建一個(gè)測(cè)試用戶遵馆,我這里的用戶名是用的是手機(jī)號(hào)碼,你可以自行替換為郵箱丰榴。別忘了設(shè)置命名空間喲:

>>> namespace App\Models;
>>> User::create(['name' => 'Test','mobile' => 17623239881,'password' => bcrypt('123456')]);

正確執(zhí)行結(jié)果如下圖:

1.png

然后打開(kāi) Postman 來(lái)進(jìn)行 api 測(cè)試

2.png

正確的請(qǐng)求結(jié)果如下圖:

3.png

可以看到我們已經(jīng)成功的拿到了 token货邓,接下來(lái)我們就去驗(yàn)證一下刷新 token 吧

4.png

如圖可以看到我們已經(jīng)拿到了新的 token,接下來(lái)的事情便會(huì)交由我們前面設(shè)置的 axios 攔截器處理四濒,它會(huì)將本地的 token 替換為此 token换况。

版本科普

感覺(jué)蠻多人對(duì)版本沒(méi)什么概念,所以在這里科普下常見(jiàn)的版本峻黍。

  • α(Alpha)版

    ? 這個(gè)版本表示該 Package 僅僅是一個(gè)初步完成品复隆,通常只在開(kāi)發(fā)者內(nèi)部交流,也有很少一部分發(fā)布給專業(yè)測(cè)試人員姆涩。一般而言,該版本軟件的 Bug 較多惭每,普通用戶最好不要安裝骨饿。

  • β(Beta)版

    該版本相對(duì)于 α(Alpha)版已有了很大的改進(jìn),修復(fù)了嚴(yán)重的錯(cuò)誤台腥,但還是存在著一些缺陷宏赘,需要經(jīng)過(guò)大規(guī)模的發(fā)布測(cè)試來(lái)進(jìn)一步消除。通過(guò)一些專業(yè)愛(ài)好者的測(cè)試黎侈,將結(jié)果反饋給開(kāi)發(fā)者察署,開(kāi)發(fā)者們?cè)龠M(jìn)行有針對(duì)性的修改。該版本也不適合一般用戶安裝峻汉。

  • RC/ Preview版

    RC 即 Release Candidate 的縮寫贴汪,作為一個(gè)固定術(shù)語(yǔ),意味著最終版本準(zhǔn)備就緒休吠。一般來(lái)說(shuō) RC 版本已經(jīng)完成全部功能并清除大部分的 BUG扳埂。一般到了這個(gè)階段 Package 的作者只會(huì)修復(fù) Bug,不會(huì)對(duì)軟件做任何大的更改瘤礁。

  • 普通發(fā)行版本

    一般在經(jīng)歷了上面三個(gè)版本后阳懂,作者會(huì)推出此版本。此版本修復(fù)了絕大部分的 Bug柜思,并且會(huì)維護(hù)一定的時(shí)間岩调。(時(shí)間根據(jù)作者的意愿而決定,例如 Laravel 的一般發(fā)行版本會(huì)提供為期一年的維護(hù)支持赡盘。)

  • LTS(Long Term Support) 版

    該版本是一個(gè)特殊的版本号枕,和普通版本旨在支持比正常時(shí)間更長(zhǎng)的時(shí)間。(例如 Laravel 的 LTS 版本會(huì)提供為期三年的 維護(hù)支持亡脑。)

結(jié)語(yǔ)

jwt-auth 確實(shí)是一個(gè)很棒的用戶認(rèn)證 Package堕澄,配置簡(jiǎn)單邀跃,使用方便。


原文:https://segmentfault.com/a/1190000012606246

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛙紫,一起剝皮案震驚了整個(gè)濱河市拍屑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坑傅,老刑警劉巖僵驰,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異唁毒,居然都是意外死亡蒜茴,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門浆西,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)粉私,“玉大人,你說(shuō)我怎么就攤上這事近零∨岛耍” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵久信,是天一觀的道長(zhǎng)窖杀。 經(jīng)常有香客問(wèn)我,道長(zhǎng)裙士,這世上最難降的妖魔是什么入客? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮腿椎,結(jié)果婚禮上桌硫,老公的妹妹穿的比我還像新娘。我一直安慰自己酥诽,他們只是感情好鞍泉,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著肮帐,像睡著了一般咖驮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上训枢,一...
    開(kāi)封第一講書(shū)人閱讀 52,736評(píng)論 1 312
  • 那天托修,我揣著相機(jī)與錄音,去河邊找鬼恒界。 笑死睦刃,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的十酣。 我是一名探鬼主播涩拙,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼际长,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了兴泥?” 一聲冷哼從身側(cè)響起工育,我...
    開(kāi)封第一講書(shū)人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搓彻,沒(méi)想到半個(gè)月后如绸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旭贬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年怔接,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稀轨。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扼脐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出靶端,到底是詐尸還是另有隱情谎势,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布杨名,位于F島的核電站,受9級(jí)特大地震影響猖毫,放射性物質(zhì)發(fā)生泄漏台谍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一吁断、第九天 我趴在偏房一處隱蔽的房頂上張望趁蕊。 院中可真熱鬧,春花似錦仔役、人聲如沸掷伙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)任柜。三九已至,卻和暖如春沛厨,著一層夾襖步出監(jiān)牢的瞬間宙地,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工逆皮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宅粥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓电谣,卻偏偏與公主長(zhǎng)得像秽梅,于是被迫代替她去往敵國(guó)和親抹蚀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

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