Slide 1

Slide 1 text

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.

Slide 2

Slide 2 text

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.

Slide 3

Slide 3 text

Show Hands! 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 3

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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.

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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.

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 10

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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 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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 19 REST + Hypermedia Basics

Slide 20

Slide 20 text

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.

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

“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

Slide 27

Slide 27 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 27 Root API

Slide 28

Slide 28 text

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" }

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

Spring Data REST: Repository @RepositoryRestResource( collectionResourceRel = "movies", path = "movies") interface MovieRepository extends CrudRepository, PagingAndSortingRepository< Movie, Long> { } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 36

Slide 37

Slide 37 text

Spring Data REST: Repository (2) @RepositoryRestResource( exported = false ) interface MovieRepository extends CrudRepository, PagingAndSortingRepository< Movie, Long> { } 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 37

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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" } } }

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

HAL Explorer 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 41

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Demo 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 44

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 47 Spring HATEOAS

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 49 Links

Slide 50

Slide 50 text

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 link text 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 50

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 54 Representation Models

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

Controller Example @GetMapping("/directors/{id}") public ResponseEntity> 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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 60 Affordances

Slide 61

Slide 61 text

“ 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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

“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/

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

Demo 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 67

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

for HATEOAS Kai Tödter

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 72

Slide 73

Slide 73 text

Project Dependencies Gradle: implementation 'com.toedter:spring-hateoas-jsonapi:2.0.1’ Maven: com.toedter spring-hateoas-jsonapi 2.0.1 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 73

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 76 Annotations

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 80 Builder

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 82 Relationships

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 86 Inclusion

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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> 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

Slide 91

Slide 91 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 91 Sparse Fieldsets Demo

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 94 Configuration

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 101 Affordances

Slide 102

Slide 102 text

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);

Slide 103

Slide 103 text

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", ...

Slide 104

Slide 104 text

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 }, … ]

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 107 Security

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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" }

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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 }

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

Spring Security OAuth 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 122

Slide 123

Slide 123 text

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(); } }

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

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 => 200 4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 125

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 128 Congratulations!

Slide 129

Slide 129 text

4/25/2024 © Kai Tödter, Licensed under a Creative Commons Attribution 4.0 International License. 129 Discussion

Slide 130

Slide 130 text

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