微信授權登錄(PHP)
主要是在項目中遇到網頁授權登錄這個需求,就對此做些總結記錄。
OAuth2.0授權
OAuth是一個開放協議,允許用戶讓第三方應用以安全且標准的方式獲取該用戶在某一網站、移動或桌面應用上存儲的個人信息,而無需將用戶名和密碼提供給第三方應用。常見微信、QQ登錄,省去管理賬戶的麻煩,也不會造成用戶的流失。
打開微信的官方文檔,會看到網頁授權一些說明。(有點摸不着頭腦,主要是當時沒能關聯起來,微信文檔每次看都那么郁悶!!!)
來了解下授權的模式,官方說的詳細(這得仔細閱讀下):
關於網頁授權的兩種scope的區別說明
1、以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的用戶的openid的,並且是靜默授權並自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業務頁面)
2、以snsapi_userinfo為scope發起的網頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,並且由於用戶同意過,所以無須關注,就可在授權后獲取該用戶的基本信息。
3、用戶管理類接口中的“獲取用戶基本信息接口”,是在用戶和公眾號產生消息交互或關注后事件推送后,才能根據用戶OpenID來獲取用戶基本信息。這個接口,包括其他微信接口,都是需要該用戶(即openid)關注了公眾號后,才能調用成功的。
實現思路
- 在微信公眾號中設定網頁授權域名;
- 用戶打開登錄頁,進入網頁授權頁面url;
- 微信詢問用戶是否同意授權給微信公眾號;(snsapi_base 靜默授權無此步驟,並且此模式snsapi_userinfo需關注微信公眾號)
- 用戶同意授權(snsapi_base靜默授權直接默認同意)
- 回調code,帶code參數請求微信,換取網頁授權access_token(此步獲取openid,微信用戶的唯一標識)
- 通過【獲取用戶基本信息】接口,參數access_token(和上面的不同,普通的access_token)和openid,判斷是否綁定微信公眾號,未綁定,跳轉綁定頁。(此步驟snsapi_base無需,當然這個步驟可以去掉,視情況而定,像我項目就有這要求,唉)
- 將openid帶入登錄頁,商戶服務器檢測當前openid是否存在數據庫用戶記錄中
- 不存在openid記錄,登錄用戶,商戶服務器將openid存入用戶表中;(存在openid記錄跳過此步驟)
- openid記錄存在,則拉取用戶信息,存入會話,完成登錄;
以上是本人實際項目中的做法,除此之外我們的接口做了簽名驗證,有一定安全性。僅供參考,總覺得有更好的思路,希望大家指出。
話不多說,貼出實現邏輯代碼:
商戶端登錄
public function actionLogin() {
session_start();
$_SESSION['openid'] = empty( $_SESSION['openid'] ) ? $_GET['openid'] : $_SESSION['openid'];
if(!$_GET['openid']){
$this->apiget('WeixinServer/ScopeRedirectUriAppid',array('sign'=>$this->sign,'sourcetype'=>7,'data' => ''));
header("Location:".Yii::app()->params['apipath'].'/WeixinServer/ScopeRedirectUriAppid?sign='.$this->sign.'&sourcetype=7' );
exit;
}
if($_SESSION['openid']){
//判斷綁定公眾號
$jssdk = new JSSDK();
$access_token = $jssdk->getAccessToken();
$url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$access_token}&openid={$_SESSION['openid']}&lang=zh_CN";
$UserInfo = json_decode($this->dogetCurl($url),1);
if( empty($UserInfo['subscribe']) ){
//跳轉關注頁
header("Location:https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzA3Mzg2MjMyNw==&scene=124#wechat_redirect" );
die;
}
//微信登錄
$WeixinLogin = $this->apiget('WeixinServer/WeixinLogin', array('sign'=>$this->sign,'sourcetype'=>7,'openid'=>$_SESSION['openid']));
if( $WeixinLogin['error']==1){
return $this->render($this->act);
}
$this->WeinxinLogin($WeixinLogin);
die;
}
$this->render($this->act);
}
##微信授權成功 寫入session,跳轉首頁
public function WeinxinLogin( $data=array() ){
$_SESSION['user'] = $data['data'];
$this->redirect('/index/index');
}
##curl請求
public function apiget($path, $param=array()){
$url = Yii::app()->params['apipath'].$path;
$curl = curl_init();
curl_setopt($curl, CURLOPT_USERAGENT, '3'); //User-Agent
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$return = curl_exec($curl);
curl_close($curl);
$j = json_decode($return, 1);
return $return;
}
WeixinServer 處理微信授權
<?php
class WeixinServerController extends Controller {
public function __construct() {
//todo 驗證簽名
}
public function error($msg){
header("Content-type: application/json");
$o['time'] = "".time();
$o['error'] = '1';
$o['errorMsg'] = $msg;
echo json_encode($o);die;
}
public function Login($user=array()){
//todo 獲取用戶信息
}
##=====================================微信授權========================================##
##微信授權登錄
public function actionWeixinLogin(){
if( empty($_POST['openid']) ) $this->error('openid為空');
//todo 通過openid查詢用戶
$user = DB::get_one("SELECT * FROM {{user}} WHERE openid='{$_POST['openid']}' ");
if( !$user )$this->error('openid不存在');
$userinfo = $this->Login($user['uid']);
$this->out($userinfo);
}
##微信授權 獲取code
public function actionScopeRedirectUriAppid( ){
$APPID = "XXXXXXXXXXXX";
$REDIRECT_URI= 'http://'.$_SERVER['HTTP_HOST'].'/WeixinServer/WeixinOpenidCallback';
$scope='snsapi_base'; //手動授權snsapi_userinfo 靜默授權snsapi_base
$url='https://open.weixin.qq.com/connect/oauth2/authorize?appid='.$APPID.'&redirect_uri='.urlencode($REDIRECT_URI).'&response_type=code&scope='.$scope.'&state=scene#wechat_redirect';
header("Location:{$url}" );
}
##微信回調,獲取openid
public function actionWeixinOpenidCallback(){
if( $code= Yii::app()->request->getParam('code') ){
$APPID = "XXXXXXXXXXXX";
$SECRET = "XXXXXXXXXXXX";
$url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$APPID}&secret={$SECRET}&code={$code}&grant_type=authorization_code";
$UserOpenidArr = json_decode($this->dogetCurl($url),1);
if( empty($UserOpenidArr ['openid']) ) {
print_r( $UserOpenidArr );
die();
}
header("Location:http://XXXXXXXX/index/login?openid={$UserOpenidArr ['openid']}" );
}else{
die("微信授權失敗");
}
}
public function dogetCurl( $url ='' ) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt ( $ch, CURLOPT_HTTPHEADER, array( 'Connection: Keep-Alive', 'Keep-Alive: 300' ));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($ch);
curl_close ( $ch );
return $data;
}
}
代碼結合實現思路,大致思想清楚就好實現了,希望對大家有幫助(代碼寫的爛,將就看吧)。
