基于tp5小程序登录的实现 demo版本 获取code 返回token 解密微信数据信息 和验证数据来源真实性(包含小程序前端和php后端代码 )
程序员文章站
2024-01-08 14:31:52
...
curl等公共函数方法在文章的最后
Wxbizdatacrypt.php
/**
* 对微信小程序用户加密数据的解密示例代码.
*
* @copyright Copyright (c) 1998-2014 Tencent Inc.
*/
namespace app\api\controller;
use app\api\controller\Base;
use think\Model;
use think\Db;
use think\Cache;
include_once "ErrorCode.php";
class Wxbizdatacrypt
{
private $appid;
private $sessionKey;
/**
* 构造函数
* @param $sessionKey string 用户在小程序登录后获取的会话**
* @param $appid string 小程序的appid
*/
public function __construct( $appid, $sessionKey)
{
$this->sessionKey = $sessionKey;
$this->appid = $appid;
}
/**
* 检验数据的真实性,并且获取解密后的明文.
* @param $encryptedData string 加密的用户数据
* @param $iv string 与用户数据一同返回的初始向量
* @param $data string 解密后的原文
*
* @return int 成功0,失败返回对应的错误码
*/
public function decryptData( $encryptedData, $iv, &$data )
{
if (strlen($this->sessionKey) != 24) {
return ErrorCode::$IllegalAesKey;
}
$aesKey=base64_decode($this->sessionKey);
if (strlen($iv) != 24) {
return ErrorCode::$IllegalIv;
}
$aesIV=base64_decode($iv);
$aesCipher=base64_decode($encryptedData);
$result=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
$dataObj=json_decode( $result );
if( $dataObj == NULL )
{
return ErrorCode::$IllegalBuffer;
}
if( $dataObj->watermark->appid != $this->appid )
{
return ErrorCode::$IllegalBuffer;
}
$data = $result;
return ErrorCode::$OK;
}
}
ErrorCode.php
<?php
namespace app\api\controller;
use app\api\controller\Base;
use think\Model;
use think\Db;
use think\Cache;
/**
* error code 说明.
* <ul>
* <li>-41001: encodingAesKey 非法</li>
* <li>-41003: aes 解密失败</li>
* <li>-41004: 解密后得到的buffer非法</li>
* <li>-41005: base64加密失败</li>
* <li>-41016: base64解密失败</li>
* </ul>
*/
class ErrorCode
{
public static $OK = 0;
public static $IllegalAesKey = -41001;
public static $IllegalIv = -41002;
public static $IllegalBuffer = -41003;
public static $DecodeBase64Error = -41004;
}
?>
本来以为php7.0 mcrypt禁用后 解码会有问题 后来发现官直接用官网给的demo 没发现有啥问题
下面这个图是获取token
下面是请求代码里小程序login返回的结果 第一个图为携带参数 这些参数在调用小程序 getuserinfo 里可以得到
第二个图为返回结果 里面结果是自定义的 为了返回给前端用
tp5的一段代码写的比较烂 返回值很随意 微信小程序登录代码如下 后端php代码如下
<?php
namespace app\api\controller;
use app\api\controller\Base;
use think\Model;
use think\Db;
use think\Cache;
//这个不需要继承
class Login
{
protected $code;
protected $wxLoginUrl;
protected $wxAppID;
protected $wxAppSecret;
protected $salt;
public function __construct()
{
$this->wxAppID = config('app_id');
$this->wxAppSecret = config('app_secret');
}
//生成token
public function getcode()
{
$code = input("post.code");
if(!$code)
{
$data['status'] = 0;
$data['token'] = '';
$data['code'] = 0;
echo json_encode($data);exit;
}
else{
$this->code = $code;
$this->wxLoginUrl = sprintf(config('login_url'),
$this->wxAppID,
$this->wxAppSecret,
$this->code
);
//echo $this->wxLoginUrl;exit;
$this->salt=config('salt');
}
$result = curl_get($this->wxLoginUrl);
$res = json_decode($result,true);
$data = array();
if(array_key_exists('errcode', $res))
{
$data['status'] = 0;
$data['token'] = '';
$data['code'] = 0;
echo json_encode($data);exit;
}
else
{
$data['status']=1;
//生成token
$token = md5( md5(time().$res['openid']).$this->salt);
Cache::set($token,1,3600);
$cache_data_key = $token."_info";
Cache::set($cache_data_key,$res,3600);
$data['token']= $token;
echo json_encode($data);exit;
}
}
//登录
public function login()
{
$log_path = date("Y/m/d",time());
$d_path = dirname(dirname(dirname(APP_PATH)))."/xxxx/log/{$log_path}";
$dir_path = str_replace('\\','/',$d_path);
if(!is_dir($dir_path))
{
mkdir($dir_path,0777,true);
}
$token = input("post.token");
$cache_data_key = $token."_info";
$cache_data = Cache::get($cache_data_key);
if(empty($cache_data))
{
$data['status'] = 0;
$data['data'] = '';
echo json_encode($data);exit;
}
$encryptedData = input("post.encryptedData");
$iv = input("post.iv");
$rawData = input("post.rawData");
$signature = input("post.signature");
//先判断信息是否正确
//signature String 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,参考文档 signature。
$check_signature = sha1($rawData.$cache_data['session_key']);
if($check_signature!=$signature)
{
$data['status']=0;
$data['data']='';
echo json_encode($data);exit;
}
$pc = new Wxbizdatacrypt( $this->wxAppID ,$cache_data['session_key'] );
$errCode = $pc->decryptData($encryptedData, $iv, $wx_data );
if ($errCode == 0) {
//解密成功
$datas=json_decode($wx_data,true);
if((array_key_exists('head_url', $cache_data)) && (array_key_exists('nickname', $cache_data)))
{
if(($cache_data['head_url']!='')&&($cache_data['nickname']!=''))
{
if(($datas['head_url']!=$cache_data['head_url'])||($datas['nickname']!=$cache_data['nickname']))
{
//修改数据库和缓存的头像和昵称
$user_save['head_url'] = $datas['head_url'];
$user_save['nickname'] = $this->filterNickname($datas['nickname']);
$user_where['openid'] = $cache_data['openid'];
$user_id=db('user')->where($user_where)->save($user_save);
if($user_id){
$cache_data['head_url'] = $datas['head_url'];
$cache_data['nickname'] = $this->filterNickname($datas['nickname']);
$cache_data_key = $token."_info";
Cache::set($cache_data_key,$cache_data,3600);
}else{
$data['status'] = 0;
$data['data'] = '';
echo json_encode($data);exit;
}
}
}else{
$data['status'] = 0;
$data['data'] = '';
echo json_encode($data);exit;
}
}else{
$cache_data['head_url'] = $datas['avatarUrl'];
$cache_data['nickname'] = $datas['nickName'];
$where_user['openid'] = $datas['openId'];
$user_arr = db('user')->where($where_user)->find();
if(empty($user_arr)){
$user_insert=array();
$user_insert['create_time']=time();
$user_insert['head_url'] = $datas['avatarUrl'];
$user_insert['nickname'] = $this->filterNickname($datas['nickName']);
$user_insert['openid'] = $datas['openId'];
$user_id=db('user')->insert($user_insert);
if($user_id){
$cache_data['head_url'] = $user_insert['head_url'];
$cache_data['nickname'] = $user_insert['nickname'];
$cache_data['uid'] = $user_arr['user_id'];
$cache_data_key = $token."_info";
Cache::set($cache_data_key,$cache_data,3600);
}
else
{
$data['status'] = 0;
$data['data'] = '';
echo json_encode($data);exit;
}
}else{
$cache_data['head_url'] = $user_arr['head_url'];
$cache_data['nickname'] = $user_arr['nickname'];
$cache_data['uid'] = $user_arr['user_id'];
$cache_data_key = $token."_info";
Cache::set($cache_data_key,$cache_data,3600);
}
}
} else {
//解密失败
$data['status'] = 0;
$data['data'] = '';
echo json_encode($data);exit;
}
//写日志 并且记录到日志表
@file_put_contents($dir_path."/test.log", date('Y-m-d H:i:s') . ' openid ' . $datas['openId'] . PHP_EOL, FILE_APPEND);
$insert_data=array();
$insert_data['time'] = time();
$insert_data['openid'] = $datas['openId'];
@db('logs')->insert($insert_data);
$return_data['status'] = 1;
$return_data['uid'] = $cache_data['uid'];
$return_data['head_url'] = $cache_data['head_url'];
$return_data['nickname'] = $cache_data['nickname'];
echo json_encode($return_data);exit;
}
//过滤nickname 标签
public function filterNickname($nickname) {
$nickname = preg_replace('/[\x{1F600}-\x{1F64F}]/u', '', $nickname);
$nickname = preg_replace('/[\x{1F300}-\x{1F5FF}]/u', '', $nickname);
$nickname = preg_replace('/[\x{1F680}-\x{1F6FF}]/u', '', $nickname);
$nickname = preg_replace('/[\x{2600}-\x{26FF}]/u', '', $nickname);
$nickname = preg_replace('/[\x{2700}-\x{27BF}]/u', '', $nickname);
$nickname = str_replace(array('"', '\''), '', $nickname);
return addslashes(trim($nickname));
}
}
Wxbizdatacrypt.php<?php
/**
* 对微信小程序用户加密数据的解密示例代码.
*
* @copyright Copyright (c) 1998-2014 Tencent Inc.
*/
namespace app\api\controller;
use app\api\controller\Base;
use think\Model;
use think\Db;
use think\Cache;
include_once "ErrorCode.php";
class Wxbizdatacrypt
{
private $appid;
private $sessionKey;
/**
* 构造函数
* @param $sessionKey string 用户在小程序登录后获取的会话**
* @param $appid string 小程序的appid
*/
public function __construct( $appid, $sessionKey)
{
$this->sessionKey = $sessionKey;
$this->appid = $appid;
}
/**
* 检验数据的真实性,并且获取解密后的明文.
* @param $encryptedData string 加密的用户数据
* @param $iv string 与用户数据一同返回的初始向量
* @param $data string 解密后的原文
*
* @return int 成功0,失败返回对应的错误码
*/
public function decryptData( $encryptedData, $iv, &$data )
{
if (strlen($this->sessionKey) != 24) {
return ErrorCode::$IllegalAesKey;
}
$aesKey=base64_decode($this->sessionKey);
if (strlen($iv) != 24) {
return ErrorCode::$IllegalIv;
}
$aesIV=base64_decode($iv);
$aesCipher=base64_decode($encryptedData);
$result=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
$dataObj=json_decode( $result );
if( $dataObj == NULL )
{
return ErrorCode::$IllegalBuffer;
}
if( $dataObj->watermark->appid != $this->appid )
{
return ErrorCode::$IllegalBuffer;
}
$data = $result;
return ErrorCode::$OK;
}
}
ErrorCode.php
<?php
namespace app\api\controller;
use app\api\controller\Base;
use think\Model;
use think\Db;
use think\Cache;
/**
* error code 说明.
* <ul>
* <li>-41001: encodingAesKey 非法</li>
* <li>-41003: aes 解密失败</li>
* <li>-41004: 解密后得到的buffer非法</li>
* <li>-41005: base64加密失败</li>
* <li>-41016: base64解密失败</li>
* </ul>
*/
class ErrorCode
{
public static $OK = 0;
public static $IllegalAesKey = -41001;
public static $IllegalIv = -41002;
public static $IllegalBuffer = -41003;
public static $DecodeBase64Error = -41004;
}
?>
结构目录如下
代码写的比较烂 只要有错误 全都返回状态为0 其实这里应该根据多种情况返回不同的错误代码 或者错误信息描述
这里就是简单实现的功能 没有完善 数据库查询也直接在c层写了 有洁癖和完美的 可以在model层自己重新写
=================================我是分割线==============================
小程序前端代码如下
index.js
//index.js
//获取应用实例
const app = getApp()
Page({
data: {
code:null,
token:null,
tk:null
},
//登录获取code
login:function(){
var that=this
wx.login({
success:function(res){
console.log(res.code);
that.setData({
code: res.code
}),
wx.request({
url: 'http://www.tpxcx.com/api/login/getcode',
method: "post",
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: {
code: res.code
},
success: function (res) {
if (res.data.status == 1) {
that.setData({
token: res.data.token
})
console.log("token=" + that.data.token);
} else {
console.log("请求token失败s");
}
}
})
}
})
},
tk:function()
{
var that = this
wx.getUserInfo({
success: function (res) {
console.log(res.userInfo);
console.log(res.encryptedData);
console.log(res.iv);
wx.request({
url: 'http://www.tpxcx.com/api/login/login',
method: "post",
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: {
token: that.data.token,
encryptedData: res.encryptedData,
signature: res.signature,
iv: res.iv,
rawData: res.rawData
},
success: function (res) {
if (res.data.status == 1) {
} else {
}
}
})
}
})
},
share_test: function () {
var that = this
wx.getUserInfo({
success: function (res) {
console.log(res.userInfo);
console.log(res.encryptedData);
console.log(res.iv);
wx.request({
url: 'http://www.tpxcx.com/api/banner/getbanner',
//url: 'http://www.tpxcx.com/api/catedetail/get_detail_one ',
method: "post",
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: {
token: that.data.token,
//uid:5,
id:23
},
success: function (res) {
if (res.data.status == 1) {
} else {
}
}
})
}
})
}
})
index.wxml
<button bindtap="login">登录</button>
<button bindtap="tk">token</button>
<button bindtap="share_test">分享测试返回头像和昵称</button>
程序里用到的一些公共方法curl啥的 代码如下
<?php
//二维数组拼接,拼接value 去重
function arr_unique($arr2d){
foreach ($arr2d as $k=>$v) {
$v=join(',',$v);
$temp[]=$v;
}
if($temp){
$temp=array_unique($temp);
foreach ($temp as $k=>$v) {
$temp[$k]=explode(',', $v);
}
return $temp;
}
}
/**
* @param string $url post请求地址
* @param array $params
* @return mixed
*/
function curl_post($url, array $params = array())
{
$data_string = json_encode($params);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt(
$ch, CURLOPT_HTTPHEADER,
array(
'Content-Type: application/json'
)
);
$data = curl_exec($ch);
curl_close($ch);
return ($data);
}
function curl_post_raw($url, $rawData)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $rawData);
curl_setopt(
$ch, CURLOPT_HTTPHEADER,
array(
'Content-Type: text'
)
);
$data = curl_exec($ch);
curl_close($ch);
return ($data);
}
/**
* @param string $url get请求地址
* @param int $httpCode 返回状态码
* @return mixed
*/
function curl_get($url, &$httpCode = 0)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//不做证书校验,部署在linux环境下请改为true
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$file_contents = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $file_contents;
}
function getRandChar($length)
{
$str = null;
$strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
$max = strlen($strPol) - 1;
for ($i = 0;
$i < $length;
$i++) {
$str .= $strPol[rand(0, $max)];
}
return $str;
}
上一篇: 第八节--访问方式