鲲圭发现 将Linkedin登录集成到你的独立站

将Linkedin登录集成到你的独立站

1. 创建应用

登录到Linkedin开发者账号中心:https://www.linkedin.com/developers/login,选择My Apps -> Create app,

创建好应用后,在应用详情页“Auth"部分,可查看Client ID和Client Secret,稍后我们需要用到。

接着我们还需要配置“Redirect URLs”部分,这个地址便是用户授权通过后返回到你的网站处理授权码的接收点,需要注意URL地址有以下规则和限制:

  • URL地址必须包含完整路径,比如 https://example.com/auth/callback, 不能是这种相对路径 /auth/callback
  • URL中的参数将被忽略, 比如 https://example.com/?id=1 ,Linkedin在回调该网址时不会携带id=1参数;
  • URL不允许包含 “#”,比如 https://example.com/auth/callback#linkedin

2. 发起授权请求

我们创建一个LinkedIn类,添加一个getAuthorizeURL()方法,用于生成授权请求链接:

<?php

class LinkedIn {
    /** @var 申请的应用Client ID */
    private $key;

    /** @var 申请的应用Client Secret */
    private $secret;

    /** @var 授权回调地址,需在你的应用设置Redirect URLs中有匹配记录 */
    private $redirect;

    /** @var 授权范围,需与你的应用设置Permissions 部分一致*/
    private $scope = 'r_emailaddress r_liteprofile w_member_social';

    public function __construct($key, $secret, $redirect) {
        $this->key = $key;
        $this->secret = $secret;
        $this->redirect = $redirect;
    }
    
    public function getAuthorizeURL() {
        $params = [
            'response_type' => 'code',
            'client_id' => $this->key,
            'redirect_uri' => $this->redirect,
            'state' => uniqid('', true),
            'scope' => $this->scope
        ];
        session_start();
        $_SESSION['linkedin_state'] = $params['state'];
        
        return 'https://www.linkedin.com/oauth/v2/authorization?'.http_build_query($params);
    }
}

用户点击生成的链接后,将会跳转到Linkedin授权页面:

3. 换取 access_token

access_token是经用户授权后可获取用户基本信息的钥匙,我们在上一步授权完成后会拿到授权码,通过授权码可以换取access_token。在LinkedIn类中增加getAccessToken()方法:

public function getAccessToken() {
    session_start();
    if(!(isset($_REQUEST['code']) && isset($_REQUEST['state']) && $_SESSION['linkedin_state'] == $_REQUEST['state'])) {
        throw new Exception('Invalid access');
    }
    $params = [
        'grant_type' => 'authorization_code',
        'code' => $_REQUEST['code'],
        'redirect_uri' => $this->redirect,
        'client_id' => $this->key,
        'client_secret' => $this->secret,
    ];
    $url = 'https://www.linkedin.com/oauth/v2/accessToken';
    $response = file_get_contents($url, false, stream_context_create([
        'http' => [
            'method' => 'POST',
            'header'  => 'Content-type: application/x-www-form-urlencoded',
            'content' => http_build_query($params)
        ]
    ]));
    if(!$response) throw new Exception('HTTP request failed.');
    $token = json_decode($response);
    return $token->access_token;
}

4. 获取用户基本信息

用户基本信息的获取貌似在微软收购LinkedIn后发生了些许变化,以前的获取方式变得不可用了:

public function getUserInfo($token) {
    $params = [
        'oauth2_access_token' => $token,
        'format' => 'json'
    ];
    $url = 'https://api.linkedin.com/v2/people/~'.'?'.http_build_query($params);
    
    $response = file_get_contents($url, false, stream_context_create([
        'http' => [
            'method' => 'GET'
        ]
    ]));
    $data = json_decode($response);
    if(!$data) throw new Exception($data);
    
    return $data;
}

翻阅最新文档发现,Linkedin的文档地址都迁移到到微软服务器,真是变成一家人了啊?,重新调整后的getUserInfo()方法:

public function getUserInfo($token) {
    $url = 'https://api.linkedin.com/v2/me';
    $response = file_get_contents($url, false, stream_context_create([
        'http' => [
            'method' => 'GET',
            'header' => 'Authorization: Bearer '.$token
        ]
    ]));
    $data = json_decode($response);
    if(!$data) throw new Exception($data);
    
    return $data;
}

返回的用户信息数据结构如下:

{
   "firstName":{
      "localized":{
         "en_US":"Bob"
      },
      "preferredLocale":{
         "country":"US",
         "language":"en"
      }
   },
   "localizedFirstName": "Bob",
   "headline":{
      "localized":{
         "en_US":"API Enthusiast at LinkedIn"
      },
      "preferredLocale":{
         "country":"US",
         "language":"en"
      }
   },
   "localizedHeadline": "API Enthusiast at LinkedIn",
   "vanityName": "bsmith",
   "id":"yrZCpj2Z12",
   "lastName":{
      "localized":{
         "en_US":"Smith"
      },
      "preferredLocale":{
         "country":"US",
         "language":"en"
      }
   },
   "localizedLastName": "Smith",
   "profilePicture": {
        "displayImage": "urn:li:digitalmediaAsset:C4D00AAAAbBCDEFGhiJ"
   }
}

我们通过其中的属性 id 来识别该用户的唯一性,通过 vantyName 来获得用户昵称,profilePicture.displayImage 则是用户头像。基本上我们获取了这些信息对本文第三方登录集来说就足够了。