Google api 双腿oauth2:如何在没有特定API库的情况下调用google drive rest API

Google api 双腿oauth2:如何在没有特定API库的情况下调用google drive rest API,google-api,google-oauth,google-api-java-client,Google Api,Google Oauth,Google Api Java Client,我在Google开发者控制台中创建了一个应用程序,然后创建了OAuth2凭据。我有客户id和客户机密。现在,我想使用这些来获取一个访问令牌,用于对GoogleDrive API的两段式调用。我正在用java使用Google的oauth2客户端: import com.google.api.client.auth.oauth2.ClientCredentialsTokenRequest; import com.google.api.client.auth.oauth2.ClientParamete

我在Google开发者控制台中创建了一个应用程序,然后创建了OAuth2凭据。我有客户id和客户机密。现在,我想使用这些来获取一个访问令牌,用于对GoogleDrive API的两段式调用。我正在用java使用Google的oauth2客户端:

import com.google.api.client.auth.oauth2.ClientCredentialsTokenRequest;
import com.google.api.client.auth.oauth2.ClientParametersAuthentication;
import com.google.api.client.auth.oauth2.TokenResponse;
...
public void oauth2Test() {
   String clientId = "...";
   String clientSecret = "...";
   ClientCredentialsTokenRequest request = new ClientCredentialsTokenRequest(
         new NetHttpTransport(),
         new JacksonFactory(),
         new GenericUrl("https://accounts.google.com/o/oauth2/auth"));
   request.setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret));
   TokenResponse response;
   try {
      response = request.execute();      
   } catch (Exception ex) {
      ex.printStackTrace();
   }
}
然而,我收到一个“400错误请求”的消息

“缺少必需的参数:响应类型”

在双腿请求模型中,获取访问令牌的正确方法是什么?注意:我只有client_id和client_secret,没有完整的API令牌

编辑:我最初的问题不准确。虽然我更喜欢从client_id和client_secret开始,但这并不是必需的。可以使用谷歌特定的API来获取访问令牌,也可以使用谷歌认证。必要的是,我能够在通用REST调用中使用从授权过程中获得的任何访问令牌。换句话说,给定google应用程序凭据(可以是{client_id,client_secret}或JSON或P12格式的google服务帐户密钥),我如何获取访问令牌以及如何在REST API调用中使用它们——我是设置授权头还是其他什么

第一个答案指出不支持client_凭证,我已经验证了这一点。但是我仍然需要一个路径来获取承载令牌,这样我就可以在REST调用中使用它,而无需特定的Google客户端API库。所以我开始编写代码,但使用谷歌库。它需要一个JSON凭证文件

InputStream is = getClass().getResourceAsStream("JSONCredFile");
GoogleCredential credential = GoogleCredential.fromStream(is).createScoped(scopes);
Drive service = new Drive.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
        .setApplicationName("My app")
        .build();
FileList result = service.files().list().setPageSize(10)
        .setFields("nextPageToken, files(id, name)")
        .execute();
通过将SSLSocket代理连接到凭证(细节省略),我能够跟踪出站通信:

POST /token HTTP/1.1
Accept-Encoding: gzip
User-Agent: Google-HTTP-Java-Client/1.23.0 (gzip)
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: oauth2.googleapis.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 771

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=<lots of encoded stuff>
很明显,这就是问题所在。但是现在呢?不知何故,我需要在不通过特定库进行API调用的情况下获取承载令牌。GoogleOAuth2库似乎不支持这种请求类型,至少我没有看到TokenRequest的“JWT”风格。我可以直接编写OAuth2调用,或者创建支持JWT的TokenRequest子类


有更好的主意吗?

谷歌不支持grant_type=client_凭证,这是使用OAuth客户端ID和密码执行2LO的方式


您可以使用服务帐户执行2LO:

好的,我终于想出了如何创建JWT、发送OAuth2请求和提取访问令牌。为了更容易地与google OAuth2客户端集成,我将TokenRequest子类化:

import com.google.api.client.auth.oauth2.TokenRequest;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Collection;
import java.util.stream.Collectors;
import org.joda.time.DateTime;

/**
 * @author jlilley
 */
public class JWTTokenRequest extends TokenRequest {
    private String serviceKeyJson;
    private boolean doRsa = true;
    public JWTTokenRequest(HttpTransport transport, JsonFactory jsonFactory, GenericUrl tokenServerUrl) {
        super(transport, jsonFactory, tokenServerUrl, "urn:ietf:params:oauth:grant-type:jwt-bearer");
    }
    @Override
    public JWTTokenRequest setTokenServerUrl(GenericUrl tokenServerUrl) {
        return (JWTTokenRequest)super.setTokenServerUrl(tokenServerUrl);
    }
    public JWTTokenRequest setServiceKey(String json) throws Exception {
        this.serviceKeyJson = json;
        return this;
    }
    public JWTTokenRequest setServiceKey(InputStream is) throws Exception {
        try (BufferedReader buffer = new BufferedReader(new InputStreamReader(is))) {
            return setServiceKey(buffer.lines().collect(Collectors.joining("\n")));
        }
    }
    @Override
    public JWTTokenRequest setScopes(Collection<String> scopes) {
        return (JWTTokenRequest) super.setScopes(scopes);
    }
    @Override
    public JWTTokenRequest set(String fieldName, Object value) {
        return (JWTTokenRequest) super.set(fieldName, value);
    }    
    /**
     * Create a JWT for the given project id, signed with the given RSA key.
     */
    private String signJwtRsa(JwtBuilder jwtBuilder, PKCS8EncodedKeySpec spec) {
        try {
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return jwtBuilder.signWith(SignatureAlgorithm.RS256, kf.generatePrivate(spec)).compact();
        } catch (Exception ex) {
            throw new RuntimeException("Error signing JWT", ex);
        }
    }

    /**
     * Create a JWT for the given project id, signed with the given ES key.
     */
    private String signJwtEs(JwtBuilder jwtBuilder, PKCS8EncodedKeySpec spec) {
        try {
            KeyFactory kf = KeyFactory.getInstance("EC");
            return jwtBuilder.signWith(SignatureAlgorithm.ES256, kf.generatePrivate(spec)).compact();
        } catch (Exception ex) {
            throw new RuntimeException("Error signing JWT", ex);
        }
    }

    /**
     * Finalize the JWT and set it in the assertion property of the web service call
     * @throws java.io.IOException
     */
    void makeAssertion() {
        JsonParser parser = new JsonParser();
        JsonObject jsonDoc = (JsonObject) parser.parse(serviceKeyJson);
        String pkStr = jsonDoc.get("private_key").getAsString()
                .replace("\n", "")
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "");
        byte[] pkBytes = Base64.getDecoder().decode(pkStr);
        DateTime now = new DateTime();
        JwtBuilder jwtBuilder = Jwts.builder()
                .setIssuedAt(now.toDate())
                .setExpiration(now.plusMinutes(60).toDate())
                .setAudience(getTokenServerUrl().toString())
                .setIssuer(jsonDoc.get("client_email").getAsString());
        if (getScopes() != null) {
            jwtBuilder = jwtBuilder.claim("scope", getScopes());
        }
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkBytes);
        String pkId = jsonDoc.get("private_key_id").getAsString();
        jwtBuilder.setHeaderParam("kid", pkId);
        jwtBuilder.setHeaderParam("typ", "JWT");
        set("assertion", doRsa ? signJwtRsa(jwtBuilder, spec) : signJwtEs(jwtBuilder, spec));
    }

    /**
     * Finalize the JWT, set it in the assertion property of the web service call, and make the token request.
     */
    @Override
    public TokenResponse execute() throws IOException {
        makeAssertion();
        return super.execute();
    }
}
import com.google.api.client.auth.oauth2.TokenRequest;
导入com.google.api.client.auth.oauth2.TokenResponse;
导入com.google.api.client.http.GenericUrl;
导入com.google.api.client.http.HttpTransport;
导入com.google.api.client.json.JsonFactory;
导入com.google.gson.JsonObject;
导入com.google.gson.JsonParser;
导入io.jsonwebtoken.JwtBuilder;
导入io.jsonwebtoken.Jwts;
导入io.jsonwebtoken.SignatureAlgorithm;
导入java.io.BufferedReader;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.InputStreamReader;
导入java.security.KeyFactory;
导入java.security.spec.PKCS8EncodedKeySpec;
导入java.util.Base64;
导入java.util.Collection;
导入java.util.stream.collector;
导入org.joda.time.DateTime;
/**
*@author jliley
*/
公共类JWTTokenRequest扩展了TokenRequest{
私有字符串serviceKeyJson;
私有布尔值doRsa=true;
公共JWTTokenRequest(HttpTransport传输、JsonFactory JsonFactory、GenericUrl tokenServerUrl){
super(传输,jsonFactory,令牌服务器URL,“urn:ietf:params:oauth:grant-type:jwt-bearer”);
}
@凌驾
公共JWTTokenRequest setTokenServerUrl(GenericUrl tokenServerUrl){
返回(JWTTokenRequest)super.setTokenServerUrl(tokenServerUrl);
}
公共JWTTokenRequest setServiceKey(字符串json)引发异常{
this.serviceKeyJson=json;
归还这个;
}
公共JWTTokenRequest setServiceKey(InputStream is)引发异常{
try(BufferedReader buffer=new BufferedReader(new InputStreamReader(is))){
返回setServiceKey(buffer.lines().collect(Collectors.joining(“\n”));
}
}
@凌驾
公共JWTTokenRequest集合范围(集合范围){
返回(JWTTokenRequest)超级设置范围(范围);
}
@凌驾
公共JWTTokenRequest集(字符串字段名、对象值){
返回(JWTTokenRequest)super.set(字段名、值);
}    
/**
*为给定的项目id创建一个JWT,并使用给定的RSA密钥签名。
*/
私有字符串签名JWTRSA(JwtBuilder JwtBuilder,PKCS8EncodedKeySpec规范){
试一试{
KeyFactory kf=KeyFactory.getInstance(“RSA”);
返回jwtBuilder.signWith(SignatureAlgorithm.RS256,kf.generatePrivate(spec)).compact();
}捕获(例外情况除外){
抛出新的运行时异常(“错误签名JWT”,ex);
}
}
/**
*为给定的项目id创建一个JWT,并用给定的ES密钥签名。
*/
私有字符串签名JWTES(JwtBuilder JwtBuilder,PKCS8EncodedKeySpec规范){
试一试{
KeyFactory kf=KeyFactory.getInstance(“EC”);
返回jwtBuilder.signWith(SignatureAlgorithm.ES256,kf.generatePrivate(spec)).compact();
}捕获(例外情况除外){
抛出新的运行时异常(“错误签名JWT”,ex);
}
}
/**
*完成JWT并在web服务调用的断言属性中设置它
*@抛出java.io.IOException
*/
void makeAssertion(){
JsonParser=新的JsonParser();
JsonObject jsonDoc=(JsonObject)parser.parse(serviceKeyJson);
字符串pkStr=jsonDoc.get(“私钥”).getAsString()
.replace(“\n”和“”)
.replace(“----开始私钥-------”,“”)
.replace(“----结束私钥----”);
byte[]pkBytes=Base64.getDecoder().decode(pkStr);
DateTime now=new DateTi
import com.google.api.client.auth.oauth2.TokenRequest;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Collection;
import java.util.stream.Collectors;
import org.joda.time.DateTime;

/**
 * @author jlilley
 */
public class JWTTokenRequest extends TokenRequest {
    private String serviceKeyJson;
    private boolean doRsa = true;
    public JWTTokenRequest(HttpTransport transport, JsonFactory jsonFactory, GenericUrl tokenServerUrl) {
        super(transport, jsonFactory, tokenServerUrl, "urn:ietf:params:oauth:grant-type:jwt-bearer");
    }
    @Override
    public JWTTokenRequest setTokenServerUrl(GenericUrl tokenServerUrl) {
        return (JWTTokenRequest)super.setTokenServerUrl(tokenServerUrl);
    }
    public JWTTokenRequest setServiceKey(String json) throws Exception {
        this.serviceKeyJson = json;
        return this;
    }
    public JWTTokenRequest setServiceKey(InputStream is) throws Exception {
        try (BufferedReader buffer = new BufferedReader(new InputStreamReader(is))) {
            return setServiceKey(buffer.lines().collect(Collectors.joining("\n")));
        }
    }
    @Override
    public JWTTokenRequest setScopes(Collection<String> scopes) {
        return (JWTTokenRequest) super.setScopes(scopes);
    }
    @Override
    public JWTTokenRequest set(String fieldName, Object value) {
        return (JWTTokenRequest) super.set(fieldName, value);
    }    
    /**
     * Create a JWT for the given project id, signed with the given RSA key.
     */
    private String signJwtRsa(JwtBuilder jwtBuilder, PKCS8EncodedKeySpec spec) {
        try {
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return jwtBuilder.signWith(SignatureAlgorithm.RS256, kf.generatePrivate(spec)).compact();
        } catch (Exception ex) {
            throw new RuntimeException("Error signing JWT", ex);
        }
    }

    /**
     * Create a JWT for the given project id, signed with the given ES key.
     */
    private String signJwtEs(JwtBuilder jwtBuilder, PKCS8EncodedKeySpec spec) {
        try {
            KeyFactory kf = KeyFactory.getInstance("EC");
            return jwtBuilder.signWith(SignatureAlgorithm.ES256, kf.generatePrivate(spec)).compact();
        } catch (Exception ex) {
            throw new RuntimeException("Error signing JWT", ex);
        }
    }

    /**
     * Finalize the JWT and set it in the assertion property of the web service call
     * @throws java.io.IOException
     */
    void makeAssertion() {
        JsonParser parser = new JsonParser();
        JsonObject jsonDoc = (JsonObject) parser.parse(serviceKeyJson);
        String pkStr = jsonDoc.get("private_key").getAsString()
                .replace("\n", "")
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "");
        byte[] pkBytes = Base64.getDecoder().decode(pkStr);
        DateTime now = new DateTime();
        JwtBuilder jwtBuilder = Jwts.builder()
                .setIssuedAt(now.toDate())
                .setExpiration(now.plusMinutes(60).toDate())
                .setAudience(getTokenServerUrl().toString())
                .setIssuer(jsonDoc.get("client_email").getAsString());
        if (getScopes() != null) {
            jwtBuilder = jwtBuilder.claim("scope", getScopes());
        }
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkBytes);
        String pkId = jsonDoc.get("private_key_id").getAsString();
        jwtBuilder.setHeaderParam("kid", pkId);
        jwtBuilder.setHeaderParam("typ", "JWT");
        set("assertion", doRsa ? signJwtRsa(jwtBuilder, spec) : signJwtEs(jwtBuilder, spec));
    }

    /**
     * Finalize the JWT, set it in the assertion property of the web service call, and make the token request.
     */
    @Override
    public TokenResponse execute() throws IOException {
        makeAssertion();
        return super.execute();
    }
}