支付宝第三方授权登陆

在laravel框架中使用支付宝第三方授权登陆

image description

支付宝第三方授权登陆

说明:支付宝第三方授权登陆需要的一些必要参数,此处不做具体说明,有不明白可以自行百度或者查看官方文档官方参考地址:https://docs.open.alipay.com/common/105193

此处做一个简单获取说明:

  • 设置 授权回调路径 和 接口加签方式(选择RSA2(SHA256)密钥)
  1. 首先需要到阿里官方下载对应的SDK,我们用的是PHP,所以选择PHP版本下载,参考地址:https://docs.open.alipay.com/54/103419,解压后,复制下面4个文件

file

  1. 因为需要引入第三方SDK,为了方便,在app目录下新建Libs文件夹,再在Libs目录下新建AlipayAop文件夹,之后将之前复制的4个文件放在AlipayAop目录下;为了方便引用SDK中的类,我们在项目根目录的conposer.json文件中,找到图片对应的位置,添加上app/Libs/,使其能随着项目自动加载

file

此处可能会出现一个tmp问题,解决办法:在app/Libs/AlipayAop/lotusphp_runtime文件夹下新建一个新的文件夹tmp

如果是本地环境,要进行正常访问的话,可能需要修改支付宝SDK的入口文件AopSdk.php,具体修改如下图

file

如果没有出现问题的话,此处可以略过。

  1. 相关参数配置,为了方便,我们在config文件夹下新建一个新的配置文件auth_alipay.php,具体内容为:
return [
'alipay' => [
        'app_id' => 'your_app_id',
        'scope' => 'auth_userinfo',//接口权限值,目前只支持auth_userinfo(获取用户详细信息,需要用户授权)和auth_base(只能获取到用户在支付宝中的userID)两个值
        'redirect' => 'http://www.example.com/auth/socialite_ali/alipay/callback',//回调地址

        'privateKey' => '之前工具生成的应用私钥(很长的一段字符串)',//商户应用私钥

        'publicKey' => '之前工具生成的应用公钥',//商户应用公钥

    ],
];
  1. 参数配置完成后,下面开始正戏;此处我们还是使用laravel的服务提供者模式,此模式具体设置方法,此处不做说明,有不清楚的请查看laravel官方文档或者本人的上一篇文章 PHP中文转拼音类
  • 首先创建新的Services类,在app\Services目录下,新建一个新的类AuthAliPayService,具体类容如下:
namespace App\Services;
use Symfony\Component\HttpFoundation\RedirectResponse;
//引入的SDK
require (app_path() . '/Libs/AlipayAop/AopSdk.php');
use AlipayAop\aop\AopClient;
use AlipayAop\aop\request\AlipaySystemOauthTokenRequest;
use AlipayAop\aop\request\AlipayUserUserinfoShareRequest;

/**
* 支付宝第三方授权
*/
class AuthAliPayService
{
    function __construct()
    {
	$this->app_id = config('auth_alipay.alipay.app_id');
	$this->scope = config('auth_alipay.alipay.scope');
	$this->redirect = config('auth_alipay.alipay.redirect');
	$this->privateKey = config('auth_alipay.alipay.privateKey');
	$this->publicKey = config('auth_alipay.alipay.publicKey');
	$this->token_uri = 'https://openapi.alipay.com/gateway.do';
    }

/**
     * {@inheritdoc}.
     * 返回授权的完整路由
     * @see \Laravel\Socialite\Two\AbstractProvider::getAuthUrl()
     */
    protected function getAuthUrl($state)
    {
        return $this->buildAuthUrlFromBase('https://openauth.alipay.com/oauth2/publicAppAuthorize.htm', $state);
    }

    /**
     * {@inheritdoc}.
     * 设置正确的url参数
     */
    protected function buildAuthUrlFromBase($url, $state)
    {
        $query = http_build_query($this->getCodeFields($state));

        return $url.'?'.$query;
    }

    /**
     * {@inheritdoc}.
     * 授权路由需要的参数
     */
    protected function getCodeFields($state = null)
    {
        return [
            'app_id' => $this->app_id,
            'redirect_uri' => $this->redirect,
            'scope' => $this->scope,
            'state' => $state,
        ];
    }

    /**
     * 跳转到支付宝授权页面
     * @return [type] [description]
     */
    public function redirect($state){
	$url = $this->getAuthUrl($state);
	return new RedirectResponse($url);
    }

    /**
     * 获取用户支付宝信息
     * @param  [type] $auth_code [description]
     * @return [type]            [description]
     */
    public function user($auth_code){
	//初始化
	$aop = new \AopClient();
	$aop->gatewayUrl = $this->token_uri;
	$aop->appId = $this->app_id;
	$aop->rsaPrivateKey = $this->privateKey;
	$aop->alipayrsaPublicKey = $this->publicKey;
	$aop->apiVersion = '1.0';
	$aop->signType = 'RSA2';
	$aop->postCharset='UTF-8';
	$aop->format='json';

	//获取access_token
	$request = new \AlipaySystemOauthTokenRequest();
	$request->setGrantType("authorization_code");
	$request->setCode($auth_code);//这里传入 code
	$result = $aop->execute($request);
	$responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
	$access_token = $result->$responseNode->access_token;

	//获取用户信息
	$request_a = new \AlipayUserUserinfoShareRequest();
	$result_a = $aop->execute ($request_a,$access_token); //这里传入获取的access_token
	$responseNode_a = str_replace(".", "_", $request_a->getApiMethodName()) . "_response";

	$user_id = $result_a->$responseNode_a->alipay_user_id;   //用户唯一id
	$headimgurl = empty($result_a->$responseNode_a->avatar) ? NULL : $result_a->$responseNode_a->avatar;   //用户头像
	$nick_name = empty($result_a->$responseNode_a->nick_name) ? NULL : $result_a->$responseNode_a->nick_name;    //用户昵称
	$gender = $result_a->$responseNode_a->gender;//用户性别
	if($gender=='m'){
		$sex = '男';
	}else if($gender=='f'){
		$sex = '女';
	}else{
		$sex = NULL;
	}

	//文档:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.v7xVPE&treeId=53&articleId=104114&docType=1
	$user = [
		'user_id' => $user_id,
		'nick_name' => $nick_name,
		'avatar' => $headimgurl,
		'gender' => $sex,
	];
	return $user;
    }
}
  • 使用命令php artisan make:provider AuthAliPayServiceProvider,创建新的Provider类,修改其中的register方法;
use App\Services\AuthAliPayService;//新增
public function register()
{
    $this->app->bind('auth_alipay', function () {
        return new AuthAliPayService();
    });
}
  • 创建门面类,在app\Facades目录下新建新的文件AuthAliPayFacade,内容为:
namespace App\Facades;

use Illuminate\Support\Facades\Facade;
/**
 * @see \App\Services\AuthAliPayFacade
 */
class AuthAliPayFacade extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'auth_alipay';
    }
}
  • 最终在config\app.php文件中添加上对应的参数;
'providers' => [
    ...
    App\Providers\AuthAliPayServiceProvider::class,
];
'aliases' => [
    ...
    'AuthAlipay' => App\Facades\AuthAliPayFacade::class,//支付宝登陆授权
];

到此为止,必要代码布置完成,下面介绍使用方法

  1. 创建对应的路由
Route::get('auth/socialite_ali/alipay','Auth\AliPayLoginController@redirectToAli');//登陆跳转
Route::get('auth/socialite_ali/alipay/callback','Auth\AliPayLoginController@handleAliCallback');//回调路由
  1. 使用命令php artisan make:controller Auth\AliPayLoginController创建对应的controller;controller具体名称和位置,请自行设置;
namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use AuthAlipay;
use App\User;
use Auth;

use App\Http\Requests;
use App\Http\Controllers\Controller;

class AliPayLoginController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function redirectToAli(Request $request)
    {
        $time = time();
        $token = genToken(32).$time;//genToken()主要是用于创建一个唯一token;此参数主要是为了创建一个可以标识的唯一参数,用于验证阿里服务器返回的参数是否正确;你可以换成你自己的函数,PHP自带的随机生成函数都是可以的,如果有需要的,我会在文章末尾贴出该函数
        $request->session()->put($token,$token);
        //return AuthAlipay::redirect($token);
        return app('auth_alipay')->redirect($token);
    }

    public function handleAliCallback(Request $request)
    {
        //验证链接是否正确
        $status = $request->get('state');

        if($request->session()->has($status) && $request->session()->get($status)==$status){

            $request->session()->forget($status);//删除session

            $auth_code = $request->get('auth_code');

            //使用auth_code换取接口access_token及用户userId以及其他的一些信息
            $user = app('auth_alipay')->user($auth_code);

            $user_id = $user['user_id'];//支付宝授权用户唯一id

            $nick_name = empty($user['nick_name']) ? NULL : $user['nick_name'];

            $avatar = empty($user['avatar']) ? NULL : $user['avatar'];//头像暂时先不动

            $gender = $user['gender'];

            //判断是否是第一次登陆
            //先判断当前用户是否已经登录了
            if(!Auth::guest()){

                //已经登录了,执行绑定操作
                $u_id = Auth::user()->user_id;

                //先判断当前的第三方是否已经绑定了
                $is_binding = User::where('user_zhifubao',$user_id)->first();

                if(!$is_binding){

                    User::where('user_id',$u_id)->update(['user_zhifubao' => $user_id,]);

                }else{

                    return redirect('userorder/userinfo?order-style=personalInfo')->withErrors('此支付宝账号已经注册过或绑定过本平台账号,请换个支付宝账号进行绑定或使用该账号进行重新登陆!');

                }

                return redirect('userorder/userinfo?order-style=personalInfo');

            }else{

                $is_binding = User::where('user_zhifubao',$user_id)->first();

                if($is_binding){

                    $u_id = $is_binding->user_id;

                }else{

                    $time = time();

                    //否则以当前第三方信息,创建一个新的用户信息
                    $data = [
                        'user_email' => NULL,
                        'user_phone' => NULL,
                        'user_zhifubao' => $user_id,
                        'user_password' => NULL,
                        'user_type' => '1',
                        'user_level' =>'VIP0',
                        'user_gender' => $gender,
                        'user_points' => 1000,
                        'user_isfetch_register' => 1,
                        'user_createtime' => $time,
                        'user_fromdevice' => 3,
                        'user_nickname' => $nick_name
                    ];

                    $u_id = DB::table('trs_user')->insertGetId($data);
                }

                //通过用户ID直接进行认证
                Auth::loginUsingId($u_id);
                //登陆成功后,跳转到之前的页面
                return redirect()->intended('/');
            }
        }else{
            return redirect('auth/login')->withErrors('网络或认证信息出错,请重试!');
        }
    }
}
  1. 至此,在laravel上进行支付宝第三方授权登陆以全部完成,现在只需要在前端页面添加上对应的链接即可

<a href="{{url('auth/socialite_ali/alipay')}}" title="使用支付宝快速登录"><img src="{{asset('支付宝图标')}}" alt=""></a>

补上token函数

/**
 * 生成一个随机的token值
 * @param int $len
 * @param bool $md5
 * @return bool|string
 */
function genToken( $len = 32, $md5 = true ) {

    mt_srand( (double)microtime()*1000000 );

    $chars = array(
        'Q', '@', '8', 'y', '%', '^', '5', 'Z', '(', 'G', '_', 'O', '`',
        'S', '-', 'N', '<', 'D', '{', '}', '[', ']', 'h', ';', 'W', '.',
        '/', '|', ':', '1', 'E', 'L', '4', '&', '6', '7', '#', '9', 'a',
        'A', 'b', 'B', '~', 'C', 'd', '>', 'e', '2', 'f', 'P', 'g', ')',
        '?', 'H', 'i', 'X', 'U', 'J', 'k', 'r', 'l', '3', 't', 'M', 'n',
        '=', 'o', '+', 'p', 'F', 'q', '!', 'K', 'R', 's', 'c', 'm', 'T',
        'v', 'j', 'u', 'V', 'w', ',', 'x', 'I', '$', 'Y', 'z', '*'
    );

    $numChars = count($chars) - 1; $token = '';

    for ( $i=0; $i<$len; $i++ )
        $token .= $chars[ mt_rand(0, $numChars) ];

    if ( $md5 ) {

        $chunks = ceil( strlen($token) / 32 ); $md5token = '';

        for ( $i=1; $i<=$chunks; $i++ )

            $md5token .= md5( substr($token, $i * 32 - 32, 32) );

        $token = substr($md5token, 0, $len);
    } return $token;
}

End