Allegro Rest API w praktyce z użyciem Spring Boota


Allegro Rest API w praktyce z użyciem Spring Boota

Z tego wpisu dowiesz się jak korzystać z Allegro Rest API w praktyce z użyciem Spring Boota. Zaczniemy od rejestracji nowej aplikacji w serwisie Allegro.

Rejestracja aplikacji

Pod adresem https://apps.developer.allegro.pl/new rejestrujemy nową aplikację:

Każda nowo utworzona aplikacja posiada przypisane unikalne wartości które są niezbędne do uzyskania zasobów Rest API allegro:

  • Klucz WebAPI (Client ID)
  • Client Secret

Client ID oraz Client Secret są niezbędne do komunikacji w ramach protokołu OAuth. Tokeny dostępowe są zgodne ze standardem JWT – polecam wpis na JavaLeader o tokenach JWThttps://javaleader.pl/2019/10/31/jwt-json-web-token-w-spring-boot/ . OAuth jest protokołem open source, który pozwala na bezpieczną autoryzację do API danego usługodawcy.

Uzyskujemy token dostępowy na podstawie kodu zwróconego przez Allegro, krok pierwszy to

wywołanie endpointa:

https://allegro.pl/auth/oauth/authorize?response_type=code&client_id=TUTAJ CLIENT ID&redirect_uri=https://javaleader.pl

otrzymujemy kod który niezbędny jest do uzyskania tokena JWT:

https://javaleader.pl/?code=lMLTtXHfob8f75YeLbf6IwXaMLpGhMct

Po uzyskaniu kodu należy wywołać endpoint metodą POST za pomocą dowolnego klienta restowego np. Advanced REST client:

https://allegro.pl/auth/oauth/token?grant_type=authorization_code&code=UZYSKANY KOD&redirect_uri=https://javaleader.pl

dodatkowo (co jest niezbędne) przekazując nagłówek:

authorization z parametrem:

basic Base64(client_id:client_secret)

gdzie Base64(client_id:client_secret) to zakodowany ciąg client_id:client_secret w Base64 z użyciem https://www.base64encode.org.

Konfiguracja projektu jako Resource Server (serwera zasobów):

Serwer zasobów to API które dostępne jest po poprawnej weryfikacji tokenu który jest podany przez użytkownika. Mając wygenerowany Bearer token możemy przejść do konfiguracji Resource Servera. Tworzymy nowy projekt Spring Boota – plik pom.xml – niezbędne zależności:

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-tomcat</artifactId>
		<scope>provided</scope>
	</dependency>
 
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
		<exclusions>
			<exclusion>
				<artifactId>tomcat-embed-el</artifactId>
				<groupId>org.apache.tomcat.embed</groupId>
			</exclusion>
			<exclusion>
				<artifactId>tomcat-embed-core</artifactId>
				<groupId>org.apache.tomcat.embed</groupId>
			</exclusion>
			<exclusion>
				<artifactId>tomcat-embed-websocket</artifactId>
				<groupId>org.apache.tomcat.embed</groupId>
			</exclusion>
		</exclusions>
	</dependency>
 
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
 
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-oauth2</artifactId>
	</dependency>
 
</dependencies>
 
<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>${spring-cloud.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

Klasa startowa z kluczową adnotacją @EnableResourceServer:

@SpringBootApplication
@EnableResourceServer
public class AllegroApiApplication extends SpringBootServletInitializer {
	public static void main(String[] args) {
		SpringApplication.run(AllegroApiApplication.class, args);
	}
	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
		return builder.sources(AllegroApiApplication.class);
	}
}

Plik application properties:

security.oauth2.resource.user-info-uri = https://api.allegro.pl/me
security.oauth2.resource.prefer-token-info = false
logging.level.org.springframework.security = DEBUG

Kontroller który zabezpieczony jest z użyciem:

security.oauth2.resource.user-info-uri = https://api.allegro.pl/me

przedstawia się następująco:

@RestController
public class AllegroCodeController {
    @GetMapping("/protected")
    String showJavaLeaderMsg(Principal principal){
        return "[user] " +  principal.getName() + " JavaLeader.pl!";
    }
}

dzięki czemu bez posiadania tokena nie ma możliwości dostać się do endpointa showJavaLeaderMsg. Wykonując teraz zapytanie GET z użyciem Advanced Rest Client i przekazując w nagłówku prawidłowy bearer token otrzymamy błąd 401 Unauthorized:

{
    "error": "invalid_token",
    "error_description": "..."
}

a w logach informację:

 GET request for "https://api.allegro.pl/me" resulted in 406 (Not Acceptable); invoking error handler

Dlaczego?

wynika to z tego, że zapytanie które wykonywane jest do https://api.allegro.pl/me zawiera domyślne nagłówki które nie są akceptowane przez allegro:

DEBUG 28035 --- [o-8009-exec-468] o.s.s.oauth2.client.OAuth2RestTemplate   : Created GET request for "https://api.allegro.pl/me"
DEBUG 28035 --- [o-8009-exec-468] o.s.s.oauth2.client.OAuth2RestTemplate   : Setting request Accept header to [application/json, application/*+json]

co skutkuje, że wywołanie endpointa https://api.allegro.pl/me zwraca błąd 406 (Not Acceptable). Należy to poprawić! W dokumentacji Springa można przeczytać:

“Internally, Resource Server uses an OAuth2RestTemplate to invoke the /userinfo endpoint. At times, it may be necessary to add filters or perform other customization for this invocation. To customize the creation of this bean, you can expose a UserInfoRestTemplateCustomizer, like so:”

@Bean
public UserInfoRestTemplateCustomizer customHeader() {
	return restTemplate ->
			restTemplate.getInterceptors().add(new MyCustomInterceptor());
}

w projekcie zatem tworzymy konfigurację:

@Configuration
public class Config {
    @Bean
    public UserInfoRestTemplateCustomizer customHeader() {
        return restTemplate ->
                restTemplate.getInterceptors().add(new CustomHttpRequestInterceptor());
    }
}

oraz interceptor który dodaje poprawny nagłówek ACCEPT do zapytania które wykonywane jest do endpointa https://api.allegro.pl/me:

enum AllegroEnum {
    ACCEPT ("application/vnd.allegro.public.v1+json");
    String acceptHeader;
    AllegroEnum(String ACCEPT) {
        acceptHeader = ACCEPT;
    }
}
 
public class CustomHttpRequestInterceptor implements ClientHttpRequestInterceptor {
   @Override
   public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        request.getHeaders().add("ACCEPT", AllegroEnum.ACCEPT.acceptHeader);
        return execution.execute(request, body);
    }
}

co w konsekwencji pozwala na dostęp do chronionych zasobów z użyciem mechanizmu Resource Server gdzie informacje o użytkowniku udostępniane są z serwisu allegro (oczywiście jeżeli bearer token jest prawidłowy).

DEBUG o.s.security.web.FilterChainProxy        : /protected at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
DEBUG o.s.s.oauth2.client.OAuth2RestTemplate   : Created GET request for "https://api.allegro.pl/me"
DEBUG o.s.s.oauth2.client.OAuth2RestTemplate   : Setting request Accept header to [application/json, application/*+json]
DEBUG o.s.s.oauth2.client.OAuth2RestTemplate   : GET request for "https://api.allegro.pl/me" resulted in 200 (OK)
DEBUG o.s.s.oauth2.client.OAuth2RestTemplate   : Reading [interface java.util.Map] as "application/vnd.allegro.public.v1+json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@1f5cc408]

Wykorzystanie allegro API w praktyce:

Zasób – Get product parameters available in given category (pobierz wszystkie parametry produktów w danej kategorii).

https://api.allegro.pl/sale/categories/{categoryId}/product-parameters

Tworzymy metodę kontrola w naszym API która pozwoli wyświetlić parametry produktów z allegro z danej kategorii:

@RequestMapping(value = "/sale/categories/{categoryId}/product/parameters")
public String getSaleCategoriesProductParameters(@PathVariable String categoryId,@RequestHeader("Authorization") String bearer) {
    HttpHeaders headers = new HttpHeaders();
    headers.add("ACCEPT", AllegroEnum.ACCEPT.acceptHeader);
    headers.add("Authorization", bearer );
 
    HttpEntity <String> entity = new HttpEntity(headers);
    return restTemplate.exchange("https://api.allegro.pl/sale/categories/" + categoryId + "/product-parameters",
            HttpMethod.GET, entity, String.class).getBody();
}

Testy:

/sale/categories/435/product/parameters

zwróci kod HTTP 200 z pobranymi danymi. Do API można się dostać tylko podając prawidłowy bearer token wygenerowany z allegro.

Podsumowanie:

Aplikacja którą napisaliśmy w tym artykule posiada własne API które korzysta z API Allegro. Aby korzystać z naszego API które stanowi serwer zasobów należy wygenerować niezbędny kod bearer token. Informacje o użytkowniku dostarczane są przez allegro pod adresem https://api.allegro.pl/me.


Leave a comment

Your email address will not be published.


*