当 运行 WSO2 AM 1.10.0 的(新)发布 API 时,范围验证失败

Scope validation fails when running the (new) published API of WSO2 AM 1.10.0

我在下面的答案中提供了一个工作代码,因为这组 API 相对较新,我在网上看不到其他 end-to-end 示例, 因此对于希望使用新的 API 集的任何人来说可能是有用的参考。

我正在尝试使用新发布的一组 APIWSO2 AM 1.10.0。我根据 Sanjeewa Malalgoda 的一篇文章编写了一个示例客户端:http://wso2.com/library/articles/2015/11/article-introducing-wso2-api-manager-new-rest-api-for-store-and-publisher-operations

这是代码,基于那篇文章并修复了一些小问题 errors/typos:

  package test;

  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.Base64;
  import java.util.HashMap;
  import java.util.Map;

  import org.json.JSONObject;
  import org.wso2.carbon.automation.engine.exceptions.AutomationFrameworkException;
  import org.wso2.carbon.automation.test.utils.http.client.HttpRequestUtil;
  import org.wso2.carbon.automation.test.utils.http.client.HttpResponse;

  public class test 
  {
    public static void main(String[] args) throws
        UnsupportedEncodingException,
        AutomationFrameworkException,
        InterruptedException,
        MalformedURLException,
        IOException
    {

        //    PHASE 1: REGISTER CLIENT
        //    ------------------------

        String dcrEndpointURL = getKeyManagerURLHttp() +
                              "client-registration/v0.9/register";

        String applicationRequestBody = " {\n" +
                    " \"callbackUrl\": \"google.sk\",\n" +
                    " \"clientName\": \"test_11\",\n" +
                    " \"tokenScope\": \"Production\",\n" +
                    " \"owner\": \"admin\",\n" +
                    " \"grantType\": \"password refresh_token\",\n" +
                    " \"saasApp\": true\n" +
                    " }";
        Map<String, String> dcrRequestHeaders = new HashMap<String, String>();

        //  This is base 64 encoded basic Auth value for user name admin and password admin.
        String basicAuthAdmin = "admin" + ":" + "admin";
        byte [] encodedBytesAdmin = Base64.getEncoder().encode(basicAuthAdmin.getBytes("UTF-8"));
        dcrRequestHeaders.put("Authorization", "Basic " + new String(encodedBytesAdmin, "UTF-8"));
        System.out.println(dcrRequestHeaders.get("Authorization"));

        dcrRequestHeaders.put("Content-Type", "application/json");

        JSONObject clientRegistrationResponse = new JSONObject(HttpRequestUtil.doPost(
             new URL(dcrEndpointURL), 
             applicationRequestBody,dcrRequestHeaders));

        System.out.println(clientRegistrationResponse);

        consumerKey = new JSONObject(clientRegistrationResponse.getString("data")).get("clientId").toString();
        consumerSecret =new JSONObject(clientRegistrationResponse.getString("data")).get("clientSecret").toString();
        System.out.println(consumerKey);
        System.out.println(consumerSecret);

        Thread.sleep(2000);

        //    PHASE 2: REQUEST TOKEN
        //    ----------------------

        String requestBody = "grant_type=password&username=admin&password=admin&scope=API_CREATOR_SCOPE";

        URL tokenEndpointURL = new URL(getGatewayURLNhttp() + "token");

        Map<String, String> authenticationRequestHeaders = new HashMap<String, String>();
        String basicAuthConsumer = consumerKey + ":" + consumerSecret;
        byte [] encodedBytesConsumer = Base64.getEncoder().encode(basicAuthConsumer.getBytes("UTF-8"));
        authenticationRequestHeaders.put("Authorization", "Basic " + new String(encodedBytesConsumer, "UTF-8"));

        JSONObject accessTokenGenerationResponse = new JSONObject(HttpRequestUtil.doPost(tokenEndpointURL, requestBody, authenticationRequestHeaders));
        System.out.println(accessTokenGenerationResponse);

        //Get access token and refresh token from token API call.
        //Now we have access token and refresh token that we can use to invoke API.
        JSONObject tokenData = new JSONObject(accessTokenGenerationResponse.getString("data"));
        String userAccessToken = tokenData.getString("access_token"); 
        String refreshToken = tokenData.getString("refresh_token");
        System.out.println("Access token: " + userAccessToken);     
        System.out.println("Refresh token: " + refreshToken);

        //    PHASE 3: CALL THE API
        //    ---------------------

        Map<String, String> requestHeaders = new HashMap<String, String>();     
        requestHeaders.put("Authorization", "Bearer " + userAccessToken);

        System.out.println(requestHeaders);

        HttpResponse response = HttpRequestUtil.doGet(getKeyManagerURLHttp()+"api/am/publisher/v0.9/apis?query=admin&type=provide",requestHeaders);
        System.out.println(response.getResponseCode());
        System.out.println(response.getResponseMessage());      
    }

    static String getKeyManagerURLHttp()
    {
        return "http://127.0.0.1:9763/";
    }

    static String getGatewayURLNhttp()
    {
        return "http://127.0.0.1:8280/";
    }
  }

代码注册客户端,returns访问和刷新令牌成功。

这是令牌请求的响应:

"data":     
    {
      "access_token": "bb5176def22ffbc4a7b12d2fd1ee9733",
      "refresh_token": "357926275971df21f9529ebb30ba36d1",
      "scope":"default",
      "token_type":"Bearer",
      "expires_in":2502
    }

但是,当我在 API 查询的 header 中发送此令牌时:

    {Authorization=Bearer bb5176def22ffbc4a7b12d2fd1ee9733}

我收到错误代码 401/未授权。

查看服务器日志,我得到以下信息:

[2016-02-07 17:38:20,371] ERROR - WebAppAuthenticatorImpl You cannot access API as scope validation failed
[2016-02-07 17:38:20,372]  WARN - PhaseInterceptorChain Interceptor for {http://publisher.api.rest.apimgt.carbon.wso2.org/}SubscriptionsApi has thrown exception, unwinding now
org.apache.cxf.interceptor.security.AuthenticationException: Unauthenticated request
        at org.wso2.carbon.apimgt.rest.api.util.interceptors.auth.OAuthAuthenticationInterceptor.handleMessage(OAuthAuthenticationInterceptor.java:62)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
        at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
        at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:251)
        at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234)
        at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208)
        at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160)
        at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:180)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:293)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:217)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:268)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.wso2.carbon.tomcat.ext.valves.CompositeValve.continueInvocation(CompositeValve.java:99)
        at org.wso2.carbon.tomcat.ext.valves.CarbonTomcatValve.invoke(CarbonTomcatValve.java:47)
        at org.wso2.carbon.webapp.mgt.TenantLazyLoaderValve.invoke(TenantLazyLoaderValve.java:57)
        at org.wso2.carbon.tomcat.ext.valves.TomcatValveContainer.invokeValves(TomcatValveContainer.java:47)
        at org.wso2.carbon.tomcat.ext.valves.CompositeValve.invoke(CompositeValve.java:62)
        at org.wso2.carbon.tomcat.ext.valves.CarbonStuckThreadDetectionValve.invoke(CarbonStuckThreadDetectionValve.java:159)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
        at org.wso2.carbon.tomcat.ext.valves.CarbonContextCreatorValve.invoke(CarbonContextCreatorValve.java:57)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1074)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1698)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

一些笔记

  1. 我是 运行 版本 1.10.0,来自 http://wso2.com/api-management/try-it
  2. WSO2 Published API 我在此部署中拥有的都是 v0.9(而不是 v1,如某些示例中所示)
  3. 我尝试了 API_CREATOR_SCOPE、API_PUBLISHER_SCOPE 的令牌请求。都失败了。
  4. 在令牌请求的响应中,显示 "scope: default"。不确定这是否可以。
  5. 异常显示 "You cannot access API as scope validation failed",所以我猜范围有问题。但我不确定为什么以及如何解决。

请检查 /_system/config/apimgt/applicationdata/tenant-conf.json 文件中可用的角色和范围。然后请求带有此处提到的范围的令牌。然后您将获得具有正确范围的访问令牌。请注意,具有默认范围的令牌不能用于 REST API 功能。

这是一个工作示例,遵循 Sanjeewa 的修复。

  package test;

  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.Base64;
  import java.util.HashMap;
  import java.util.Map;

  import org.json.JSONObject;
  import org.wso2.carbon.automation.engine.exceptions.AutomationFrameworkException;
  import org.wso2.carbon.automation.test.utils.http.client.HttpRequestUtil;
  import org.wso2.carbon.automation.test.utils.http.client.HttpResponse;

  public class test 
  {
    public static void main(String[] args) throws
        UnsupportedEncodingException,
        AutomationFrameworkException,
        InterruptedException,
        MalformedURLException,
        IOException
    {

        //    PHASE 1: REGISTER CLIENT
        //    ------------------------

        String dcrEndpointURL = getKeyManagerURLHttp() +
                              "client-registration/v0.9/register";

        String applicationRequestBody = " {\n" +
                    " \"callbackUrl\": \"google.sk\",\n" +
                    " \"clientName\": \"test_11\",\n" +
                    " \"tokenScope\": \"Production\",\n" +
                    " \"owner\": \"admin\",\n" +
                    " \"grantType\": \"password refresh_token\",\n" +
                    " \"saasApp\": true\n" +
                    " }";
        Map<String, String> dcrRequestHeaders = new HashMap<String, String>();

        //  This is base 64 encoded basic Auth value for user name admin and password admin.
        String basicAuthAdmin = "admin" + ":" + "admin";
        byte [] encodedBytesAdmin = Base64.getEncoder().encode(basicAuthAdmin.getBytes("UTF-8"));
        dcrRequestHeaders.put("Authorization", "Basic " + new String(encodedBytesAdmin, "UTF-8"));
        System.out.println(dcrRequestHeaders.get("Authorization"));

        dcrRequestHeaders.put("Content-Type", "application/json");

        JSONObject clientRegistrationResponse = new JSONObject(HttpRequestUtil.doPost(
             new URL(dcrEndpointURL), 
             applicationRequestBody,dcrRequestHeaders));

        System.out.println(clientRegistrationResponse);

        consumerKey = new JSONObject(clientRegistrationResponse.getString("data")).get("clientId").toString();
        consumerSecret =new JSONObject(clientRegistrationResponse.getString("data")).get("clientSecret").toString();
        System.out.println(consumerKey);
        System.out.println(consumerSecret);

        Thread.sleep(2000);

        //    PHASE 2: REQUEST TOKEN
        //    ----------------------

        String requestBody = "grant_type=password&username=admin&password=admin&scope=apim:view_api";

        URL tokenEndpointURL = new URL(getGatewayURLNhttp() + "token");

        Map<String, String> authenticationRequestHeaders = new HashMap<String, String>();
        String basicAuthConsumer = consumerKey + ":" + consumerSecret;
        byte [] encodedBytesConsumer = Base64.getEncoder().encode(basicAuthConsumer.getBytes("UTF-8"));
        authenticationRequestHeaders.put("Authorization", "Basic " + new String(encodedBytesConsumer, "UTF-8"));

        JSONObject accessTokenGenerationResponse = new JSONObject(HttpRequestUtil.doPost(tokenEndpointURL, requestBody, authenticationRequestHeaders));
        System.out.println(accessTokenGenerationResponse);

        //Get access token and refresh token from token API call.
        //Now we have access token and refresh token that we can use to invoke API.
        JSONObject tokenData = new JSONObject(accessTokenGenerationResponse.getString("data"));
        String userAccessToken = tokenData.getString("access_token"); 
        String refreshToken = tokenData.getString("refresh_token");
        System.out.println("Access token: " + userAccessToken);     
        System.out.println("Refresh token: " + refreshToken);

        //    PHASE 3: CALL THE API
        //    ---------------------

        Map<String, String> requestHeaders = new HashMap<String, String>();     
        requestHeaders.put("Authorization", "Bearer " + userAccessToken);

        System.out.println(requestHeaders);

        HttpResponse response = HttpRequestUtil.doGet(getKeyManagerURLHttp()+"api/am/publisher/v0.9/apis?query=admin&type=provide",requestHeaders);
        System.out.println(response.getResponseCode());
        System.out.println(response.getResponseMessage());      
        System.out.println(response.getData());     
    }

    static String getKeyManagerURLHttp()
    {
        return "http://127.0.0.1:9763/";
    }

    static String getGatewayURLNhttp()
    {
        return "http://127.0.0.1:8280/";
    }
  }

结果:

200
OK
{"count":1,"next":"","list":[{"name":"employees","context":"/employees","id":"09cef2c8-89f0-405e-97af-225942a52d83","description":null,"version":"1.0.0","provider":"admin","status":"PUBLISHED"}],"previous":""}

对于基本身份验证,将 repository\deployment\server\webapps\api#am#publisher#v0.9\WEB-INF 的 beans.xml 更改为:

<bean id="AuthenticationInterceptor" class="org.wso2.carbon.apimgt.rest.api.util.interceptors.auth.BasicAuthenticationInterceptor" />

然后代码就大大简化了:

public class test {

public static void main(String[] args) throws
    UnsupportedEncodingException,
    AutomationFrameworkException,
    InterruptedException,
    MalformedURLException,
    IOException
{
    String dcrEndpointURL = getKeyManagerURLHttp()+"client-registration/v0.9/register";

    String basicAuthAdmin = "admin" + ":" + "admin";
    byte [] encodedBytesAdmin = Base64.getEncoder().encode(basicAuthAdmin.getBytes("UTF-8"));
    Map<String, String> requestHeaders = new HashMap<String, String>();     
    requestHeaders.put("Authorization", "Basic " + new String(encodedBytesAdmin, "UTF-8"));

    System.out.println(requestHeaders);

    HttpResponse response = HttpRequestUtil.doGet(getKeyManagerURLHttp()+"api/am/publisher/v0.9/apis",requestHeaders);
    System.out.println(response.getResponseCode());
    System.out.println(response.getResponseMessage());      
    System.out.println(response.getData());

    HttpResponse response1 = HttpRequestUtil.doGet(getKeyManagerURLHttp()+"api/am/publisher/v0.9/subscriptions",requestHeaders);
    System.out.println(response1.getResponseCode());
    System.out.println(response1.getResponseMessage());
    System.out.println(response1.getData());

}

static String getKeyManagerURLHttp()
{
    return "http://127.0.0.1:9763/";
}

static String getGatewayURLNhttp()
{
    return "http://127.0.0.1:8280/";
}
}