OAuth - Twitter上的错误(无法进行身份验证)

时间:2011-07-19 18:11:26

标签: php security class twitter oauth

我写了一堂课(所以我可以了解OAuth是如何运作的)。它工作正常;我可以用类检索访问令牌。但是当我尝试发布更新时,它说我没有通过身份验证!我在这里做错了什么?

// By Kevin Jacobs
class OAuth {

    private $url = null;
    private $debug = false;
    private $method = 'POST';
    private $oauthData = array();
    private $data = array();
    private $token = array('key' => '', 'secret' => '');
    private $consumer = array('key' => '', 'secret' => '');

    /**
     * Encode a string in such a way you can use it for OAuth.
     * @param string $oauthData Data to encode
     * @return string $encData Encoded data
     */
    public static function encode($oauthData) {
        if (is_string($oauthData)) {
            return str_ireplace(
                array('+', '%7E'),
                array(' ', '~'),
                rawurlencode($oauthData)
            );
        } else {
            return '';
        }
    }

    /**
     * Generates a relative unique random string of a certain length.
     * @param int $length Length of the string
     * @return string $strRand A random string
     */
    public static function generateString($length = 40) {
        // Only strong cryptographic strings are allowed
        while (!isset($bStrong) || $bStrong === false) {
            $bytes = openssl_random_pseudo_bytes(floor($length / 2), $bStrong);
        }
        $strRand = bin2hex($bytes);
        return sha1($strRand);
    }

    /**
     * Generate a token pair (key and secret).
     * @return array $tokenPair
     */
    public static function generateTokenPair() {
        $tokenPair = array();
        $tokenPair['key'] = self::generateString();
        $tokenPair['secret'] = self::generateString();
        return $tokenPair;
    }

    /**
     * Set the callback URL.
     * @param string $callbackURL
     */
    public function setCallback($callback = null) {
        if ($callback === null) {
            $callback = 'http';
            if ($_SERVER['SERVER_PORT'] == 443) $callback .= 's';
            $callback .= '://' . $_SERVER['SERVER_NAME'];
            if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
                $callback .= ':' . $_SERVER['SERVER_PORT'];
            }
            $callback .= $_SERVER['REQUEST_URI'];
        }
        $this->oauthData['oauth_callback'] = $callback;
    }

    /**
     * Get the callback URL.
     * @return string $callbackURL
     */
    public function getCallback() {
        return $this->oauthData['oauth_callback'];
    }

    /**
     * Generate the nonce.
     * @return string $nonce
     */
    public function setNonce() {
        $this->oauthData['oauth_nonce'] = md5(self::generateString(20) . mktime());
        return $this->oauthData['oauth_nonce'];
    }

    /**
     * Set the timestamp.
     * @return int $timestamp
     */
    public function setTimestamp() {
        $this->oauthData['oauth_timestamp'] = mktime();
        return $this->oauthData['oauth_timestamp'];
    }

    /**
     * Set the OAuth version.
     * @param string Version
     */
    public function setVersion($version = '1.0') {
        $this->oauthData['oauth_version'] = $version;
    }

    /**
     * Set the HTTP method.
     * @param string Method
     */
    public function setMethod($method = 'POST') {
        $this->method = trim(strtoupper($method));
    }

    /**
     * Get the HTTP method.
     * @return string Method
     */
    public function getMethod() {
        return $this->method;
    }

    /**
     * Get the URL to call.
     * @return string URL
     */
    public function getURL() {
        return $this->url;
    }

    /**
     * Set the URL to call.
     * @param string URL
     */
    public function setURL($URL) {
        $this->url = $URL;
    }

    /**
     * Get the token key and secret
     * @return array $token Containing token key and secret
     */
    public function getToken() {
        return $this->token;
    }

    /**
     * Set the token
     * @param string $tokenKey Token key
     * @param string $tokenSecret Token secret
     */
    public function setToken($tokenKey, $tokenSecret = null) {
        $this->token['key'] = $tokenKey;
        $this->token['secret'] = $tokenSecret;
        $this->oauthData['oauth_token'] = $tokenKey;
        $this->oauthData['oauth_token_secret'] = $tokenSecret;
    }

    /**
     * Get the consumer
     * @return array $consumer Containing consumer key and secret
     */
    public function getConsumer() {
        return $this->consumer;
    }

    /**
     * Set the consumer
     * @param string $consumerKey Consumer key
     * @param string $consumerSecret Consumer secret
     */
    public function setConsumer($consumerKey, $consumerSecret) {
        $this->oauthData['oauth_consumer_key'] = $consumerKey;
        $this->consumer['key'] = $consumerKey;
        $this->consumer['secret'] = $consumerSecret;
    }

    /**
     * Generate the signature.
     * @return array Signature properties
     */
    public function setSignature() {
        // Set the signature method
        $this->oauthData['oauth_signature_method'] = 'HMAC-SHA1';
        // First, sort the OAuth data
        $oauthData = $this->oauthData;
        ksort($oauthData);
        // Now combine them in a string
        $query = http_build_query($oauthData);
        // Make it URL proof
        $query = rawurlencode($query);
        // Fetch the method and URL
        $method = $this->getMethod();
        $url = $this->getURL();
        // Make the URL URL proof
        $url = rawurlencode($url);
        // Now bind everything together
        $baseString = $method . '&' . $url . '&' . $query;
        // Retrieve the key
        $consumer = $this->getConsumer();
        $token = $this->getToken();
        $key = self::encode($consumer['secret']) . '&' . self::encode($token['secret']);
        // Encrypt the base string
        $signature = hash_hmac('SHA1', $baseString, $key, true);
        // And make it URL proof using base64_encode
        $signature = base64_encode($signature);
        $this->oauthData['oauth_signature'] = $signature;
    }

    public function setVerifier($verifier) {
        $this->oauthData['oauth_verifier'] = $verifier;
    }

    public function debugOn() {
        $this->debug = true;
    }

    public function debugOff() {
        $this->debug = false;
    }

    public function setData($data) {
        $this->data = $data;
    }

    public function call($url) {
        $method = $this->getMethod();
        $this->setURL($url);
        $this->setNonce();
        $this->setTimestamp();
        $this->setSignature();
        $oauthData = $this->oauthData;
        $data = $this->data;
        $data = array_merge($data, $oauthData);

        if ($method == 'GET') {
            $url = explode('#', $url);
            $url = reset($url);
            if (strpos($url, '?') !== false) {
                $binder = '&';
            } else {
                $binder = '?';
            }
            $url .= $binder . http_build_query($oauthData);
        }
        $ch = curl_init();
        if (!empty($headers)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        if ($method == 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $oauthData);
        }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_ENCODING, '');
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

}

$x = new OAuth();
$x->debugOn();
$x->setVersion('1.0');
$x->setConsumer('consumerToken', 'consumerSecret');
$x->setToken('accessToken', 'accessSecret');
$x->setMethod('POST');
$x->setData(array('status' => 'Hello World!'));
echo $x->call('http://api.twitter.com/1/statuses/update.json', true);

以下代码正在运行(检索访问令牌):

$x = new OAuth();
$x->debugOn();
$x->setVersion('1.0');
$x->setConsumer('consumerToken', 'consumerSecret');
$x->setToken('accessToken', 'accessSecret');
$x->setMethod('POST');
if (isset($_GET['oauth_verifier']) && isset($_GET['oauth_token'])) {
    // Request token -> Access token
    $verifier = $_GET['oauth_verifier'];
    $token = $_GET['oauth_token'];
    $x->setVerifier($verifier);
    $x->setToken($token);
    $x->setMethod('GET');
    $result = $x->call('https://api.twitter.com/oauth/access_token', true);
    parse_str($result);
    echo 'Access token: ' . $oauth_token . '<br />';
    echo 'Access token secret: ' . $oauth_token_secret;
} else {
    // Request token
    $x->setCallback();
    $x->setMethod('GET');
    $result = $x->call('https://api.twitter.com/oauth/request_token');
    parse_str($result);
    header('Location: http://api.twitter.com/oauth/authorize?oauth_token=' . $oauth_token);
}

1 个答案:

答案 0 :(得分:1)

我在这里猜测,但由于您只谈到“使用类检索访问令牌”,我怀疑您实际上并没有完成整个Twitter OAuth授权流程。您获得的初始令牌只是获取您可用于发布更新的真实令牌的起点;你必须jump through a bunch of hoops

如果我错了,你真的经历过那些箍,没关系。 :)