给phpwind加自定义oauth认证(第三方)

由于业务系统使用keycloak做统一的认证,但论坛直接使用的phpwind。
用两套用户体系确实有点不太合适,特别是国情决定的实名制等。
如果在APP上搞定了用户注册加实名的话,那论坛使用APP的用户进行登录,理论上也是实现实名了。

代码是基于phpwind v9.1.0,基础上的。
大概改不到10个文件,下面按照先后顺序依次说明。

1.开放平台接入设置

//ThirdOpenPlatformController.php 文件,可能会有两个

    /**
     * 显示在模板中的文本内容 
     * 
     * @access private
     * @return void
     */
    private function _displayText($type){
        $_lab1 = array(
            'AppId' =>'AppKey',
            'AppKey'=>'AppSecret',
        );
        $_lab2 = array(
            'AppId' =>'AppId',
            'AppKey'=>'AppKey',
        );
        $_labs = array(
            'taobao'=>$_lab1,
            'weibo' =>$_lab1,
            'weixin'=>$_lab2,
            'qq'    =>$_lab2,
            'hifipi'=> $_lab2, //追加
        );
        return $_labs[$type];
    }

2.Windid工具库

//WindidUtility.php 追加$htoken的处理,用于通过header['Authorization']传值,
    
    public static function buildRequest($url, $params = array(), $isreturn = true, $timeout = 10, $method = 'post', $htoken = '') {
        $request = Wind::getComponent('httptransfer', array($url, $timeout));
        $request->setWaitResponse($isreturn);
        if ($htoken != '') {
            $request->setHeader($htoken, 'Authorization');
        }
        if ($method == 'post') {
            if (!$params) $params = array('__data' => '1');//兼容部分版本post content不能为空的错误
            return $request->post($params);
        } else {
            return $request->get($params);
        }
    }

3.登录

//PwThirdLoginService.php 追加针对oauth2认证的逻辑

// 追加hifipi的相关keycloak定义
public static $supportedPlatforms = array(
    // See http://wiki.open.qq.com/wiki/website/%E4%BD%BF%E7%94%A8Authorization_Code%E8%8E%B7%E5%8F%96Access_Token
    'qq'    => array(
        'img'       => 'http://oss.aliyuncs.com/phpwind-image/4819ac3d87071089648af06c5fb7f204.png',
        'authorize' => 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&state=phpwind&scope=get_user_info',
        'accesstoken' => 'https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s',
        'openid' => 'https://graph.qq.com/oauth2.0/me?access_token=%s',
        'userinfo' => 'https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s',
        'text' => '使用QQ帐号登录',
    ),
    // See http://open.weibo.com/wiki/%E9%A6%96%E9%A1%B5
    'weibo' => array(
        'img' => 'http://oss.aliyuncs.com/phpwind-image/be7945e0d9521e74b8130a138b8694df.png',
        'authorize' => 'https://api.weibo.com/oauth2/authorize?client_id=%s&redirect_uri=%s&scope=email&state=phpwind&display=default',
        'accesstoken' => 'https://api.weibo.com/oauth2/access_token',
        'userinfo'    => 'https://api.weibo.com/2/users/show.json?access_token=%s&uid=%s',
        'text' => '使用新浪微博帐号登录',
    ),
    'hifipi'=>array(
        'img' => 'http://auth.hifipi.com/auth/resources/3.3.0.final/admin/keycloak/img/keyclok-logo.png',
        'authorize' => 'http://auth.hifipi.com/auth/realms/hifipi/protocol/openid-connect/auth?response_type=code&client_id=%s&redirect_uri=%s&state=phpwind&scope=get_user_info',
        'accesstoken' => 'http://auth.hifipi.com/auth/realms/hifipi/protocol/openid-connect/token',
        'userinfo' => 'http://auth.hifipi.com/auth/realms/hifipi/protocol/openid-connect/userinfo',
        'text' => '使用HIFIPI帐号登录',
    ),
);
// 追加hifipi的auth处理
public function getAuthorizeUrl($platform)
{
    $thirdPlatforms = Wekit::C('webThirdLogin');

    $config = Wekit::C()->getConfigByName('site', 'info.url');
    $redirecturl = $config['value'].'/index.php?m=u&c=login&a=thirdlogincallback&platform='.$platform;

    switch($platform) {
    case 'qq':
        return sprintf(self::$supportedPlatforms[$platform]['authorize'],
                       $thirdPlatforms[$platform.'.appid'],
                       urlencode($redirecturl)
                      );
    case 'weibo':
        return sprintf(self::$supportedPlatforms[$platform]['authorize'],
                       $thirdPlatforms[$platform.'.appid'],
                       urlencode($redirecturl)
                      );
    case 'hifipi':
        return sprintf(self::$supportedPlatforms[$platform]['authorize'],
                       $thirdPlatforms[$platform.'.appid'],
                       urlencode($redirecturl)
                      );
    default:
        // should never happen
        return '';
    }
}
//追加hifipi的token处理,取到access_token就完事了
public function getAccessToken($platform, $authcode)
{
    $thirdPlatforms = Wekit::C('webThirdLogin');
    $config = Wekit::C()->getConfigByName('site', 'info.url');

    $method = 'get';
    $redirecturl = $config['value'].'/index.php?m=u&c=login&a=thirdlogincallback&platform='.$platform;
    switch($platform) {
    case 'qq':
        $url = sprintf(self::$supportedPlatforms[$platform]['accesstoken'],
                       $thirdPlatforms[$platform.'.appid'],
                       $thirdPlatforms[$platform.'.appkey'],
                       $authcode,
                       urlencode($redirecturl)
                      );
        break;
    case 'weibo':
        $url = self::$supportedPlatforms[$platform]['accesstoken'];
        $postdata = array('client_id' => $thirdPlatforms[$platform.'.appid'],
                          'client_secret' => $thirdPlatforms[$platform.'.appkey'],
                          'code' => $authcode,
                          'redirect_uri' => $redirecturl,
                         );
        $method = 'post';
        break;
    case 'hifipi':
        $url = self::$supportedPlatforms[$platform]['accesstoken'];
        $postdata = array('client_id' => $thirdPlatforms[$platform.'.appid'],
                          'grant_type' => 'authorization_code',
                          'client_secret' => $thirdPlatforms[$platform.'.appkey'],
                          'code' => $authcode,
                          'redirect_uri' => $redirecturl,
                         );
        $method = 'post';
        break;
    default:
        // should never happen
        return array(false, '');
    }

    $data = $this->_request($url, ($method == 'post' ? $postdata : array()), $method);
    if (!$data) {
        return array(false, '');
    }
    switch($platform) {
    case 'qq':
        if (substr($data, 0, 8) == 'callback') {
            $result = json_decode(substr($data, 10, -4), true);
        } else {
            parse_str($data, $result);
        }
        if (isset($result['error'])) {
            return array(false, array($result['error'], $result['error_description']));
        } else {
            return array(true, $result['access_token'], 'extra' => array());
        }
    case 'weibo':
        $result = json_decode($data, true);
        if (isset($result['error_code']) && $result['error_code'] != 0) {
            return array(false, array($result['error_code'], $result['error']));
        }
        return array(true, $result['access_token'], 'extra' => array('uid' => $result['uid']));
    case 'hifipi':
        $result = json_decode($data, true);
        if (isset($result['error']) && $result['error'] != 0) {
            return array(false, array($result['error'], $result['error_description']));
        }
        return array(true, $result['access_token'], 'extra' => array('uid' => $result['uid']));
    default:
        return array(false, '');
    }
}
//取用户info,由于原代码有点长,这里省略了部分
public function getUserInfo($platform, $accesstoken, array $extra)
{
    switch($platform) {
    case 'qq':
        $url = sprintf(self::$supportedPlatforms[$platform]['openid'],
                       $accesstoken
                      );
        break;
    default:
        break;
    }
    if (isset($url)) {
        $openid = $this->getOpenId($url);
        if (!$openid[0]) {
            return $openid;
        }
        $openid = $openid[1];
    } else {
        $openid = $extra['uid'];
    }

    $htoken = ''; //追加
    $thirdPlatforms = Wekit::C('webThirdLogin');
    switch($platform) {
    case 'qq':
    /////此处省略部分代码
    case 'weibo':
    /////此处省略部分代码
    case 'hifipi':
        $url = self::$supportedPlatforms[$platform]['userinfo'];
        $htoken = 'bearer '.$accesstoken; //追加
        break;
    default:
        break;
    }
    $data     = $this->_request($url, array(), 'get', $htoken);
    $userinfo = array();
    switch($platform) {
    case 'qq':
    /////此处省略部分代码
    case 'weibo':
    /////此处省略部分代码
    case 'hifipi':
        $result = json_decode($data, true);
        if (isset($result['error']) && $result['error'] != 0) {
            $userinfo[0] = false;
            $userinfo[1] = array('code' => $result['error'],
                                 'msg'  => $result['error_description']
                                );
        } else {
            $userinfo[0] = true;
            $userinfo[1] = array(
                    'uid'      => $result['sub'],
                    'username' => substr($result['preferred_username'], 0, 15),
                    //'gender'   => $result['gender'] == 'm' ? 0 : 1,
                    //'avatar'   => $result['avatar_large'],
                    'type'     => $platform,
                    'email'    => $result['email'],
                    );
        }
        return $userinfo;
    default:
        return array(false, '');
    }
}
//追加了$htoken的参数
protected function _request($url, $params, $method = 'get', $htoken = '')
{
    $result = WindidUtility::buildRequest($url, $params, /* isreturn = */ true,
                                          self::HTTP_TIMEOUT, $method, $htoken);
    return !empty($result) ? $result : false;
}

4.管理后台的画面

.....省略.....
//thirdopenplatform_run.htm
<ul class="cc">
    <li {$typeClasses['qq']|html}><a href="{@url:config/ThirdOpenPlatform/run?type=qq}">QQ</a></li>
    <li {$typeClasses['weibo']|html}><a href="{@url:config/ThirdOpenPlatform/run?type=weibo}">新浪微博</a></li>
    <li {$typeClasses['hifipi']|html}><a href="{@url:config/ThirdOpenPlatform/run?type=hifipi}">Keycloak</a></li>
</ul>
.....省略.....

在后台及keycloak里配置好realm和client_id/client_secret,然后就可以使用keycloak的账号进行登录了。

具体效果,可以参考http://bbs.hifipi.com,刚建起来现在是测试(现在是连的测试用keycloak服务器),APP正在着手开发中。

相关推荐