我正在尝试使用戴尔的保修 API。为此,您首先需要获取访问令牌(1 小时后过期),然后使用该令牌发出 API 请求。他们有一个邮递员教程,效果很好,但我正在尝试使一些东西更加自动化,因为我们有 1000 多个资产可以使用此 API 查找。
java.net.http尽管旧 API 存在大量示例,但我仍尝试使用,但我宁愿不使用外部库或旧 API。
要获取令牌,您可以使用您的客户端 ID 和客户端密钥向他们的 API 发送请求。
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.util.Base64;
public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        
        String tokenURL = "https://apigtwb2c.us.dell.com/auth/oauth/v2/token";
        String clientID = "{redacted}";
        String clientSecret = "{redacted}";
        String formatted = clientID + ":" + clientSecret;
        String encoded = Base64.getEncoder().encodeToString((formatted).getBytes());
        
        HttpClient client = HttpClient.newBuilder().version(Version.HTTP_1_1).followRedirects(Redirect.NORMAL).connectTimeout(Duration.ofSeconds(10)).build();
        HttpRequest request = HttpRequest.newBuilder().uri(URI.create(tokenURL)).headers("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8", "Accept", "application/json", "grant_type", "client_credentials", "Authorization", "Basic " + encoded)
                .POST(BodyPublishers.noBody()).build();
        
        System.out.println(request.headers());
        
        HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
        
        System.out.println(response.statusCode());
        System.out.println(response.body());
    }
}
Run Code Online (Sandbox Code Playgroud)
服务器正在响应
400
{
  "error":"invalid_request",
  "error_description":"Missing or duplicate parameters"
}
Run Code Online (Sandbox Code Playgroud)
根据他们的文档,400 是一个糟糕的请求。我目前正在发送一个空正文,但不确定这是否是问题所在。
我也不完全确定缺少什么。我尝试了多种标题组合但没有成功。对于 cURL 来说,同样的效果也很好,这也是我走到现在的原因。
这是 cURL 请求,以防万一..
curl -v https://apigtwb2c.us.dell.com/auth/oauth/v2/token -H "Accept: application/json" -u "{redacted}:{redacted}" -d "grant_type=client_credentials"
*   Trying 143.166.28.87:443...
* Connected to apigtwb2c.us.dell.com (143.166.28.87) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=Texas; L=Round Rock; O=Dell; CN=*.apis.dell.com
*  start date: Jul 26 19:18:16 2021 GMT
*  expire date: Jul 20 19:18:15 2022 GMT
*  subjectAltName: host "apigtwb2c.us.dell.com" matched cert's "apigtwb2c.us.dell.com"
*  issuer: C=US; O=Entrust, Inc.; OU=See www.entrust.net/legal-terms; OU=(c) 2012 Entrust, Inc. - for authorized use only; CN=Entrust Certification Authority - L1K
*  SSL certificate verify ok.
* Server auth using Basic with user '{redacted}'
> POST /auth/oauth/v2/token HTTP/1.1
> Host: apigtwb2c.us.dell.com
> Authorization: Basic {redacted}
> User-Agent: curl/7.79.1
> Accept: application/json
> Content-Length: 29
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Pragma: no-cache
< Cache-Control: no-store
< X-Correlation-ID: {redacted}
< Content-Type: application/json;charset=UTF-8
< Content-Length: 127
< Date: Thu, 14 Oct 2021 18:56:28 GMT
< Server: dell
<
{
  "access_token":"{redacted}",
  "token_type":"Bearer",
  "expires_in":3600,
  "scope":"oob"
* Connection #0 to host apigtwb2c.us.dell.com left intact
Run Code Online (Sandbox Code Playgroud)
谢谢!
根据您的curl日志,请求应该具有此内容类型application/x-www-form-urlencoded
这种内容类型需要一个主体,所以这是你的第一个错误:
.POST(BodyPublishers.noBody())
第二个错误是,在oauth2协议中,grant_type=client_credentials不是标头,它是主体中的表单参数。您还卷曲片段确认:curl ... -d "grant_type=client_credentials"。检查一下:curl -d
如果 Dell 平台严格实施 oauth2 协议,则应实施 oauth2 规范:
https://datatracker.ietf.org/doc/html/rfc6749#section-4.4
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
Run Code Online (Sandbox Code Playgroud)
其中我们可以看到身体只需要:
String encoded = Base64 ...如果戴尔实施另一种资助方式,您应该阅读文档,如下所示:
https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/
POST /token HTTP/1.1
Host: authorization-server.com
 
grant_type=client_credentials
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx
Run Code Online (Sandbox Code Playgroud)
其中我们可以看到 body 需要 client_id 和 client_secret,这与将凭证作为基本身份验证标头发送的规范不同。
如果戴尔实施此规范并假设您使用 Java 11,则此未经测试的代码应该可以工作:
String url = "https://apigtwb2c.us.dell.com/auth/oauth/v2/token";
Map<String, String> parameters = new HashMap<>();
parameters.put("grant_type", "client_credentials");
parameters.put("client_id", "****");
parameters.put("client_secret", "****");
String form = parameters.keySet().stream()
.map(key -> key + "=" + URLEncoder.encode(parameters.get(key), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url))
.headers("Content-Type", "application/x-www-form-urlencoded")
.POST(BodyPublishers.ofString(form)).build();
HttpResponse<?> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.statusCode() + response.body().toString());
Run Code Online (Sandbox Code Playgroud)
一些提供商支持多种类型的请求,因此前面的代码片段应该可以工作。如果Dell严格执行主要规范,只需删除这部分:
parameters.put("client_id", "****");
parameters.put("client_secret", "****");
Run Code Online (Sandbox Code Playgroud)
并添加您的授权标头
String formatted = clientID + ":" + clientSecret;
String encoded = Base64.getEncoder().encodeToString((formatted).getBytes());
...
.headers("Content-Type", "application/x-www-form-urlencoded", "Authorization", "Basic " + encoded)
Run Code Online (Sandbox Code Playgroud)