cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
10784
Views
10
Helpful
8
Replies
Highlighted
Beginner

Not able to access APIC REST APIs with web token

Hi,

I have written a Simple Java REST client using Spring boot framework and tried to access APIs exposed by APIC.

1. I am able to get proper response from POST request :

POST : https://[apic ip]/api/aaaLogin.json?gui-token-request=yes

payload

{

"aaaUser" : {

"attributes" : {

"name" : "username",

"pwd" : "password"

}

}

}

2. To make subsequent REST request I have used url token provide by above REST response as header with name 'APIC-challenge'

GET : https://[apic-ip]/api/class/topSystem.json

I got following response : Response code : 403

"text":"Need a valid webtoken cookie (named APIC-Cookie) or a signed request with signature in the cookie APIC-Request-Signature for all REST API requests"

Kindly suggest.

1 ACCEPTED SOLUTION

Accepted Solutions
Highlighted

Customer replied later, and apparently it didn't make it into this thread:

I have added following header to second REST request :

header1.add("Cookie", "APIC-Cookie="+apicCookie);

And I am getting 200 Ok result and hence my issue has been resolved.

Thanks

Abhishek

View solution in original post

8 REPLIES 8
Highlighted
Beginner

Also, if REST request made by browser clients like POSTMAN required results are observed

Highlighted

If it worked in POSTMAN, then it should have worked in Java.  From this, it looks like cookies may be blocked in your Java app.

If that's not the case, then could you send us your Java code so we can have a look, and try it here?

Thanks,

Dave

Highlighted

I have created a simple REST Client using Spring boot, when I try to make first POST call to following service :

https://192.168.1.145/api/aaaLogin.json?gui-token-request=yes

Using following implementation :

import java.util.ArrayList;

import java.util.List;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.http.HttpEntity;

import org.springframework.http.HttpHeaders;

import org.springframework.http.HttpMethod;

import org.springframework.http.MediaType;

import org.springframework.http.ResponseEntity;

import org.springframework.web.client.HttpClientErrorException;

import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.JsonFactory;

import com.fasterxml.jackson.core.JsonParser;

import com.fasterxml.jackson.core.JsonToken;

@SpringBootApplication

public class RestApplication implements CommandLineRunner {

  String jsonStringUser = "{" + "\"aaaUser\":" + "{" + "\"attributes\":" + "{" + "\"name\":\"user\"" + ","

  + "\"pwd\":\"password\"" + "} } }";

  public static void main(String args[]) {

  SpringApplication.run(RestApplication.class);

  }

  @Override

  public void run(String... args) throws Exception {

  RestTemplate restTemplate = new RestTemplate();

  String urlToken = "";

  HttpHeaders header = new HttpHeaders();

  header.setContentType(MediaType.APPLICATION_JSON);

  List<MediaType> aList = new ArrayList<MediaType>();

  aList.add(MediaType.APPLICATION_JSON);

  header.setAccept(aList);

  HttpEntity<String> entity = new HttpEntity<String>(jsonStringUser, header);

  try {

  ResponseEntity<String> result = restTemplate.exchange(

  "http://192.168.1.145/api/aaaLogin.json?gui-token-request=yes", HttpMethod.POST, entity,

  String.class);

  JsonFactory factory = new JsonFactory();

  JsonParser parser = factory.createParser(result.getBody().toString());

  while (!parser.isClosed()) {

  JsonToken jsonToken = parser.nextToken();

  if (JsonToken.VALUE_STRING.equals(jsonToken)) {

  String fieldName = (String) parser.getCurrentName();

  if (fieldName.equals("urlToken")) {

  urlToken = parser.getValueAsString();

  System.out.println("Value :            " + urlToken);

  break;

  }

  }

  }

  }

  catch (HttpClientErrorException ex) {

  System.out.println("Exception is " + ex.getMessage());

  System.out.println("Exception is " + ex.getResponseBodyAsString());

  System.out.println("Exception is " + ex.getMostSpecificCause());

  }

  }

}

It failed throwing following exception :

java.lang.IllegalStateException: Failed to execute CommandLineRunner

  at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:803) [spring-boot-1.3.6.RELEASE.jar:1.3.6.RELEASE]

  at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:784) [spring-boot-1.3.6.RELEASE.jar:1.3.6.RELEASE]

  at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:771) [spring-boot-1.3.6.RELEASE.jar:1.3.6.RELEASE]

  at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-1.3.6.RELEASE.jar:1.3.6.RELEASE]

  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1185) [spring-boot-1.3.6.RELEASE.jar:1.3.6.RELEASE]

  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1174) [spring-boot-1.3.6.RELEASE.jar:1.3.6.RELEASE]

  at borderlessodc.api.Application.main(Application.java:135) [main/:na]

Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://192.168.1.145/api/aaaLogin.json?gui-token-request=yes": java.security.cert.CertificateException: No subject alternative names present; nested exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present

  at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:607) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]

  at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]

  at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:475) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]

  at borderlessodc.api.Application.run(Application.java:200) [main/:na]

  at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) [spri-1.3.6.RELEASE.jar:1.3.6.RELEASE]

  ... 6 common frames omitted

Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present

  at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) ~[na:1.8.0_91]

  at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949) ~[na:1.8.0_91]

  at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302) ~[na:1.8.0_91]

  at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296) ~[na:1.8.0_91]

  at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509) ~[na:1.8.0_91]

  at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216) ~[na:1.8.0_91]

  at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979) ~[na:1.8.0_91]

  at sun.security.ssl.Handshaker.process_record(Handshaker.java:914) ~[na:1.8.0_91]

  at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062) ~[na:1.8.0_91]

  at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) ~[na:1.8.0_91]

  at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) ~[na:1.8.0_91]

  at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) ~[na:1.8.0_91]

  at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) ~[na:1.8.0_91]

  at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) ~[na:1.8.0_91]

  at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153) ~[na:1.8.0_91]

  at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:80) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]

  at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]

  at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53) ~[spring-web-4.2.7.RELEASE.jar:4.2.7                                                                                                                                        .RELEASE]

  at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:596) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]

  ... 10 common frames omitted

Caused by: java.security.cert.CertificateException: No subject alternative names present

  at sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:144) ~[na:1.8.0_91]

  at sun.security.util.HostnameChecker.match(HostnameChecker.java:93) ~[na:1.8.0_91]

  at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:455) ~[na:1.8.0_91]

  at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:436) ~[na:1.8.0_91]

  at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:200) ~[na:1.8.0_91]

  at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124) ~[na:1.8.0_91]

  at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491) ~[na:1.8.0_91]

  ... 24 common frames omitted

To overcome above mentioned error I have added this code before the first call to the REST :

javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(

  new javax.net.ssl.HostnameVerifier(){

     public boolean verify(String hostname,

             javax.net.ssl.SSLSession sslSession) {

         return hostname.equals("192.168.1.145");

     }

  });

Then my first REST call completed with expected response and I am able to extract the urlToken from it which I again passed to subsequent REST call as "APIC-challenger" header.

RestTemplate restTemplate1 = new RestTemplate();

  HttpHeaders header1 = new HttpHeaders();

  header.setContentType(MediaType.APPLICATION_JSON);

  List<MediaType> aList1 = new ArrayList<MediaType>();

  aList1.add(MediaType.APPLICATION_JSON);

  header1.setAccept(aList1);

  header1.set("APIC-challenge", urlToken);

  HttpEntity<String> entity1 = new HttpEntity<String>(header1);

  System.out.println("Second Entity         " + entity1);

  ResponseEntity<String> result1 = restTemplate1.exchange("https://192.168.1.145/api/class/topSystem.json",

  HttpMethod.GET, entity1, String.class);

Now making this subsequent call I am getting following response :

{"imdata":[{"error":{"attributes":{"code":"403","text":"Need a valid webtoken cookie (named APIC-Cookie) or a signed request with signature in the cookie APIC-Request-Signature for all REST API requests"}}}]}

I also tried passing APIC-Cookie header with the value received from first POST response header but response remains the same.

Can security imposed by APIC be bypassed i.e. is there any way to turn of this security from APIC web GUI and accordingly later on roll back?

Kindly suggest.

Thanks,

Abhishek

Highlighted

Customer replied later, and apparently it didn't make it into this thread:

I have added following header to second REST request :

header1.add("Cookie", "APIC-Cookie="+apicCookie);

And I am getting 200 Ok result and hence my issue has been resolved.

Thanks

Abhishek

View solution in original post

Highlighted

Does anyone know where this header is added if using Postman?

Highlighted

For anyone else having this issue in Postman (which does automatically handle cookies by default), make sure you are using an HTTPS:// URI. If you copied/pasted from API Inspector in the APIC, it uses HTTP which only works from within the interface. Outside requires HTTPS and will give this (above) error if trying with HTTP. 

 

Switch to HTTPS and it should work.

Highlighted

Where do I add this code

header1.add("Cookie", "APIC-Cookie="+apicCookie);

in the login request?

Highlighted
Beginner

For all of you who make API calls with Ansible URI module, this is what you need to do:

 

    - name: Login
      uri:
        url: "https://<IPADDRESS>/api/aaaLogin.json"
        method: POST
        headers:
          Content-Type: application/json
        body_format: json
        body:
          {
           "aaaUser":{
             "attributes":{
               "name": "<USER>",
               "pwd": "<PASSWORD>"
                          }
                     }
          }
        return_content: yes
        validate_certs: no
      register: output

This will return a token so that you can make any API call without using any password and username, for instance:

Getting the local users via API call using the token we just got from the API call above:

    - name: Create Token
      set_fact:
        aci_token: "{{ output['cookies_string'] }}"

    - uri:
        url: "https://<IPADDRESS>/api/class/aaaUser.json"
        method: GET
        headers:
          Content-Type: application/json
          Cookie: "{{ aci_token }}"
        return_content: yes
        validate_certs: no
      register: local_user

    - debug: var=local_user

Hope it helps !

 

Regards,

Ernesto Quintana 

Content for Community-Ad