{"id":19464214,"url":"https://github.com/hpgrahsl/flightswebapi","last_synced_at":"2026-04-24T20:04:42.504Z","repository":{"id":152889348,"uuid":"77223843","full_name":"hpgrahsl/FlightsWebAPI","owner":"hpgrahsl","description":"Sample Project for my Java Magazin (04/2017) Article together with Manfred Steyer","archived":false,"fork":false,"pushed_at":"2017-03-31T21:07:15.000Z","size":22,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-30T21:08:41.050Z","etag":null,"topics":["keycloak","oauth2","oidc","security","spring-boot","webapi"],"latest_commit_sha":null,"homepage":"https://jaxenter.de/ausgaben/java-magazin-4-17","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hpgrahsl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-12-23T12:23:37.000Z","updated_at":"2017-11-24T07:00:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"8d3271cb-40b7-43f4-972b-a43cc7865fb9","html_url":"https://github.com/hpgrahsl/FlightsWebAPI","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hpgrahsl/FlightsWebAPI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpgrahsl%2FFlightsWebAPI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpgrahsl%2FFlightsWebAPI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpgrahsl%2FFlightsWebAPI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpgrahsl%2FFlightsWebAPI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hpgrahsl","download_url":"https://codeload.github.com/hpgrahsl/FlightsWebAPI/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpgrahsl%2FFlightsWebAPI/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32238758,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T13:21:15.438Z","status":"ssl_error","status_checked_at":"2026-04-24T13:21:15.005Z","response_time":64,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["keycloak","oauth2","oidc","security","spring-boot","webapi"],"created_at":"2024-11-10T18:13:51.472Z","updated_at":"2026-04-24T20:04:42.498Z","avatar_url":"https://github.com/hpgrahsl.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FlightsWebAPI\nDieses Repository beinhaltet das beispielhafte WebAPI-Projekt für meinen gemeinsamen [Java Magazin Artikel 04/2017](https://jaxenter.de/ausgaben/java-magazin-4-17) mit [Manfred Steyer](https://www.softwarearchitekt.at/) und zeigt, wie mittels OIDC/OAuth2 und auf Basis der Identity \u0026 Access Management Lösung [Keycloak](http://www.keycloak.org/) die Absicherung einer WebAPI erfolgen kann.\n\n#### Spring Boot WebAPI \nAusgangspunkt für die Absicherung des Backends mittels OIDC/Oauth2 stellt eine sehr simpel gestrickte, auf [Spring Boot](https://projects.spring.io/spring-boot/) basierende WebAPI dar, welche es konsumierenden Clients ermöglichen soll, nach Flügen zu suchen. Über Maven werden zunächst die folgenden Abhängigkeiten im *pom.xml* konfiguriert:\n\n```xml\n    \u003cparent\u003e\n            \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n            \u003cartifactId\u003espring-boot-starter-parent\u003c/artifactId\u003e\n            \u003cversion\u003e1.5.1.RELEASE\u003c/version\u003e\n    \u003c/parent\u003e\n\n    \u003cdependencies\u003e\n        \u003cdependency\u003e\n            \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n            \u003cartifactId\u003espring-boot-starter-web\u003c/artifactId\u003e\n        \u003c/dependency\u003e\n        \u003cdependency\u003e\n            \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n            \u003cartifactId\u003espring-boot-starter-data-jpa\u003c/artifactId\u003e\n        \u003c/dependency\u003e\n        \u003cdependency\u003e\n            \u003cgroupId\u003ecom.h2database\u003c/groupId\u003e\n            \u003cartifactId\u003eh2\u003c/artifactId\u003e\n        \u003c/dependency\u003e\n    \u003c/dependencies\u003e\n```\n\n\nDas Domänenmodell der WebAPI besteht lediglich aus einer einzigen Entitätsklasse namens *Flight*, welche Attribute von Flügen kapselt. Zu Demostrationszwecken und weil es sich der Angular-Client so erwartet, werden die beiden String Attribute origin sowie destination im Rahmen der JSON Serialisierung mittels *@JsonProperty* Annotationen auf die Key-Namen from bzw. to gemappt. Ebenso wird für den in Java 8 eingeführten LocalDateTime Typ ein vom Client präferiertes Datumsformat angegeben.\n\n```java\n@Entity\npublic class Flight {\n\n    @Id @GeneratedValue(strategy = GenerationType.AUTO)\n    private Long id;\n\n    @JsonProperty(\"from\")\n    @NotNull\n    private String origin;\n\n    @JsonProperty(\"to\")\n    @NotNull\n    private String destination;\n\n    @JsonFormat(shape=JsonFormat.Shape.STRING,\n            pattern=\"yyyy-MM-dd'T'HH:mm:ss.SSS\")\n    @NotNull\n    private LocalDateTime date;\n\n    //Konstruktor, Getter, toString Methoden entfernt\n\n}\n```\nFür die korrekte Unterstützung der Java 8 spezifischen Time-API Typen werden entsprechende Maven Abhängigkeiten für den Serializer (hier Jackson FasterXML) konfiguriert:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.fasterxml.jackson.module\u003c/groupId\u003e\n    \u003cartifactId\u003ejackson-module-parameter-names\u003c/artifactId\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.fasterxml.jackson.datatype\u003c/groupId\u003e\n    \u003cartifactId\u003ejackson-datatype-jdk8\u003c/artifactId\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.fasterxml.jackson.datatype\u003c/groupId\u003e\n    \u003cartifactId\u003ejackson-datatype-jsr310\u003c/artifactId\u003e\n\u003c/dependency\u003e\n```\n\nAls Repository für die *Flight* Entitäten fungiert ein [Spring Data](http://projects.spring.io/spring-data/) CrudRepository. Wir deklarieren dafür lediglich ein paar verschiedene *find**-Methoden, um Flüge basierend auf unterschiedlichen Suchangaben finden zu können – Spring Data kümmert sich um die restlichen Details.\n\n```java\npublic interface FlightRepository extends CrudRepository\u003cFlight, Long\u003e {\n    @Override\n    List\u003cFlight\u003e findAll();\n    List\u003cFlight\u003e findByOrigin(String origin);\n    List\u003cFlight\u003e findByDestination(String destination);\n    List\u003cFlight\u003e findByOriginAndDestination(String origin, String destination);\n}\n```\n\nNachdem sich in den Maven Abhängigenkeiten des Projekts bereits Spring Boot Starter Data JPA sowie [H2](http://www.h2database.com/) als Datenbank befinden, ist die einfache in-memory Persistenz für *Flight* Entitäten damit bereits verwendbar. Was noch fehlt sind ein paar Demodaten für die WebAPI, welche je nach Präferenz sehr schnell auf zwei verschiedene Arten generiert werden können. Eine Möglichkeit ist es, per Konvention ein SQL-Skript mit INSERT Befehlen in eine Datei namens *data.sql* unter *src/main/resources/* abzulegen. Wer sich mehr Flexibilität wünscht, kann sich im Code eine Liste mit Flight Entitäten generieren und diese mittels eines *CommandLineRunner Beans* im Flight Repository ablegen. Letztere Variante findet sich im Code Snippet ganz unten.\n\nClient-Anwendungen benötigen entsprechende HTTP Endpunkte, um die Flights WebAPI zu konsumieren. Diese werden über einen Spring Controller, genauer gesagt *@RestController* bereitgestellt, welcher das *Flight* Repository verwendet, um Suchanfragen zu Flügen beantworten zu können. Damit die Client-Anwendung unabhängig von einem anderen Host/Port ausgeliefert werden kann, muss mit *@CrossOrigin* die nötige [CORS](https://www.w3.org/TR/cors/) Einstellung erfolgen. Mit * als Wildcard werden Anfragen von beliebigen Client Domains erlaubt. In realen Projekten sollte dies möglichst restriktiv auf Host/Port Mappings beschränkt sein, von denen Client-Anfragen gewährt werden dürfen.\n\n```java\n@RestController\n@RequestMapping(\"api/flight\")\n@CrossOrigin(origins = \"*\")\npublic class FlightsController {\n\n    @Autowired\n    FlightRepository flightRepo;\n\n    @RequestMapping(method = RequestMethod.GET)\n    public List\u003cFlight\u003e getAllFlights() {\n        return flightRepo.findAll();\n    }\n\n    @RequestMapping(value = \"{id}\", method = RequestMethod.GET)\n    public Flight getFlightById(@PathVariable(\"id\") Long id) {\n        //Methoden Code entfernt\n    }\n\n    @RequestMapping(params = {\"from\"}, method = RequestMethod.GET)\n    public List\u003cFlight\u003e getFlightsByOrigin(@RequestParam(value = \"from\", required = false, defaultValue = \"\") String from) {\n        //Methoden Code entfernt\n    }\n\n    //weitere HTTP GET-Methoden entfernt\n}\n```\n\nDie Autoren verzichten der Einfachheit halber bewußt auf einen Service Layer, als auch auf ein explizites Entity \u003c-\u003e DTO Mapping. Beides wären nützliche Erweiterungen im Rahmen von realen WebAPI Projekten.\n\nSchließlich zeigt das folgende Snippet noch die startbare Hauptklasse das Backends und damit den finalen Teil der Spring Boot Flights WebAPI. Zu diesem Zeitpunkt gibt es noch keinerlei Absicherungmaßnahmen, weshalb das Backend von beliebigen Clients verwendbar ist.\n\n```java\n@SpringBootApplication\npublic class FlightsApp {\n\nprivate static final List\u003cFlight\u003e FLIGHTS;\n\n    static {\n        FLIGHTS = new ArrayList\u003c\u003e();\n        //Code zum Hinzufügen von Flight Entitäten entfernt\n    }\n\n    @Bean\n    CommandLineRunner init(FlightRepository flightRepo) {\n        return (args) -\u003e FLIGHTS.forEach(flightRepo::save);\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(FlightsApp.class, args);\n    }\n\n}\n```\n#### Absicherung mittels Keycloak Adapter\nFür eine Bearer Token-basierte Absicherung der HTTP/S-Endpunkte des @RestController mittels Keycloak werden zwei Maven Dependencies eingefügt.\n\n```xml\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.keycloak\u003c/groupId\u003e\n        \u003cartifactId\u003ekeycloak-spring-boot-adapter\u003c/artifactId\u003e\n        \u003cversion\u003e2.5.0.Final\u003c/version\u003e\n    \u003c/dependency\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.keycloak\u003c/groupId\u003e\n        \u003cartifactId\u003ekeycloak-tomcat8-adapter\u003c/artifactId\u003e\n        \u003cversion\u003e2.5.0.Final\u003c/version\u003e\n    \u003c/dependency\u003e\n```\n\nDie Konfiguration erfolgt in der *application.properties* von Spring Boot wie folgt:\n\n```properties\nkeycloak.cors = true\nkeycloak.realm = angular-spring\nkeycloak.auth-server-url = http://localhost:8080/auth\nkeycloak.bearer-only = true\nkeycloak.resource = spring-webapi\n\nkeycloak.securityConstraints[0].securityCollections[0].name = secured controller\nkeycloak.securityConstraints[0].securityCollections[0].authRoles[0] = flightapi_user\nkeycloak.securityConstraints[0].securityCollections[0].patterns[0] = /api/flight/*\n```\nDamit ist sichergestellt, dass sämtliche *@RestController* Zugriffe auf _/api/flight/*_ abgesichert sind und der aufrufende Client ein gültiges *Bearer Token* vorweisen muss, welches den *flightapi_user* Claim beinhaltet, um Zugriff zu bekommen.  \n\nWeitere Informationen sowie Details zur Konfiguration von Keycloak selbst sind näher im Java Magazin [Artikel](https://jaxenter.de/ausgaben/java-magazin-4-17) beschrieben. \n\n#### Angular SPA als Client\nEine beispielhafte Client Implementierung, welche diese WebAPI konsumiert, findet sich in Form einer Angular SPA in folgendem [GitHub Repository von Manfred Steyer](https://github.com/manfredsteyer/angular-oauth2-oidc-sample).\n\n#### Cloud VM für Demozwecke\nEine in Microsoft Azure gehostete Keycloak Instanz steht zum Experimentieren bereit und kann testweise statt einer lokalen Installation verwendet werden. Die VM läuft von 01.03.-31.03.2017. Bei Interesse zum Ausprobieren danach einfach melden...\n\n[https://hpg-keycloak.northeurope.cloudapp.azure.com/auth/](https://hpg-keycloak.northeurope.cloudapp.azure.com/auth/)\n\nZur Anmeldung mittels Angular SPA stehen die folgenden beiden User Accounts bereit:\n\nhanspeter =\u003e hp123456$#\n\nmanfred =\u003e m#$654321\n\nAuch die vorliegende und im Artikel beschriebene Spring Boot WebAPI ist dort gehostet und unter folgender URL für die Angular SPA erreichbar:\n\n[https://hpg-keycloak.northeurope.cloudapp.azure.com:8443/api/flight/](https://hpg-keycloak.northeurope.cloudapp.azure.com:8443/api/flight/)\n\n#### Certificates powered by [letsencrypt](https://letsencrypt.org)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhpgrahsl%2Fflightswebapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhpgrahsl%2Fflightswebapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhpgrahsl%2Fflightswebapi/lists"}