Share
Embed
Bootiful REST Services with Spring
This is the slide deck for my workshop "Bootiful REST Services with Spring". The code with all the exercises is available at https://github.com/toedter/rest-spring-workshop
Kai Toedter
January 08, 2023
Transcript
130 slides
-
Bootiful REST Services with Spring Kai Tödter 4/25/2024 1 ©
Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. -
Who am I? ▪ Principal Key Expert at Siemens Smart
Infrastructure ▪ Open-Source Lover ▪ VR Enthusiast and Gamer ▪ E-mail: [email protected] ▪ X (Twitter): twitter.com/kaitoedter ▪ Mastodon: https://mastodon.social/@kaitoedter ▪ Github: github.com/toedter 4/25/2024 2 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. -
Show Hands! 4/25/2024 © Kai Tödter, Licensed under a Creative
Commons Attribution 4.0 International License. 3 -
Spring Boot? REST? HATEOAS? HAL/HAL-FORMS? JSON:API? Security? 4/25/2024 © Kai
Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 4 -
After this workshop I’ll have an idea how to put
it all together! 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 5 -
Outline ▪ Spring Boot ▪ REST & Hypermedia ▪ Spring
Data REST ▪ Spring HATEOAS ▪ JSON:API ▪ Security 4/25/2024 6 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. -
Slides ▪ You find this slide deck at https://speakerdeck.com/toedter/bootiful- rest-services-with-spring
▪ Or just search for “speakerdeck toedter” 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 7 -
Requirements: Labs on local machine ▪ Your favorite text editor
or IDE ▪ IntelliJ IDEA, Eclipse, Sublime, … ▪ Java >= 17 (JDK) installed 4/25/2024 8 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. -
Lab 0: Check Build Environment ▪ Install the tutorial sources
▪ Clone https://github.com/toedter/spring-rest-workshop ▪ Open terminal and cd into spring-rest-workshop ▪ gradlew build ▪ Linux & Mac: ./gradlew build ▪ If the build is successful, you are ready to go! 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 9 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 10 -
Why Spring Boot? ▪ Fast way to build web applications
▪ Inspects your classpath and beans you have configured ▪ You can focus more on business features and less on infrastructure ▪ Easily deployable as microservice 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 11 -
What does Spring Boot NOT? ▪ Generate code ▪ Change
your configuration 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 12 -
HelloController @RestController public class HelloController { @RequestMapping("/") public String index()
{ return "Greetings from Spring Boot!"; } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 13 -
Application @SpringBootApplication public class Application { public static void main(String[]
args) { SpringApplication .run(Application.class, args); } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 14 -
Controller Test with MockMVC @WebMvcTest(HelloController.class) public class HelloControllerTest { @Autowired
private MockMvc mockMVC; @Test public void shouldGetGreeting() throws Exception { mockMVC.perform(MockMvcRequestBuilders.get("/") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(equalTo( HelloController.LAB1_GREETINGS_FROM_SPRING_BOOT))); } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 15 -
Integration Test with Random Port @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class
HelloControllerIntegrationTest { @Autowired private TestRestTemplate restTemplate; @Test public void shouldGetGreeting() throws Exception { ResponseEntity<String> response = restTemplate.getForEntity("/", String.class); assertThat(response.getBody(), equalTo(HelloController.LAB1_GREETINGS_FROM_SPRING_BOOT)); } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 16 -
Lab 1: Task 1 ▪ Open lab1/complete in your IDE
▪ Run Application.java ▪ Open browser with localhost:8080 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 17 -
Lab 1: Task 2 ▪ Open lab1/initial in your IDE
▪ Create a Spring Boot based web app ▪ Run it and open your browser with localhost:8080 ▪ Optional: Write some tests! ▪ Get some ideas from …/complete 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 18 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 19 REST + Hypermedia Basics -
Outline ▪ REST Basics ▪ HATEOAS ▪ Hypermedia with HAL
▪ Spring Data REST 4/25/2024 20 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. -
What is REST? ▪ Stands for Representational State Transfer ▪
Is a Software Architecture Style ▪ was introduced and defined in 2000 by Roy T. Fielding in his doctoral dissertation ▪ REST != CRUD via HTTP 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 21 -
REST Architectural Constraints ▪ Client-Server ▪ Stateless ▪ Cacheable ▪
Layered system ▪ Code on demand (optional) ▪ Uniform interface (see next slide) 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 22 -
Uniform Interface ▪ Identification of resources ▪ Manipulation of resources
through their representations ▪ Create => HTTP POST ▪ Read => HTTP GET ▪ Update => HTTP PUT, HTTP PATCH ▪ Delete => HTTP DELETE ▪ Self-descriptive messages ▪ Hypermedia as the engine of application state (HATEOAS) 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 23 -
Richardson Maturity Model Level 3: Hypermedia Controls Level 2: HTTP
Verbs Level 1: Resources Level 0: The Swamp of POX 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 24 Source: https://martinfowler.com/articles/richardsonMaturityModel.html -
HATEOAS ▪ Is for “Hypermedia As The Engine Of Application
State” ▪ Very hard to pronounce ☺ ▪ Key concept of REST 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 25 -
“With HATEOAS, a client interacts with a network application whose
application servers provide information dynamically through hypermedia. A REST client needs little to no prior knowledge about how to interact with an application or server beyond a generic understanding of hypermedia.” 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 26 Wikipedia Source: https://en.wikipedia.org/wiki/HATEOAS -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 27 Root API -
api.github.com 4/25/2024 © Kai Tödter, Licensed under a Creative Commons
Attribution 4.0 International License. 28 { "current_user_url": "https://api.github.com/user", "current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}", "authorizations_url": "https://api.github.com/authorizations", "code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}", "commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}", "emails_url": "https://api.github.com/user/emails", "emojis_url": "https://api.github.com/emojis", "events_url": "https://api.github.com/events", "feeds_url": "https://api.github.com/feeds", "followers_url": "https://api.github.com/user/followers", "following_url": "https://api.github.com/user/following{/target}", "gists_url": "https://api.github.com/gists{/gist_id}", "hub_url": "https://api.github.com/hub", "issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}", "issues_url": "https://api.github.com/issues" } -
Hypermedia APIs for Services are like Web Pages with Links
for Humans 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 29 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 30 HAL Image Source: http://wallpoper.com/images/00/26/43/92/hal-9000_00264392.jpg -
HAL ▪ Is for Hypertext Application Language ▪ Was created
by Mike Kelly ▪ Representations for both JSON and XML ▪ Very popular 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 31 -
HAL Structure 4/25/2024 © Kai Tödter, Licensed under a Creative
Commons Attribution 4.0 International License. 32 Plain old JSON Properties Links Plain old JSON Properties Links Plain old JSON Properties Links … Embedded Resources Embedded Resources Embedded Resources -
HAL Example { "id":1, "text":"hello all!", "_links": { "self": {
"href":"http://localhost:8080/chatty/api/messages/1" } }, "_embedded": { "author": { "id":"toedter_k" } } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 33 -
Spring ▪ Spring Boot ▪ Spring Data REST ▪ Spring
HATEOAS 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 34 -
Spring Data Rest: Domain @Data @Entity @NoArgsConstructor public class Movie
{ @Id private Long id; private String title; private String year; } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 35 -
Spring Data REST: Repository @RepositoryRestResource( collectionResourceRel = "movies", path =
"movies") interface MovieRepository extends CrudRepository<Movie, Long>, PagingAndSortingRepository< Movie, Long> { } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 36 -
Spring Data REST: Repository (2) @RepositoryRestResource( exported = false )
interface MovieRepository extends CrudRepository<Movie, Long>, PagingAndSortingRepository< Movie, Long> { } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 37 -
Spring Data REST: JSON Result { _links: { self: {
href: "http://localhost:8080/api/movies" } }, _embedded: { movies: [ { title: "The Shawshank Redemption", year: „1994", _links: { self: { href: "http://localhost:8080/api/movies/1" }, … 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 38 -
Spring Data REST Root API 4/25/2024 © Kai Tödter, Licensed
under a Creative Commons Attribution 4.0 International License. 39 { "_links": { "movies": { "href": "http://localhost:8080/api/movies{?page,size,sort}", "templated": true }, "directors": { "href": "http://localhost:8080/api/directors{?page,size,sort}", "templated": true }, "profile": { "href": "http://localhost:8080/api/profile" } } } -
HAL Explorer ▪ https://github.com/toedter/hal-explorer ▪ Easy integration through WebJar ▪
Supports custom request headers ▪ Supports theming and responsive design ▪ Supported by Spring Data REST 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 40 -
HAL Explorer 4/25/2024 © Kai Tödter, Licensed under a Creative
Commons Attribution 4.0 International License. 41 -
Robust Clients ▪ Start from main API ▪ Find link
relations through defined contracts ▪ Follow Links ▪ For navigation ▪ For possible “actions” => Clients are robust regarding changes in link URIs 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 42 -
Controversial Discussion ▪ Are we there yet? ▪ RESTistential Crises
▪ http://www.infoq.com/news/2014/03/rest-at- odds-with-web-apis ▪ DHH, Getting hyper about hypermedia apis ▪ https://signalvnoise.com/posts/3373-getting- hyper-about-hypermedia-apis 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 43 -
Demo 4/25/2024 © Kai Tödter, Licensed under a Creative Commons
Attribution 4.0 International License. 44 -
Lab 2: Task 1 ▪ Open lab2/complete in your IDE
▪ Run Application.java ▪ Open browser with localhost:8080/api 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 45 -
Lab 2: Task 2 ▪ Open lab2/initial in your IDE
▪ Change the movie repository ▪ Run the application and open your browser with localhost:8080/api ▪ Optional: Write some tests! ▪ Get some ideas from ../complete 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 46 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 47 Spring HATEOAS -
Spring HATEOAS ▪ Spring basic framework for REST with Hypermedia
support ▪ Supports generic Hypermedia API ▪ Build-in Support for Representations like HAL, HAL-FORMS, UBER, Collection+JSON, … ▪ Community-based media types: JSON:API, Siren ▪ https://docs.spring.io/spring-hateoas/docs/current- SNAPSHOT/reference/html/ 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 48 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 49 Links -
Links ▪ Essential for hypermedia ▪ In REST: How to
navigate to a REST resource ▪ Link semantic/name is called link relation ▪ The relation between a REST resource and the target REST resource ▪ Links are well known from HTML, like <a href="url">link text</a> 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 50 -
Links in Spring HATEOAS Link link = new Link("/my-url"); ▪
A link automatically has a self relation Link link = new Link("/my-url", "my-rel"); ▪ A link with my-rel relation 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 51 -
Link Relations ▪ Many Link relations are standardized by IANA
▪ IANA = Internet Assigned Numbers Authority ▪ https://www.iana.org/assignments/link- relations/link-relations.xhtml ▪ Examples: self, item, next, last, … ▪ Recommendation: Before creating a custom name for a link relation, look up the IANA list! 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 52 -
Links are great! ▪ For providing navigation to useful other
REST resources ▪ For providing domain knowledge to the REST clients, so that they don’t have to compute domain state on there own 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 53 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 54 Representation Models -
Representation Models ▪ REST => Representational State Transfer ▪ Manipulation
of resources through their representations ▪ Domain Model != Representation Model ▪ Spring HATEOAS provides RepresentationModel abstraction 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 55 -
Spring HATEOAS RepresentationModel ▪ RepresentationModel ▪ Root class, for REST
item resources ▪ CollectionModel ▪ For REST collection resources ▪ EntityModel ▪ Convenient wrapper for converting a domain model into a representation model ▪ PagedModel ▪ Addition to CollectionModel for paged collections 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 56 -
Domain Model Example public class Director { private Long id;
private String name; public Director(String name) { this.name = name; } … } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 57 -
Controller Example @GetMapping("/directors/{id}") public ResponseEntity<EntityModel<Director>> findOne(@PathVariable Long id) { return
repository.findById(id) .map(director -> EntityModel.of(director) .add(linkTo(methodOn(DirectorController.class) .findOne(director.getId())).withSelfRel())) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 58 -
Response in HAL Media Type { "id": 2, "name": "Frank
Darabont", "_links": { "self": { "href": "http://localhost:8080/api/directors/2" } } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 59 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 60 Affordances -
“ The affordances of the environment are what it offers
… what it provides or furnishes, either for good or ill. The verb 'to afford' is found in the dictionary, but the noun 'affordance' is not. I have made it up.” James J. Gibson The Ecological Approach to Visual Perception (1986) 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 61 -
Affordances ▪ With Affordances, possible actions on a REST resource
(or Link relation) can be described in more detail, e.g. ▪ What can you do with a REST resource? ▪ Create, Update, Delete, … ▪ Which parameters are mandatory or optional ▪ How would (internationalized) labels be displayed in a user interface ▪ … 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 62 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 63 HAL-FORMS Image Source: http://wallpoper.com/images/00/26/43/92/hal-9000_00264392.jpg Shutdown! Restart -
HAL-FORMS ▪ Designed to provide forms support for HAL ▪
Created by Mike Amundsen ▪ _templates for possible actions ▪ Spec: https://rwcbook.github.io/hal-forms/ 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 64 -
“The HAL-FORMS media type design follows many of the HAL
media type conventions. For this reason, HAL-FORMS "looks like HAL." However, it is important to keep in mind that HAL-FORMS is not the same as HAL — the two should not be thought of as interchangeable in any way.” 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 65 Mike Amundsen Source: https://rwcbook.github.io/hal-forms/ -
Rendering with HAL-FORMS "_templates": { "default": { "method": "POST", "properties":
[ { "name": "imdbId", "type": "text" }, { "name": "rank", "type": "number" }, { "name": "rating", "type": "number" }, { "name": "title", "required": true, "type": "text" }, { "name": "year", "type": "number" } ], "target": "http://localhost:8080/api/movies" } … © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 66 -
Demo 4/25/2024 © Kai Tödter, Licensed under a Creative Commons
Attribution 4.0 International License. 67 -
Lab 3: Task 1 ▪ Open lab3/complete in your IDE
▪ Run Application.java ▪ Open browser with localhost:8080/api 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 68 -
Lab 3: Task 2 ▪ Open lab3/initial in your IDE
▪ Edit MovieController.java ▪ Implement the method findAll ▪ Add an affordance to create a movie ▪ If time, implement method findOne ▪ Run the application and open your browser with localhost:8080/api 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 69 -
JSON:API for Spring HATEOAS ▪ Open-Source Project ▪ Apache 2
License ▪ https://github.com/toedter/spring-hateoas- jsonapi ▪ Reference Documentation ▪ API Documentation 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 71 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 72 -
Project Dependencies Gradle: implementation 'com.toedter:spring-hateoas-jsonapi:2.0.1’ Maven: <dependency> <groupId>com.toedter</groupId> <artifactId>spring-hateoas-jsonapi</artifactId> <version>2.0.1</version>
</dependency> 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 73 -
Domain Model Example public class Director { private Long id;
private String name; public Director(String name) { this.name = name; } … } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 74 -
Response in JSON:API media type { "data": { "id": "2",
"type": "directors", "attributes": { "name": "Frank Darabont" } }, "links": { "self": "http://localhost:8080/api/directors/2" } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 75 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 76 Annotations -
Annotations ▪ @JsonApiId to mark a JSON:API id ▪ @JsonApiType
to mark a JSON:API type ▪ @JsonApiTypeForClass to mark a class to provide a JSON:API type ▪ @JsonApiRelationships to mark a JSON:API relationship, only used for deserialization ▪ @JsonApiMeta to serialize/deserialize properties to JSON:API meta 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 77 -
Example with Annotations public class Movie { @Id private String
myId; @JsonApiType private String myType; @JsonApiMeta private String myMeta; private String title; } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 78 -
Annotations Example (2) EntityModel.of( new Movie("1", "MOVIE", "metaValue", "Star Wars"));
will be rendered as { "data": { "id": "1", "type": "MOVIE", "attributes": { "title": "Star Wars" }, "meta": { "myMeta": "metaValue" } } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 79 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 80 Builder -
JsonApiBuilder Movie movie = new Movie("1", "Star Wars"); final RepresentationModel<?>
jsonApiModel = jsonApiModel() .model(movie) .build(); 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 81 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 82 Relationships -
Relationships ▪ In JSON:API, relationships between REST resources are made
explicit, using the relationship object ▪ Relationships can be to-one or to-many ▪ Relationships must contain at least one of: ▪ links: a links object containing at least one of the following: ▪ self: a link for the relationship itself ▪ related: a related resource link ▪ data: resource linkage with id and type ▪ meta: meta object that contains non-standard meta- information about the relationship 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 83 -
Build Relationships Movie movie = new Movie("1", "Star Wars"); Director
director = new Director("1", "George Lucas"); final RepresentationModel<?> jsonApiModel = jsonApiModel() .model(movie) .relationship("directors", director) .build(); 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 84 -
Relationsship Example { "data": { "id": "1", "type": "movies", "attributes":
{ "title": "Star Wars" }, "relationships": { "directors": { "data": { "id": "1", "type": "directors" } } } } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 85 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 86 Inclusion -
Inclusion of Related Resources ▪ With included, you can include
the content of related recourses in the compound document ▪ The JsonApiBuilder supports adding ▪ A single included resource ▪ A collection of included resources ▪ The builder assures that included resources with same id and type appear only ONCE 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 87 -
Inclusion Example for (Movie movie : pagedResult.getContent()) { jsonApiModelBuilder.included(movie.getDirectors()); }
"included": [ { "id": "1", "type": "directors", "attributes": { "name": "Lana Wachowski" } }, … 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 88 -
Sparse Fieldsets Convenient way to specify which ▪ Attributes of
Resources ▪ Relationships (by name) ▪ Attributes of included Relationships will be included in the JSON response 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 89 -
Controller for Sparse Fieldset In a REST controller, a method
with HTTP-mapping could provide an optional request attribute for each sparse fieldset @GetMapping("/movies") public ResponseEntity<RepresentationModel<?>> findAll( @RequestParam(value = "included", required = false) String[] included, @RequestParam(value = "fields[movies]", required = false) String[] fieldsMovies) { 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 90 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 91 Sparse Fieldsets Demo -
Meta ▪ JSON:API Meta can be added using the builder
or by using the @JsonApiMeta annotation ▪ Paging information Meta can be added automatically => Use PagedModel 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 92 -
Pagination Example … "links": { "self": "http://localhost/movies", "first": "http://localhost/movies?page[number]=0&page[size]=2", "prev":
"http://localhost/movies?page[number]=0&page[size]=2", "next": "http://localhost/movies?page[number]=2&page[size]=2", "last": "http://localhost/movies?page[number]=49&page[size]=2" }, "meta": { "page": { "number": 1, "size": 2, "totalPages": 50, "totalElements": 100 } } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 93 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 94 Configuration -
Configuration (1) You can configure ▪ If the JSON:API version
should be rendered automatically, the default is false. ▪ If JSON:API types should be rendered as pluralized or non pluralized class names. ▪ The default is pluralized ▪ If JSON:API types should be rendered as lower cased or original class names. ▪ The default is lower cased ▪ If page information of a PagedModel should be rendered automatically as JSON:API meta object. ▪ The default is true 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 95 -
Configuration (2) You can configure ▪ If a specific Java
class should be rendered with a specific JSON:API type. ▪ A lambda expression to add additional configuration to the Jackson ObjectMapper used for serialization. ▪ Experimental: Render Spring HATEOAS affordances as JSON:API link meta. 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 96 -
Configuration Example @Bean JsonApiConfiguration jsonApiConfiguration() { return new JsonApiConfiguration() .withJsonApiVersionRendered(true)
.withPluralizedTypeRendered(false) .withLowerCasedTypeRendered(false) .withTypeForClass(MyMovie.class, "my-movies") .withObjectMapperCustomizer( objectMapper -> objectMapper.configure( SerializationFeature .WRITE_DATES_AS_TIMESTAMPS, true)); } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 97 -
Error Handling To create JSON:API compliant error messages, you can
use JsonApiErrors and JsonApiError return ResponseEntity.badRequest().body( JsonApiErrors.create().withError( JsonApiError.create() .withAboutLink("http://movie-db.com/problem") .withTitle("Movie-based problem") .withStatus(HttpStatus.BAD_REQUEST.toString()) .withDetail("This is a test case"))); 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 98 -
Error Example { "errors": [ { "links": { "about": "http://movie-db.com/problem"
}, "status": "400 BAD_REQUEST", "title": "Movie-based problem", "detail": "This is a test case" } ] } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 99 -
Conclusion With JSON:API for Spring HATEOAS, it is very easy
to support JSON:API (serialization + deserialization) out of the box. With the builder, special JSON:API features like relationships and sparse fieldsets are supported as well. 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 100 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 101 Affordances -
Spring HATEOAS Affordances Example © Kai Tödter, Licensed under a
Creative Commons Attribution 4.0 International License. 102 final Affordance newMovieAffordance = afford(methodOn(MovieController.class).newMovie(null)); Link selfLink = linkTo(MovieController.class).slash("movies" + uriParams + "page[number]=" + pagedResult.getNumber() + "&page[size]=" + pagedResult.getSize()) .withSelfRel() .andAffordance(newMovieAffordance); -
Rendered Affordance (1) 4/25/2024 © Kai Tödter, Licensed under a
Creative Commons Attribution 4.0 International License. 103 "links": { "self": { "href": "http://localhost:8080/api/movies?page[number]=0... "meta": { "affordances": [ { "name": "newMovie", "link": { "rel": "newMovie", "href": "http://localhost:8080/api/movies" }, "httpMethod": "POST", ... -
Rendered Affordance (2) 4/25/2024 © Kai Tödter, Licensed under a
Creative Commons Attribution 4.0 International License. 104 "inputProperties": [ { "name": "imdbId", "type": "text" }, … { "name": "title", "type": "text", "required": true }, … ] -
Lab 4: Task 1 ▪ Open lab4/complete in your IDE
▪ Run Application.java ▪ Open browser with localhost:8080/api/movies 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 105 -
Lab 4: Task 2 ▪ Open lab4/initial in your IDE
▪ Edit Application.java ▪ Add JSON:API configuration ▪ Edit MovieController.java ▪ Implement the method findAll ▪ Add an affordance to create a movie ▪ Run the application and open your browser with localhost:8080/api 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 106 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 107 Security -
OAuth 2.1 ▪ Replaces and obsoletes OAuth 2.0 ▪ Defines
4 roles: ▪ Resource Owner (RO) ▪ Resource Server (RS) ▪ Client ▪ Authorization Server (AS) ▪ Current Draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 108 -
OAuth2 Roles Resource Owner: An entity capable of granting access
to a protected resource. When the resource owner is a person, it is referred to as an end- user. Resource Server: The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens. The resource server is often accessible via an API. Client: An application making protected resource requests on behalf of the resource owner and with its authorization. Authorization Server: The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization. 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 109 -
OAuth2 Flows 4/25/2024 © Kai Tödter, Licensed under a Creative
Commons Attribution 4.0 International License. 110 Client Resource Server Authorization Server Authorization Grant Access Token Access Token Protected Resource -
Client Credential Flow ▪ Resource Owner provides client with ▪
Client Id ▪ Client Secret ▪ URL to get the access token ▪ Optional: Audience 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 111 -
Bearer Token ▪ A Bearer Token is a security token
with the property that any party in possession of the token (a "bearer") can use the token in any way that any other party in possession of it can. Using a Bearer Token does not require a bearer to prove possession of cryptographic key material (proof-of-possession). ▪ We are using the JSON Web Token (JWT) format ▪ See https://datatracker.ietf.org/doc/html/rfc7519 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 112 -
JWT Example 4/25/2024 © Kai Tödter, Licensed under a Creative
Commons Attribution 4.0 International License. 113 { "sub": "movie-client", "aud": "movies-audience", "nbf": 1710065429, "scope": [ "movies.read" ], "iss": "http://localhost:9000", "exp": 1710065729, "iat": 1710065429, "jti": "10c15243-210e-4fa0-b39f-07d411472030" } -
JWT properties ▪ sub: subject ▪ Whom the token refers
to ▪ aud: audience ▪ Who or what the token is intended for ▪ nvb: not valid before ▪ Seconds since Unix Epoch ▪ iss: issuer ▪ Who created and signed this token ▪ exp: expiration time ▪ Seconds since Unix Epoch ▪ iat: issued at ▪ Seconds since Unix Epoch ▪ jti: JWT Id ▪ Unique identifier for this token 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 114 -
JWT Transport ▪ JWT is (usually) not encrypted ▪ JWT
is always signed with private key owned by issuer (Authorization Server) ▪ Authorization Server provides public key to verify the signature ▪ Use https://jwt.io to decode JWTs 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 115 -
Spring Authorization Server ▪ https://spring.io/projects/spring-authorization-server ▪ Framework that provides implementations
of the OAuth 2.1 and OpenID Connect 1.0 specifications and other related specifications ▪ It is built on top of Spring Security 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 116 -
Spring Authorization Server Config 4/25/2024 © Kai Tödter, Licensed under
a Creative Commons Attribution 4.0 International License. 117 spring: security: user: name: user password: password oauth2: authorizationserver: client: movie-client: registration: client-id: "movie-client" client-secret: "{noop}secret" client-authentication-methods: - "client_secret_post" authorization-grant-types: - "client_credentials" scopes: - "openid" - "profile" - "movies.read" require-authorization-consent: true -
Client Credential Call ▪ POST to http://localhost:9000/oauth2/token ▪ x-www-form-urlencoded body
▪ client_id: movie-client ▪ client_secret: secret ▪ scope: movies.read ▪ grant_type: client_credentials 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 118 -
Client Credentials Call Result 4/25/2024 © Kai Tödter, Licensed under
a Creative Commons Attribution 4.0 International License. 119 { "access_token": "eyJraWQiO … WFlbJQNo8Q", "scope": "movies.read", "token_type": "Bearer", "expires_in": 300 } -
Important URLs ▪ OpenID configuration: ▪ http://localhost:9000/.well-known/openid-configuration ▪ Public Keys:
▪ http://localhost:9000/oauth2/jwks 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 120 -
Lab 5: Task 1 ▪ Open lab5/complete/spring.authorization- server in your
IDE ▪ Run DefaultAuthorizationServerApplication ▪ Get an access token using client credential flow ▪ You can use the Postman collection 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 121 -
Spring Security OAuth 4/25/2024 © Kai Tödter, Licensed under a
Creative Commons Attribution 4.0 International License. 122 -
Spring Security Configuration (Java) 4/25/2024 © Kai Tödter, Licensed under
a Creative Commons Attribution 4.0 International License. 123 @Configuration @EnableWebSecurity public class OAuth2ResourceServerSecurityConfiguration { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .requestMatchers(HttpMethod.GET, "/api/movies/**").hasAuthority("SCOPE_movies.read") .requestMatchers(HttpMethod.POST, "/api/movies/**").hasAuthority("SCOPE_movies.write") .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults())); return http.build(); } } -
Spring Security Config (Properties) 4/25/2024 © Kai Tödter, Licensed under
a Creative Commons Attribution 4.0 International License. 124 spring: security: oauth2: resourceserver: jwt: jwk-set-uri: http://localhost:9000/oauth2/jwks issuer-uri: http://localhost:9000 audiences: movies-audience -
Lab 5: Task 2 ▪ Start Spring Authorization Server ▪
Get an access token ▪ Open lab5/complete in your IDE ▪ Run Application.java ▪ Make a GET request to http://localhost:8080/api/movies => 401 ▪ Add access token with request header ▪ Authorization: Bearer <access token> => 200 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 125 -
Lab 5: Task 3 ▪ Open lab5/initial in your IDE
▪ Edit OAuth2ResourceServerSecurityConfiguration.java ▪ Implement method securityFilterChain ▪ Add security config to application.yml ▪ Call movies endpoint with and without access token 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 126 -
Lab 5: Task 4 (Optional) ▪ Use Keycloak as Authorization
Server ▪ Take a look at lab5/keycloak ▪ Start Keycloak in Docker container ▪ Windows: docker-run.bat ▪ Linux: docker-run.sh ▪ Repeat task 3 with Keycloak ▪ Keycloaks jwks URI is http://localhost:9000/auth/realms/movies/protocol/o penid-connect/certs 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 127 -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 128 Congratulations! -
4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution
4.0 International License. 129 Discussion -
License ▪ This work is licensed under a Creative Commons
Attribution 4.0 International License. ▪ See http://creativecommons.org/licenses/by/4.0/ 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 130