Browse Source

验证xml接口请求、微信测试账号认证2

master
wanghongjun 1 year ago
parent
commit
b88a2ca632
  1. 6
      .env
  2. 113
      app/controller/wechat/Base.php
  3. 64
      app/controller/wechat/Login.php
  4. 31
      app/controller/wechat/Wechat.php
  5. 3
      app/middleware.php
  6. 10
      app/model/WechatUser.php
  7. 46
      app/service/BaseService.php
  8. 227
      app/service/user/LoginService.php
  9. 139
      app/service/wechat/WechatService.php
  10. 41
      app/validate/Login.php
  11. 10
      config/wx.php
  12. 14
      route/wechatRoute.php

6
.env

@ -15,3 +15,9 @@ DEBUG = false
[LANG]
default_lang = zh-cn
[WECHAT]
TOKEN = invoiceApi
APPID = wx95f03b3c0c33335f
APPSECRET = 117f446ea2bfdd7ef28e131faafd248b
REDIRECT_URI = https://intp.xingtongworld.com/admin/Login/wx

113
app/controller/wechat/Base.php

@ -0,0 +1,113 @@
<?php
declare (strict_types=1);
/**
* 工程基类
* @since 2017/02/28 创建
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\wechat;
use app\BaseController;
use app\service\user\LoginService;
use app\util\ReturnCode;
use fast\FuncException;
use think\App;
use think\facade\Env;
use think\facade\Request;
use think\facade\Session;
use think\Response;
class Base extends BaseController {
private $debug = [];
protected $noNeedLogin = [];
protected $user = [];
public $user_id = '';
// public function __construct()
// {
// $app = new App();
// parent::__construct($app);
//
// try {
// if(!$this->user){
// $bool = (new LoginService())->userAutologin();
// if($bool){
// $this->user = session('user');
// }
// }
// if ($this->user){
// $this->user_id = $this->user['id'];
// }
//// //需要登录接口进行校验
// if (!$this->match($this->noNeedLogin)){
// $this->checklogin();
// }
// } catch (\Exception $e) {
// return $this->buildFailed($e->getCode() ?: 400,$e->getMessage());
// }
// }
/**
* 关联检测是否包含该请求是否包含该方法
* @param $arr
* @return bool
*/
public function match($arr = []) {
$request = Request::instance();
$arr = is_array($arr) ? $arr : explode(',', $arr);
if (! $arr) {
return false;
}
$arr = array_map('strtolower', $arr);
// 是否存在
if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) {
return true;
}
// 没找到匹配
return false;
}
/**
* 用户登录检测
*/
public function checkLogin() {
$login = new LoginService();
if (!$login->isLogin()){
throw new FuncException('用户未登录',302);
}
}
public function buildSuccess(array $data = [], string $msg = '操作成功', int $code = ReturnCode::SUCCESS): Response {
$return = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
if (Env::get('APP_DEBUG') && $this->debug) {
$return['debug'] = $this->debug;
}
return json($return);
}
public function buildFailed(int $code, string $msg = '操作失败', array $data = []): Response {
$return = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
if (Env::get('APP_DEBUG') && $this->debug) {
$return['debug'] = $this->debug;
}
return json($return);
}
protected function debug($data): void {
if ($data) {
$this->debug[] = $data;
}
}
}

64
app/controller/wechat/Login.php

@ -0,0 +1,64 @@
<?php
namespace app\controller\wechat;
use app\service\user\LoginService;
use think\App;
use think\facade\Request;
class Login extends Base
{
/**
* @var
*/
public $auth;
protected $valid = \app\validate\Login::class;
public function __construct()
{
$app = new App();
parent::__construct($app);
}
/**
* @title 登录凭证校验
* @return \think\Response|void
*/
public function code2session(){
try {
$code = $this->request->param('code');
$iv = $this->request->param('iv');
$encryptedData = $this->request->param('encryptedData');
$loginService = new LoginService();
$user = $loginService->code2session($code, $iv, $encryptedData);
return $this->buildSuccess($user);
} catch (\Exception $e) { //错误消息 $e->getMessage()
return $this->buildFailed($e->getCode() ?: 400,$e->getMessage());
}
}
/**
* @title 登录
* @return \think\Response|void
*/
public function login(){
try {
validate($this->valid)->scene('login')->check(Request::post());
$phone = $this->request->post('phone');
$openid = $this->request->post('openid');
$unionid = $this->request->post('unionid');
$loginService = new LoginService();
$user = $loginService->userLogin($phone, $openid, $unionid);
return $this->buildSuccess($user);
} catch (\Exception $e) { //错误消息 $e->getMessage()
return $this->buildFailed($e->getCode() ?: 400,$e->getMessage());
}
}
public function userLogout()
{
}
}

31
app/controller/wechat/Wechat.php

@ -0,0 +1,31 @@
<?php
namespace app\controller\wechat;
use app\service\wechat\WechatService;
use think\Request;
class Wechat extends Base
{
public function index(Request $request)
{
$param = $request->param();
$signature = $param['signature'] ?? '';// 签名
$timestamp = $param['timestamp'] ?? '';// 时间戳
$nonce = $param['nonce'] ?? '';// 随机数
$echostr = $param['echostr'] ?? '0'; // 随机字符串
$checkRes = (new WechatService())->wechatChekToken($signature,$timestamp,$nonce);
$param['res_error'] = $checkRes;
if ($checkRes) {
echo $echostr;die;
}
return $this->buildFailed(400,'微信认证失败!');
}
}

3
app/middleware.php

@ -6,5 +6,6 @@ return [
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
// \think\middleware\SessionInit::class
\think\middleware\SessionInit::class,
\think\middleware\AllowCrossDomain::class,
];

10
app/model/WechatUser.php

@ -0,0 +1,10 @@
<?php
namespace app\model;
class WechatUser extends Base
{
}

46
app/service/BaseService.php

@ -0,0 +1,46 @@
<?php
declare (strict_types = 1);
namespace app\service;
use think\facade\Request;
class BaseService
{
/** @var mixed|null 后台用户id */
protected $admin_id = null;
/** @var mixed|null 后台用户信息 */
protected $admin = null;
/** @var mixed|null 用户信息 */
protected $user = null;
/** @var mixed|null 用户id */
protected $user_id = null;
protected $is_admin = false;
protected $is_user = false;
/** @var string 用户身份 admin, user */
protected $identity;
public function __construct() {
$this->user = session('user');
$baseUrl = Request::baseUrl();
//判断来自后台或用户
if (preg_match('/^\/admin\//', $baseUrl)) {
$this->identity = 'admin';
$this->is_admin = true;
$this->is_user = false;
} elseif (preg_match('/^\/wechat\//', $baseUrl)) {
$this->identity = 'user';
$this->is_admin = false;
$this->is_user = true;
}
if ($this->is_admin && $this->admin) {
$this->admin_id = $this->admin['id'];
} elseif ($this->is_user && $this->user) {
$this->user_id = $this->user['id'];
}
}
}

227
app/service/user/LoginService.php

@ -0,0 +1,227 @@
<?php
declare (strict_types = 1);
namespace app\service\user;
use app\model\WechatUser;
use app\service\BaseService;
use fast\Http;
use think\facade\Cookie;
use think\facade\Session;
class LoginService extends BaseService
{
/**
* 判断登录状态
* @return bool
*/
public function isLogin() {
if (!$this->user) {
return false;
}
return true;
}
/**
* 登录凭证校验
* @param $code
* @param $iv
* @param $encryptedData
* @return array
* @throws \fast\FuncException
*/
public function code2session($code, $iv, $encryptedData){
$http = new Http();
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=".env("app.appid")."&secret=".env("app.appsecret")."&js_code={$code}&grant_type=authorization_code";
$res = $http::get($url);
if($res['code'] != 200){
throw new \fast\FuncException($res['msg']);
}
$res['data'] = json_decode($res['data'], true);
if(isset($res['data']['errcode'])){
throw new \fast\FuncException($res['data']['errmsg']);
}
session('app_openid', $res['data']['openid']);
session('app_session_key', $res['data']['session_key']);
$res['userInfo'] = json_decode($this->decodeWechatIv($iv, $encryptedData), true);
$result = [];
$result['openid'] = $res['data']['openid'];
if (isset($res['data']['unionid'])) $result['unionid'] = $res['data']['unionid'];
$result['phone'] = $res['userInfo']['phoneNumber'];
return $result;
}
/**
* 解密微信的iv和encryptedData
* @param $iv
* @param $encryptedData
* @return false|string
* @throws \fast\FuncException
*/
public function decodeWechatIv($iv, $encryptedData){
$openid = session('app_openid');
$session_key = session('app_session_key');
if(!$openid || !$session_key){
throw new \fast\FuncException('缺少主要参数');
}
if (strlen($session_key) != 24) {
throw new \fast\FuncException('sessionkey长度错误');
}
if (strlen($iv) != 24) {
throw new \fast\FuncException('iv长度错误');
}
$aesKey=base64_decode($session_key);
$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 ) {
throw new \fast\FuncException('登录失败,请稍候再试');
}
if( $dataObj->watermark->appid != env("app.appid") ) {
throw new \fast\FuncException('小程序appid不一致,登录失败');
}
return $result;
}
/**
* 用户端登录
* @param $phone
* @param $openid
* @param $unionid
* @return WechatUser|mixed
* @throws \fast\FuncException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function userLogin($phone, $openid, $unionid) {
$field = 'id,openid,phone,nickname,sex,headimgurl';
$user = WechatUser::where('phone', $phone)->where('openid', $openid)->where('delete_time', 0)->field($field)->find();
if($user){
return $this->userSuccess($user);
}
return $this->register($phone, $openid, $unionid);
}
/**
* 用户登录成功
* @param WechatUser $user
* @return array
*/
public function userSuccess(WechatUser $user) {
session('user', $user->toArray());
$this->userKeeplogin($user->id,$user->openid,3600 * 24 * 7);
// $user->visible(['id', 'name', 'logo']);
return $user->toArray();
}
/**
* 保持登录
* @param $user_id
* @param $token
* @param $keeptime
* @return bool
*/
protected function userKeeplogin($user_id,$token,$keeptime = 0) {
if ($keeptime) {
$expiretime = time() + $keeptime;
$key = md5(md5(strval($user_id)) . md5(strval($keeptime)) . md5(strval($expiretime)) . $token);
error_reporting(E_ALL);
ini_set('display_errors','1');
$data = [$user_id, $keeptime, $expiretime, $key];
Cookie::set('userKeeplogin', implode('|', $data), 86400 * 30);
return true;
}
return false;
}
/**
* 用户端注册
* @param $phone
* @param $openid
* @param $unionid
* @return WechatUser|mixed
* @throws \fast\FuncException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function register($phone, $openid, $unionid) {
$add = [
'phone' => $phone,
'openid' => $openid,
'nickname' => '微信用户',
'unionid' => $unionid ?? '',
];
$id = (new WechatUser())->insertGetId($add);
if(!$id){
throw new \fast\FuncException('注册失败,请稍候再试');
}
$user = WechatUser::where('id', $id)->find();
return $this->userSuccess($user);
}
/**
* 自动登录
* @return WechatUser|array|false|mixed|\think\Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function userAutologin() {
$keeplogin = Cookie::get('userKeeplogin');
if (!$keeplogin) {
return false;
}
[$id, $keeptime, $expiretime, $key] = explode('|', $keeplogin);
if ($id && $keeptime && $expiretime && $key && $expiretime > time()) {
$user = WechatUser::where('id', $id)->find();
if (!$user || !$user->token) {
return false;
}
unset($user->password);
//token有变更
if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $user->token)) {
return false;
}
Session::set('user', $user->toArray());
//刷新自动登录的时效
$this->userKeeplogin($id,$user->token,$keeptime);
return $user;
} else {
return false;
}
}
/**
* 登出
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function userLogout() {
$user = WechatUser::find($this->user_id);
if ($user) {
$user->token = '';
$user->save();
}
Session::delete('user');
Cookie::delete('userKeeplogin');
return true;
}
}

139
app/service/wechat/WechatService.php

@ -0,0 +1,139 @@
<?php
declare (strict_types = 1);
namespace app\service\wechat;
use app\model\WechatUser;
use app\service\BaseService;
use fast\Http;
class WechatService extends BaseService
{
protected $token = "";
protected $appid = "";
protected $appsecret = "";
protected $redis = null;
public function __construct()
{
parent::__construct();
if($token = env('wechat.token')){
$this->token = $token;
}
$this->appid = env('wechat.appid');
$this->appsecret = env('wechat.appsecret');
$this->redis = new \Redis();
$this->redis->connect('127.0.0.1', 6379);
}
public function wechatChekToken($signature, $timestamp, $nonce){
$data = array($this->token, $timestamp, $nonce);
sort($data, SORT_STRING);
$str = sha1(implode( $data ));
if($str == $signature){
return true;
}
return false;
}
/**
* 获取accessToken
* @return mixed|\Redis|string
* @throws \fast\FuncException
*/
public function getAccessToken(){
$access_token = $this->redis->get('wechat_access_token');
if($access_token){
return $access_token;
}
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->appsecret}";
$http = new Http();
$res = $http::get($url);
if($res['code'] != 200){
throw new \fast\FuncException($res['msg']);
}
$data = json_decode($res['data'], true);
if(isset($data['errcode']) && $data['errcode'] != 0){
throw new \fast\FuncException($data['errmsg']);
}
$this->redis->set('wechat_access_token', $data['access_token'], 7200);
return $data['access_token'];
}
public function wechatEvent(){
$data = file_get_contents("php://input");
$obj = simplexml_load_string($data,"SimpleXMLElement", LIBXML_NOCDATA);
$this->log->info('消息', $obj->FromUserName);
$event = $obj->MsgType;
switch ($event){
case 'event':
$event = $obj->Event.'_'.$obj->MsgType;
$this->$event($obj);
break;
}
}
public function subscribe_event($obj){
$url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=".$this->getAccessToken()."&openid=".$obj->FromUserName."&lang=zh_CN";
$http = new Http();
$res = $http::get($url);
if($res['code'] != 200){
throw new \fast\FuncException($res['msg']);
}
$data = json_decode($res['data'], true);
if(isset($data['errcode']) && $data['errcode'] != 0){
throw new \fast\FuncException($data['errmsg']);
}
$user = WechatUser::where('openid', $data['openid'])->where('unionid', $data['unionid'])->where('is_deleted', 0)->find();
if($user){
$user->subscribe = 1;
$user->subscribe_time = date('Y-m-d H:i:s', $data['subscribe_time']);
$user->save();
return;
}
$user = [
'nickname' => $data['nickname'],
'headimgurl' => $data['headimgurl'],
'openid' => $data['openid'],
'unionid' => $data['unionid'],
'subscribe_time' => date('Y-m-d H:i:s', $data['subscribe_time']),
'subscribe' => 1,
];
(new WechatUser())->save($user);
}
public function unsubscribe_event($obj){
$url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=".$this->getAccessToken()."&openid=".$obj->FromUserName."&lang=zh_CN";
$http = new Http();
$res = $http::get($url);
if($res['code'] != 200){
throw new \fast\FuncException($res['msg']);
}
$data = json_decode($res['data'], true);
if(isset($data['errcode']) && $data['errcode'] != 0){
throw new \fast\FuncException($data['errmsg']);
}
$user = WechatUser::where('openid', $data['openid'])->where('unionid', $data['unionid'])->where('is_deleted', 0)->find();
if($user){
$user->subscribe = 0;
$user->save();
}
}
}

41
app/validate/Login.php

@ -0,0 +1,41 @@
<?php
namespace app\validate;
use think\Validate;
class Login extends Validate
{
/**
* 验证规则.
*/
protected $rule = [
'code|登录凭证' => 'require',
'phone|手机号' => 'require|mobile',
'openid|openid' => 'require',
'iv|iv' => 'require',
'encryptedData|encryptedData' => 'require',
#'unionid' => 'require',
];
/**
* 提示消息.
*/
protected $message = [
];
/**
* 字段描述.
*/
protected $field = [
];
/**
* 验证场景.
*/
protected $scene = [
'code2session' => ['code', 'iv', 'encryptedData'],
'login' => ['phone', 'openid', 'unionid']
];
}

10
config/wx.php

@ -1,10 +0,0 @@
<?php
return [
// wxOpenConfig
'APP_ID' => env('wx.app_id'),
'APP_SECRET' => env('wx.app_secret'),
//回调url
'REDIRECT_URI' => env('wx.redirect_uri'),
];

14
route/wechatRoute.php

@ -0,0 +1,14 @@
<?php
/**
* wechat路由
*/
use think\facade\Route;
Route::group('wechat', function() {
// 微信验证路由地址
Route::rule('Login/code2session', 'wechat.Login/code2session', 'post');
Route::rule('Login/login', 'wechat.Login/login', 'post');
Route::rule('Wechat/index', 'wechat.Wechat/index', 'get');
});
Loading…
Cancel
Save