Google OAuth 2.0 Server to Server:错误的请求

Fud*_*own 6 plsql google-analytics-api google-oauth service-accounts oracle12c

现在我已经在墙上敲了3天,试图让它发挥作用.

POST /oauth2/v3/token HTTP/1.1
Host: www.googleapis.com
Content-length: 495
Content-type: application/x-www-form-urlencoded
Authorization: Bearer ya29.cgEcY6meBrvaH6oe0nD_PtsFyMVqskiUYi7iJxapKHeEgPoIw8gMt0BJdIvRn1MfcEgzTS3_gTwI1w
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI5MDgyOTgxNjA1NTktc2R1bGFpbWhsaGpxOTY5M2s1Z2E4c25pZjhh%0D%0ANzhlZ3BAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0%0D%0AdHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvYW5hbHl0aWNzLnJlYWRvbmx5%0D%0AIiwiYXVkIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vb2F1dGgyL3Rv%0D%0Aa2VuIiwiZXhwIjoxNDMxNTE0MDUyLCJpYXQiOjE0MzE1MTEwNTJ9.[Cert]

HTTP/1.1 400 Bad Request
Content-length: 67
X-xss-protection: 1; mode=block
X-content-type-options: nosniff
Expires: Wed, 13 May 2015 10:08:00 GMT
Vary: Origin,X-Origin
Server: GSE
Cache-control: private, max-age=0
Date: Wed, 13 May 2015 10:08:00 GMT
X-frame-options: SAMEORIGIN
Content-type: application/json; charset=UTF-8
{
  "error_description": "Bad Request", 
  "error": "invalid_grant"
}
Run Code Online (Sandbox Code Playgroud)

所以,我在PL/SQL中创建它.Oracle 12c.我已经设法获得JWT标头和JWT Claim设置生成与google文档相同的输出.当我创建证书时,我认为是问题发生.

  1. 我需要"[----- BEGIN PRIVATE KEY -----"和----- END PRIVATE KEY ----- \n]作为SHA-256加密的一部分.我应该对"新行"做些什么吗?\n我应该用括号括起来吗?

  2. 我应该在什么时候进行URL编码?

  3. 以下示例中的代码是否足以用于加密?:http://jastraub.blogspot.co.uk/2009/07/hmacsha256-in-plsql.html

  4. 我已经附上了下面的功能,看看你是否可以发现任何问题?

谢谢你的帮助!

  FUNCTION get_JWT (p_token_id ga_app_user.ID_TOKEN%TYPE)
  RETURN VARCHAR2
  IS
  --Plain text
  baseJWTheader   VARCHAR2 (20000);
  baseclaimSet    VARCHAR2 (20000);
  baseSigKey      VARCHAR2 (20000);

  --Seconds

  sysSeconds      NUMBER;

  --Base64 Encoded
  JWTheader       VARCHAR2 (20000);
  claimSet        VARCHAR2 (20000);
  sigKey          VARCHAR2 (20000);
  sigContent      VARCHAR2 (20000);


  --Returned value
  output          RAW (20000);
   BEGIN
    SELECT JWT_HEADER, JWT_CLAIM_SET, SIGNATURE
      INTO baseJWTheader, baseclaimSet, baseSigKey
      FROM dwman.ga_app_user au
     WHERE AU.ID_TOKEN = p_token_id;

   --DBMS_OUTPUT.PUT_LINE ('Base claim Set ' || baseclaimSet);
   JWTheader :=
     TRANSLATE (
        UTL_RAW.cast_to_varchar2 (
           UTL_ENCODE.BASE64_ENCODE (UTL_RAW.CAST_TO_RAW (baseJWTheader))),
        '+/',
        '-_');

  SELECT   (  SYSDATE
            - TO_DATE ('01-01-1970 00:00:00', 'DD-MM-YYYY HH24:MI:SS'))
         * 24
         * 60
         * 60
    INTO sysSeconds
    FROM DUAL;

  baseclaimSet :=
     REPLACE (baseclaimSet, '#EXPIRE#', ROUND (sysSeconds + 3000));
  baseclaimSet := REPLACE (baseclaimSet, '#START#', ROUND (sysSeconds));

  --DBMS_OUTPUT.PUT_LINE ('Claim Set ' || baseclaimSet);

  claimSet := UTL_RAW.cast_to_varchar2 (
           UTL_ENCODE.BASE64_ENCODE (UTL_RAW.CAST_TO_RAW (baseclaimSet)));

  sigKey := baseSigKey;

  sigContent := JWTheader || '.' || claimSet;

  --DBMS_OUTPUT.PUT_LINE('Sig Content '||sigContent);

  sigContent := REPLACE (sigContent, CHR (10), '');
  sigContent := REPLACE (sigContent, CHR (13), '');
  /*
  FOR V_TR in 1..length(sigContent)
  LOOP
    DBMS_OUTPUT.PUT_LINE (substr(sigContent,V_TR,1)||'='||to_char(ASCII(substr(sigContent,V_TR,1))));

  END LOOP;
  */      
  sigContent :=
        sigContent
     || '.'
     || google_signature (sigContent, sigKey);
  RETURN UTL_URL.ESCAPE(sigContent, TRUE, 'UTF-8');
 END get_JWT;
Run Code Online (Sandbox Code Playgroud)

小智 1

您的代码片段可能只是您所做的一部分,但它似乎缺少 Google 连接所需的许多 OAUTH 步骤。

您可以通过查看以下 URL 来了解有关这些步骤的更多详细信息: https: //developers.google.com/accounts/docs/OAuth2WebServer#offline

对于这个答案的其余部分,我描述了我自己的经历,做了类似的事情(从 GA 下载数据,并使用 SQL 语句将其上传到数据库)。

首先为您的 Google 项目获取consumerKey 和consumerSecret。您需要有一个 Google 可以重定向到的 URL,无论是在您请求消费者密钥时,还是在 OAuth 调用期间提供给 Google 时。他们必须匹配。

下一步是向 Google 发送 GET 请求。下面是一个 C# 示例,您可以使用 SQL 字符串连接来构建它。

String URL_AUTH_FIRST = "https://accounts.google.com/o/oauth2/auth";
String URL_TOKEN_ENDPOINT_SECOND = "https://accounts.google.com/o/oauth2/token";
String url = String.Format(
    "{0}?client_id={1}&redirect_uri={2}&access_type=offline&scope={3}&response_type=code&state={4}&approval_prompt=force",
        URL_AUTH_FIRST,_consumerKey_web_app,redir_url,scope,state);
Run Code Online (Sandbox Code Playgroud)

您需要有一个嵌入式浏览器才能执行此操作。Google.com 会将该浏览器重定向到其控制下的网站,以便用户必须能够登录或拒绝授权您的应用程序。在 Google 获得所需信息后,他们会重定向回您的嵌入式浏览器。您可以在自己的浏览器中使用复制/粘贴来执行某些步骤,但在某些时候(如下所述)您必须回发一些数据,我不知道您是否可以从浏览器应用程序执行此操作。

Google 将通过将您的嵌入式浏览器重定向到某个网址来做出响应。URL 上有数据。您必须解析 URL 上的参数并查找参数“code”。如果你得到一个以“code”作为参数的url,你就必须以某种格式POST回Google。

WebClient client = get_WebClient(); // proprietary to include things like proxy info
try {
    NameValueCollection values = new NameValueCollection();
    values.Add("client_id", _consumerKey_web_app);
    values.Add("client_secret", _consumerSecret_web_app_offline);
    values.Add("grant_type", "authorization_code");
    values.Add("redirect_uri", URL_GOOGLE_REDIRECTS_TO_THIS_URL_AFTER_URL_AUTH);
    values.Add("code", authorization_code);
    Byte[] responseBytes = client.UploadValues(URL_TOKEN_ENDPOINT_SECOND, values);
}
Run Code Online (Sandbox Code Playgroud)

Google 将返回“responseBytes”,它是一个 json 格式的字符串,类似于:

{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
                  "expires_in":3920,
                  "token_type":"Bearer",
                  "refresh_token":"1/6BMfW9j53gdGImsixUH6kU5RsR4zwI9lUVX-tqf8JXQ"
                }
Run Code Online (Sandbox Code Playgroud)

access_token 会附加到您的 REST API 调用中。

您可以通过 REST API 提供 GA 查询,取回数据,然后使用 SQL 语句将其上传到数据库中。这就是我的应用程序的作用。

您可以保存该刷新令牌并在将来的连接中提供它。事实上,这整个序列需要通过浏览器或浏览器控件与用户登录交互地完成。完成后,并且您收到了刷新令牌,那么您的 SQL 基本上可以无限期地使用和重复使用刷新令牌,至少在用户密码更改之前。

Google 还会定期返回 401 错误。这仅意味着您必须通过向 Google 发布一组新值来重新请求您的访问令牌:

NameValueCollection values = new NameValueCollection();
                    values.Add("client_id", _consumerKey_web_app);
                    values.Add("client_secret", _consumerSecret_web_app_offline);
                    values.Add("refresh_token", refresh_token);
                    values.Add("grant_type", "refresh_token");
                    Byte[] responseBytes = client.UploadValues(URL_TOKEN_ENDPOINT_SECOND, values);
Run Code Online (Sandbox Code Playgroud)