Android三方登录

三方登录

https://github.com/sunxlfree/ThirdpartyLoginAndShare

0 很重要的大前提

测试时key须为发布到平台的包的key

三方登录原理是根据平台记录的APP_ID或key等内容调用平台接口获取用户数据,而apk的生成都必须有key,所以如果测试时打包用的key不是上传到平台的key,微博会c8998,微信直接无法回调,QQ让你下载最新版本

android {

defaultConfig {
    
    signingConfig signingConfigs.debug
}
buildTypes {
    debug {
        minifyEnabled false
        signingConfig signingConfigs.debug
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    release {
        minifyEnabled false
        signingConfig signingConfigs.debug
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
signingConfigs {
    debug {
        storeFile file('C:/Users/Administrator/Desktop/XXXX')//你的keystore路径
        storePassword "你的keystore密码"
        keyAlias "你的key的文件名"
        keyPassword "你的key的密码"
    }
}
//为了能让自己在项目中看到libs,建议加上这段
sourceSets {
    main() {
        jniLibs.srcDirs = ['libs']
    }
}
}

weibo

流程:根据平台注册的APP_KEY发起sso认证,获取到token,再用token和key获取到用户信息

  1. 微博开放平台获取到APP_KEY

  2. 下载weibosdk.jar(为了调用openapi接口),和weiboSDKCore_3.1.4.jar相关so文件

  3. 布局页面


    <activity
    android:name= "com.sina.weibo.sdk.component.WeiboSdkBrowser"
    android:configChanges= "keyboardHidden|orientation"
    android:exported= "false"
    android:windowSoftInputMode="adjustResize" >
    </activity >

     <service  
         android:name= "com.sina.weibo.sdk.net.DownloadService"  
         android:exported= "false" >  
     </service > 
    
  4. 代码

4.1 创建constant记录key Id
public class Constants {
public static final String WEIBO_APP_KEY = "微博的appKey";
public static final String WEIBO_REDIRECT_URL = "微博的授权回调页,须与官网一致,建议填写http://sns.whalecloud.com/sina2/callback";
public static final String WEIBO_SCOPE = "email,direct_messages_read,direct_messages_write,"
        + "friendships_groups_read,friendships_groups_write,statuses_to_me_read,"
        + "follow_app_official_microblog," + "invitation_write";

public static final String WX_APP_KEY = "微信的APPKEY";
public static final String QQ_APP_ID= "QQ的APPID";

}
4.2 string中添加
<string name="weibosdk_demo_token_to_string_format_1">Token:%1$s \n有效期:%2$s</string>
<string name="weibosdk_demo_token_has_existed">Token 仍在有效期内,无需再次登录。</string>
4.3 页面代码
/**
 * Created by Administrator on 2016/10/27.
 */

public class WeiboLoginActivity extends Activity implements OnClickListener {

private AuthInfo mWeiboAuthInfo;
private Button btnweibo;
private Button btnlogout;
private TextView tv;
private String nickname="";

/** 封装了 "access_token","expires_in","refresh_token",并提供了他们的管理功能 */
private Oauth2AccessToken mWeiboAccessToken;

/** 注意:SsoHandler 仅当 SDK 支持 SSO 时有效 */
private SsoHandler mWeiboSsoHandler;

/** 用户信息接口 */
private UsersAPI mWeiboUserAPI;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_weibologin);

    initweibologin();
    initViews();
    initEvents();
    initData();
}

private void initData() {
    // 从 SharedPreferences 中读取上次已保存好 AccessToken 等信息,
    // 第一次启动本应用,AccessToken 不可用
    mWeiboAccessToken = WeiboAccessTokenKeeper.readAccessToken(this);
    if (mWeiboAccessToken.isSessionValid()) {
        updateWeiboTokenView(true);
    }
}

private void initViews() {

    btnweibo = (Button) findViewById(R.id.btn_weibo_login);
    btnlogout = (Button) findViewById(R.id.btnlogout);
    tv = (TextView) findViewById(R.id.content);
    // 获取 Token View,并让提示 View 的内容可滚动(小屏幕可能显示不全)
    tv.setMovementMethod(new ScrollingMovementMethod());
}

private void initEvents() {
    btnweibo.setOnClickListener(this);
    btnlogout.setOnClickListener(this);

}

/**
 * 进行微博授权初始化操作
 */
private void initweibologin() {
    // 初始化授权类对象,将应用的信息保存
    mWeiboAuthInfo = new AuthInfo(this, ThirdpartyConstants.WEIBO_APP_KEY,
            ThirdpartyConstants.WEIBO_REDIRECT_URL, ThirdpartyConstants.WEIBO_SCOPE);
    mWeiboSsoHandler = new SsoHandler(WeiboLoginActivity.this, mWeiboAuthInfo);
}

/**
 * 当 SSO 授权 Activity 退出时,该函数被调用。
 *
 * @see {@link Activity#onActivityResult}
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    // SSO 授权回调
    // 重要:发起 SSO 登陆的 Activity 必须重写 onActivityResults
    if (mWeiboSsoHandler != null) {
        mWeiboSsoHandler.authorizeCallBack(requestCode, resultCode, data);
    }

}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btn_weibo_login:// SSO 授权, ALL IN ONE
            // 如果手机安装了微博客户端则使用客户端授权,没有则进行网页授权
            mWeiboSsoHandler.authorize(new AuthListener());
            break;

        case R.id.btnlogout:// 用户登出
            new LogoutAPI(WeiboLoginActivity.this, ThirdpartyConstants.WEIBO_APP_KEY,
                    WeiboAccessTokenKeeper.readAccessToken(WeiboLoginActivity.this)).logout(new WeiboLogOutRequestListener());
            break;
        default:
            break;
    }
}

/**
 * 微博认证授权回调类。 1. SSO 授权时,需要在 {@link #onActivityResult} 中调用
 * {@link SsoHandler#authorizeCallBack} 后, 该回调才会被执行。 2. 非 SSO
 * 授权时,当授权结束后,该回调就会被执行。 当授权成功后,请保存该 access_token、expires_in、uid 等信息到
 * SharedPreferences 中。
 */
class AuthListener implements WeiboAuthListener {

    @Override
    public void onCancel() {
        Toast.makeText(WeiboLoginActivity.this, "取消授权", Toast.LENGTH_LONG)
                .show();
    }

    @Override
    public void onComplete(Bundle values) {
        // 从 Bundle 中解析 Token
        mWeiboAccessToken = Oauth2AccessToken.parseAccessToken(values);
        if (mWeiboAccessToken.isSessionValid()) {
            nickname = "用户名:"
                    + String.valueOf(values
                    .get("com.sina.weibo.intent.extra.NICK_NAME"));
            // 显示 Token
            getWeiBoUserInfo();
            updateWeiboTokenView(false);

            // 保存 Token 到 SharedPreferences
            WeiboAccessTokenKeeper.writeAccessToken(WeiboLoginActivity.this,
                    mWeiboAccessToken);
            Toast.makeText(WeiboLoginActivity.this, "授权成功", Toast.LENGTH_SHORT)
                    .show();
            // Toast.makeText(
            // WeiboLoginActivity.this,
            // "头像地址:"
            // + String.valueOf(values
            // .get("com.sina.weibo.intent.extra.USER_ICON")),
            // Toast.LENGTH_LONG).show();

            Toast.makeText(WeiboLoginActivity.this, nickname, Toast.LENGTH_LONG)
                    .show();

        } else {
            // 以下几种情况,您会收到 Code:
            // 1. 当您未在平台上注册的应用程序的包名与签名时;
            // 2. 当您注册的应用程序包名与签名不正确时;
            // 3. 当您在平台上注册的包名和签名与您当前测试的应用的包名和签名不匹配时。
            String code = values.getString("code");
            String message = "授权失败";
            if (!TextUtils.isEmpty(code)) {
                message = message + "\nObtained the code: " + code;
            }
            Toast.makeText(WeiboLoginActivity.this, message, Toast.LENGTH_LONG)
                    .show();
        }
    }

    private void getWeiBoUserInfo() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                //获取用户信息的API
                mWeiboUserAPI = new UsersAPI(WeiboLoginActivity.this, ThirdpartyConstants.WEIBO_APP_KEY, mWeiboAccessToken);
                long uid = Long.parseLong(mWeiboAccessToken.getUid());
                mWeiboUserAPI.show(uid, mListener);

            }
        }).start();
    }

    @Override
    public void onWeiboException(WeiboException e) {
        Toast.makeText(WeiboLoginActivity.this,
                "Auth exception : " + e.getMessage(), Toast.LENGTH_LONG)
                .show();
    }

}

/**
 * 显示当前 Token 信息。
 *
 * @param hasExisted
 *            配置文件中是否已存在 token 信息并且合法
 */
private void updateWeiboTokenView(boolean hasExisted) {
    String date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
            .format(new java.util.Date(mWeiboAccessToken.getExpiresTime()));
    String format = getString(R.string.weibosdk_demo_token_to_string_format_1);
    tv.setText(String.format(format, mWeiboAccessToken.getToken(), date));

    String message = String.format(format, mWeiboAccessToken.getToken(), date);
    if (hasExisted) {
        message = getString(R.string.weibosdk_demo_token_has_existed)
                + "\n" + message;

    }
    message += "\n" + nickname;
    tv.setText(message);
}

/**
 * 微博 OpenAPI 回调接口。
 */
private RequestListener mListener = new RequestListener() {
    @Override
    public void onComplete(String response) {
        if (!TextUtils.isEmpty(response)) {
            LogUtil.i(TAG, response);
            // 调用 User#parse 将JSON串解析成User对象
            User user = User.parse(response);
            if (user != null) {
                Toast.makeText(WeiboLoginActivity.this,
                        "获取User信息成功,用户昵称:" + user.screen_name
                                + "用户头像:" + user.avatar_hd
                                + "用户性别" + user.gender,
                        Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(WeiboLoginActivity.this, response, Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    public void onWeiboException(WeiboException e) {
        LogUtil.e(TAG, e.getMessage());
        ErrorInfo info = ErrorInfo.parse(e.getMessage());
        Toast.makeText(WeiboLoginActivity.this, info.toString(), Toast.LENGTH_LONG).show();
    }
};

/**
 * 登出按钮的监听器,接收登出处理结果。(API 请求结果的监听器)
 * 看需求,微博自带,登录过的账号直接绕过确定,无法登录时传参设置
 */
private class WeiboLogOutRequestListener implements RequestListener {
    @Override
    public void onComplete(String response) {
        if (!TextUtils.isEmpty(response)) {
            try {
                JSONObject obj = new JSONObject(response);
                String value = obj.getString("result");

                if ("true".equalsIgnoreCase(value)) {
                    WeiboAccessTokenKeeper.clear(WeiboLoginActivity.this);
                    Log.i("weibologout","weibo登出成功");
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onWeiboException(WeiboException e) {
        Log.i("weibologout","weibo登出异常");
    }
}

}

微博bug

  • 文件不存在C8998
    • 考虑运行的keystore的md5是否与微博平台填写的签名一致


    • mAuthInfo = new AuthInfo()里设置的REDIRECT_URL是否与微博平台填写的授权回调页一致


wechat

客户端发起,打开WXEntryActivity中获得回调code,code拿到token和openID,再去请求用户信息接口,注意平台签名为keystore的md5

  1. 微信平台拿到AppID,导入libammsdk.jar
  • 配置xml

         <activity
              android:name=".wxapi.WXEntryActivity"
              android:configChanges="keyboardHidden|orientation|screenSize"
              android:exported="true"
              android:screenOrientation="portrait"
              android:theme="@android:style/Theme.Translucent.NoTitleBar" />
    
  • 在application设添加全局mIWxAPI

  • 发起Req请求

      public static IWXAPI mIWxAPI;
         MyApplication.mIWxAPI = WXAPIFactory.createWXAPI(LoginActivity.this, Constants.WX_APP_KEY, true);
          MyApplication.mIWxAPI.registerApp(Constants.WX_APP_KEY);
          SendAuth.Req req = new SendAuth.Req();
          req.scope = "snsapi_userinfo";
          req.state = "wechat_sdk_demo_test";
          MyApplication.mIWxAPI.sendReq(req);//执行完毕这句话之后,会在WXEntryActivity回调
    
  • WXEntry回调类,切记回调后加入关闭WXEntryActivity,否则会有一层透明的activity覆盖在发起页上

      public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
          private Bundle bundle;
          public IWXAPI mIWxAPI;
          //这个实体类是我自定义的实体类,用来保存第三方的数据的实体类
          private ThirdUserInfo info= null;
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              mIWxAPI = WXAPIFactory.createWXAPI(WXEntryActivity.this, ThirdpartyConstants.WX_APP_KEY, true);
              mIWxAPI.handleIntent(getIntent(), WXEntryActivity.this);  //必须调用此句话
          }
      
          @Override
          protected void onNewIntent(Intent intent) {
              super.onNewIntent(intent);
              mIWxAPI.handleIntent(intent, WXEntryActivity.this);//必须调用此句话
          }
      
          @Override
          public void onReq(BaseReq req) {
              System. out.println();
          }
      
      
      
          /**
           * Title: onResp
           *
           *           API:https://open.weixin.qq.com/ cgi- bin/showdocument ?action=dir_list&t=resource/res_list&verify=1&id=open1419317853 &lang=zh_CN
           * Description:在此处得到Code之后调用https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
           *  获取到token和openID。之后再调用https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID 获取用户个人信息
           *
           * @param arg0
           */
          @Override
          public void onResp(BaseResp arg0) {
              bundle=getIntent().getExtras();
              SendAuth.Resp resp = new SendAuth.Resp( bundle);
              //获取到code之后,需要调用接口获取到access_token
              if (resp. errCode == BaseResp.ErrCode. ERR_OK) {
                  String code = resp. code;
                      try {
                          getToken(code);
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
              } else{
                  WXEntryActivity. this.finish();
              }
      
          }
      
          //这个方法会取得accesstoken  和openID
          private void getToken(String code) throws IOException {
      //        AnimationUtil.showLoadingDialog(this, "微信正在获取用户信息", false);
              String access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token"
                      + "?appid=" + ThirdpartyConstants.WX_APP_KEY
                      + "&secret=" + ThirdpartyConstants.WX_APP_SECRET
                      + "&code=" + code
                      + "&grant_type=authorization_code";
              NetUtils.doGet(access_token_url, new NetUtils.HttpResponseCallBack() {
                  @Override
                  public void onSuccess(JSONObject response) {
                      if (response == null || response.length() == 0) {
                          Log.e("wechatGetToken", "null response");
                          return;
                      }
      
                      if(response.optString("access_token") == null || response.optString("access_token").length() == 0) {
                          Log.e("wechatGetToken", "errcode=" + response.optString("errcode") + " errmsg=" + response.optString("errmsg"));
                          return;
                      }
      
                      Map<String, String> data = new HashMap<String, String>();
                      String[] keys = {"access_token", "expires_in", "refresh_token", "openid", "scope"};
                      for(int i=0; i<keys.length; i++) {
                          data.put(keys[i], response.optString(keys[i]));
                      }
                      getUserInfo(data.get("access_token"),data.get("openid"));
                  }
      
                  @Override
                  public void onFailure() {
                      Log.e("wechatGetToken","error net");
                      WXEntryActivity.this.finish();
                  }
              });
          }
      
          //获取到token和openID之后,调用此接口得到身份信息
          private void getUserInfo(String token,String openId) {
              String getUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo"
                      + "?access_token=" + token
                      + "&openid=" + openId;
              NetUtils.doGet(getUserInfoUrl, new NetUtils.HttpResponseCallBack() {
                  @Override
                  public void onSuccess(JSONObject response) {
      //                AnimationUtil.cancelDialog();
                      if (response == null || response.length() == 0) {
                          Log.e("wechatGetToken", "null response");
                          return;
                      }
      
                      if(response.optString("openid") == null || response.optString("openid").length() == 0) {
                          Log.e("wechatGetToken", "errcode=" + response.optString("errcode") + " errmsg=" + response.optString("errmsg"));
                          return;
                      }
      
                      Map<String, String> data = new HashMap<String, String>();
                      String[] keys = {"openid", "nickname", "sex", "province", "city", "country", "headimgurl", "unionid"};
                      for(int i=0; i<keys.length; i++) {
                          data.put(keys[i], response.optString(keys[i]));
                      }
                      Log.i("wechatUserInfo",data.get("nickname"));
                      WXEntryActivity.this.finish();
                  }
      
                  @Override
                  public void onFailure() {
      //                AnimationUtil.cancelDialog();
                      Log.e("wechatGetToken","error net");
                      WXEntryActivity.this.finish();
                  }
              });
      
          }
      
      
      
       }
    

QQ登录

  1. app发布在腾讯开放平台后拿到APP_ID
  • manifest配置

    <activity
    android:name= "com.tencent.tauth.AuthActivity"
    android:launchMode= "singleTask"
    android:noHistory= "true" >
    <intent-filter >
    <action android:name ="android.intent.action.VIEW" />

              <category android:name ="android.intent.category.DEFAULT" />  
              <category android:name= "android.intent.category.BROWSABLE" />  
        
              <data android:scheme ="你的APP_ID" />  
          </intent-filter >  
      </activity >  
      <activity  
          android:name= "com.tencent.connect.common.AssistActivity"  
          android:configChanges="orientation|keyboardHidden|screenSize"  
          android:theme= "@android:style/Theme.Translucent.NoTitleBar" />  
    
  • 代码

      /**
       * Created by Administrator on 2016/11/17.
       */
      
      
      
      /**
       * Created by Administrator on 2016/11/4.
       */
      
      public class QQLoginActivity extends Activity{
          //初始化Tencent
          public Tencent mTencent = null;
          private ThirdUserInfo thirdUser = null;
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_qqlogin);
              mTencent = Tencent.createInstance(ThirdpartyConstants.QQ_APP_ID, this);
          }
          public void qqLogin(View view) {
              mTencent.login(QQLoginActivity.this, "all", mIUiListener);
          }
      
          public void qqLogout(View view) {
          }
      
      
          /**
           * QQ的三方授权回调监听
           */
          IUiListener mIUiListener = new IUiListener() {
      
              @Override
              public void onCancel() {
                  Log.i("qqauth","==cancel");
              }
      
              @Override
              public void onComplete(Object arg0) {
                  //登陆成功的回调,在此处可以获取用户信息
      //            AnimationUtil.showLoadingDialog(QQLoginActivity.this, "QQ登陆正在获取用户信息", false);
                  initOpenidAndToken((JSONObject) arg0);
                  updateUserInfo();
              }
      
              @Override
              public void onError(UiError arg0) {
                  Log.i("qqauth","==error");
              }
      
          };
      
          /**
           * QQ初始化OPENID以及TOKEN身份验证。
           */
          private void initOpenidAndToken(JSONObject jsonObject) {
              thirdUser = new ThirdUserInfo();
              try {
                  //这里的Constants类,是 com.tencent.connect.common.Constants类,下面的几个参数也是固定的
                  String token = jsonObject.getString(com.tencent.connect.common.Constants.PARAM_ACCESS_TOKEN);
                  String expires = jsonObject.getString(com.tencent.connect.common.Constants.PARAM_EXPIRES_IN);
                  //OPENID,作为唯一身份标识
                  String openId = jsonObject.getString(com.tencent.connect.common.Constants.PARAM_OPEN_ID);
                  if (!TextUtils.isEmpty(token) && !TextUtils.isEmpty(expires) && !TextUtils.isEmpty(openId)) {
                      //设置身份的token
                      mTencent.setAccessToken(token, expires);
                      mTencent.setOpenId(openId);
                      thirdUser.setThirdID(openId);
                  }
              } catch (Exception e) {
              }
          }
      
          /**
           * QQ在回调里面可以获取用户信息数据了
           */
          private void updateUserInfo() {
              if (mTencent != null && mTencent.isSessionValid()) {
      
                  IUiListener listener = new IUiListener() {
      
                      @Override
                      public void onError(UiError e) {
      //                    AnimationUtil.cancelDialog();
                      }
      
                      // 用户的信息回调在此处
                      @Override
                      public void onComplete(final Object response) {
                          // 返回Bitmap对象。
                          try {
                              JSONObject obj = new JSONObject(response.toString());
                              thirdUser.setNickName(obj.optString("nickname"));
                              thirdUser.setHeadimgurl(obj.optString("figureurl_qq_2"));
                              thirdUser.setGender("男".equals(obj.optString("gender")) ? "1" : "0");
                              Log.i("qqNickname", thirdUser.getNickName());
                              Log.i("qqHeadImg", thirdUser.getHeadimgurl());
                              Log.i("qqGender", thirdUser.getGender());
      
                          } catch (JSONException e) {
                              e.printStackTrace();
                          }
                      }
      
                      @Override
                      public void onCancel() {
      //                    AnimationUtil.cancelDialog();
                      }
                  };
                  UserInfo mInfo = new com.tencent.connect.UserInfo(QQLoginActivity.this, mTencent.getQQToken());
                  mInfo.getUserInfo(listener);
              }
          }
      
          /**
           * QQ的授权回调
           */
          @Override
          protected void onActivityResult(int requestCode, int resultCode, Intent data) {
              Log.d("TAG", "-->onActivityResult " + requestCode + " resultCode=" + resultCode);
              if (requestCode == com.tencent.connect.common.Constants.REQUEST_LOGIN ||
                      requestCode == com.tencent.connect.common.Constants.REQUEST_APPBAR) {
                  Tencent.onActivityResultData(requestCode, resultCode, data, mIUiListener);
              }
      
          }
      
      
      }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,475评论 4 372
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,744评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,101评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,732评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,141评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,049评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,188评论 2 320
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,965评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,716评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,867评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,341评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,663评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,376评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,200评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,990评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,179评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,979评论 2 279

推荐阅读更多精彩内容