docs(examples): adds java spring boot examples (#7226)

* docs(examples): adds java spring boot examples

* add code highlighting for java and php

* Apply suggestions from code review

Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>

* update references

---------

Co-authored-by: Silvan <silvan.reusser@gmail.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
Livio Spring 2024-01-16 12:02:58 +01:00 committed by GitHub
parent 96d0291848
commit 57f40a3c50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 466 additions and 4 deletions

View File

@ -101,7 +101,7 @@ Our examples cover a range of programming languages and frameworks, so no matter
</td> </td>
<td>Java Spring Boot Web</td> <td>Java Spring Boot Web</td>
<td><a href="https://github.com/zitadel/zitadel-java" target="_blank"><i class="lab la-github"></i></a></td> <td><a href="https://github.com/zitadel/zitadel-java" target="_blank"><i class="lab la-github"></i></a></td>
<td></td> <td><a href="/examples/login/java-spring">Guide</a></td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
@ -194,7 +194,7 @@ Our examples cover a range of programming languages and frameworks, so no matter
</td> </td>
<td>Java Spring Boot API</td> <td>Java Spring Boot API</td>
<td><a href="https://github.com/zitadel/zitadel-java" target="_blank"><i class="lab la-github"></i></a></td> <td><a href="https://github.com/zitadel/zitadel-java" target="_blank"><i class="lab la-github"></i></a></td>
<td></td> <td><a href="/examples/login/java-spring">Guide</a></td>
<td></td> <td></td>
</tr> </tr>
</table> </table>

View File

@ -0,0 +1,177 @@
---
title: ZITADEL with Java Spring Boot
sidebar_label: Java Spring Boot
---
This integration guide demonstrates the recommended way to incorporate ZITADEL into your Spring Boot web application.
It explains how to enable user login in your application and how to fetch data from the user info endpoint.
By the end of this guide, your application will have login functionality and will be able to access the current user's profile.
:::info
This documentation references our [example](https://github.com/zitadel/zitadel-java) on GitHub.
You can either create your own application or directly run the example by providing the necessary arguments.
:::
## Set up application
Before we begin developing our application, we need to perform a few configuration steps in the ZITADEL Console.
You'll need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your Project, then add a new application at the top of the page.
Select the **Web** application type and continue.
![Create app in console](/img/java-spring/app-create.png)
We recommend that you use [Proof Key for Code Exchange (PKCE)](/apis/openidoauth/grant-types#proof-key-for-code-exchange) for all applications.
![Create app in console - set auth method](/img/java-spring/app-create-auth.png)
### Redirect URIs
The Redirect URIs field tells ZITADEL where it's allowed to redirect users after authentication. For development, you can set dev mode to `true` to enable insecure HTTP and redirect to a `localhost` URI.
The Post-logout redirect send the users back to a route on your application after they have logged out.
:::info
If you are following along with the [example](https://github.com/zitadel/zitadel-java), set the dev mode to `true`, the Redirect URIs to `http://localhost:18080/webapp/login/oauth2/code/zitadel` and Post redirect URI to `http://localhost:18080/webapp`.
:::
![Create app in console - set redirectURI](/img/java-spring/app-create-redirect.png)
Continue and create the application.
### Client ID
After successful creation of the app, a pop-up will appear displaying the app's client ID. Copy the client ID, as you will need it to configure your Java client.
![Create app in console - copy client_id](/img/java-spring/app-create-clientid.png)
## Spring setup
Now that you have configured your web application on the ZITADEL side, you can proceed with the integration of your Spring client.
This guide will reference the [example repository](https://github.com/zitadel/zitadel-java) and explain the necessary steps taken in there.
If your starting from scratch, you can use the Spring Initializer with the [following setup](https://start.spring.io/#!type=maven-project&language=java&platformVersion=3.2.1&packaging=jar&jvmVersion=17&dependencies=web,thymeleaf,security,oauth2-client,lombok) as a base.
### Support classes
To be able to take the most out of ZITADELs RBAC, we first need to create a GrantedAuthoritiesMapper, that will map the role claims (`urn:zitadel:iam:org:project:roles`)
into Spring Security `authiorities`, which can be used later on to determine the granted permissions.
So in your application, create a 'support/zitadel' package and in there the `ZitadelGrantedAuthoritiesMapper.java`:
```java reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/support/zitadel/ZitadelGrantedAuthoritiesMapper.java
```
The following two classes will provide you the possibility to access and use the user's token for requests to another API, e.g. the Spring Boot API example.
Directly create them in the `support` package.
```java reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/support/TokenAccessor.java
```
```java reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/support/AccessTokenInterceptor.java
```
### Application server configuration
As we have now our support classes, we can now create and configure the application server (and API client) itself.
In a new `config` package, create first the `WebClientConfig.java`, which will provide a RestTemplate using the previously created AccessTokenInterceptor:
```java reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/config/WebClientConfig.java
```
Additionally also create the `WebSecurityConfig.java` in the same package. This class will take care of the authentication, redirecting the user to the login,
mapping the claims (using the ZitadelGrantedAuthoritiesMapper) and also provide the possibility for logout:
```java reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/config/WebSecurityConfig.java
```
For the authentication (and the server in general) to work, the application needs some configuration, so please provide the following to your `application.yml` (resources folder):
```yaml reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/resources/application.yml
```
Note that both the `issuer-uri` as well as the `client-id` are only placeholders. You can either change them in here using the values provided by ZITADEL
or pass them later on as arguments when starting the application.
### Add pages to your application
To be able to serve these pages create a `templates` directory in the `resources` folder.
Now create three HTML files in the new `templates` folder and copy the content of the examples:
**index.html**
The home page will display the Userinfo from the authentication context and the granted roles / Spring security authorities.
```html reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/resources/templates/index.html
```
**fragments.html**
The navigation to switch between the home and tasks page and allows the user to logout.
```html reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/resources/templates/fragments.html
```
**tasks.html**
The tasks page allows to interact with the Spring Boot API example and display / add new tasks.
```html reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/resources/templates/tasks.html
```
**UiController**
To serve these pages and handler their actions, you finally need a `UiController.java`:
```java reference
https://github.com/zitadel/zitadel-java/blob/main/web/src/main/java/demo/web/UiController.java
```
### Start your application
In case you've created your own application and depending on your development setup you might need to build the application first:
```bash
mvn clean package -DskipTests
```
You will need to provide the `issuer-uri` (your ZITADEL domain) and the `client-id` previously created:
```bash
java \
-Dspring.security.oauth2.client.provider.zitadel.issuer-uri=<see configuration above> \
-Dspring.security.oauth2.client.registration.zitadel.client-id=<see configuration above> \
-jar web/target/web-0.0.2-SNAPSHOT.jar
```
This could look like:
```bash
java \
-Dspring.security.oauth2.client.provider.zitadel.issuer-uri=https://my-domain.zitadel.cloud \
-Dspring.security.oauth2.client.registration.zitadel.client-id=243861220627644836@example \
-jar web/target/web-0.0.2-SNAPSHOT.jar
```
If you then visit on <http://localhost:18080/webapp> you should directly be redirected to your ZITADEL instance.
After login with your existing user you will be presented the profile page:
![Profile Page](/img/java-spring/app-profile.png)
## Completion
Congratulations! You have successfully integrated your Spring Boot web application with ZITADEL!
If you get stuck, consider checking out our [example](https://github.com/zitadel/zitadel-java) application.
This application includes all the functionalities mentioned in this quickstart.
You can directly start it with your own configuration. If you face issues, contact us or raise an issue on [GitHub](https://github.com/zitadel/zitadel-java/issues).

View File

@ -0,0 +1,283 @@
---
title: ZITADEL with Java Spring Boot
sidebar_label: Java Spring Boot
---
This integration guide shows you how to integrate **ZITADEL** into your Java Spring Boot API. It demonstrates how to secure your API using
OAuth 2 Token Introspection.
At the end of the guide you should have an API with a protected endpoint.
:::info
This documentation references our [example](https://github.com/zitadel/zitadel-java) on GitHub.
You can either create your own application or directly run the example by providing the necessary arguments.
:::
## Set up application
Before we begin developing our API, we need to perform a few configuration steps in the ZITADEL Console.
You'll need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your Project, then add a new application at the top of the page.
Select the **API** application type and continue.
![Create app in console](/img/java-spring/api-create.png)
Select Basic Auth for authenticating at the Introspection Endpoint.
![Create app in console](/img/java-spring/api-create-auth.png)
After successful creation of the app, a pop-up will appear displaying the app's client ID. Copy the client ID and secret, as you will need it to configure your Java client.
![Create api key in console](/img/java-spring/api-create-clientid-secret.png)
## Spring Setup
Now that you have configured your web application on the ZITADEL side, you can proceed with the integration of your Spring client.
This guide will reference the [example repository](https://github.com/zitadel/zitadel-java) and explain the necessary steps taken in there.
If your starting from scratch, you can use the Spring Initializer with the [following setup](https://start.spring.io/#!type=maven-project&language=java&platformVersion=3.2.1&packaging=jar&jvmVersion=17&dependencies=web,lombok,oauth2-resource-server) as a base.
### Support class
To be able to take the most out of ZITADELs RBAC, we first need to create a CustomAuthorityOpaqueTokenIntrospector, that will
customize the introspection behaviour and map the role claims (`urn:zitadel:iam:org:project:roles`)
into Spring Security `authiorities`, which can be used later on to determine the granted permissions.
So in your application, create a `support/zitadel` package and in there the `CustomAuthorityOpaqueTokenIntrospector.java`:
```java reference
https://github.com/zitadel/zitadel-java/blob/main/api/src/main/java/demo/app/support/zitadel/CustomAuthorityOpaqueTokenIntrospector.java
```
### Application server configuration
As we have now our support class, we can now create and configure the application server itself.
In a new `config` package, create the `WebSecurityConfig.java`.
This class will take care of the authorization by require the calls on `/api/tasks` to be authorized. Any other endpoint will be public by default.
It will also use the just created CustomAuthorityOpaqueTokenIntrospector for the introspection call:
```java reference
https://github.com/zitadel/zitadel-java/blob/main/api/src/main/java/demo/app/config/WebSecurityConfig.java
```
For the authorization (and the server in general) to work, the application needs some configuration, so please provide the following to your `application.yml` (resources folder):
```yaml reference
https://github.com/zitadel/zitadel-java/blob/main/api/src/main/resources/application.yml
```
Note that the `introspection-uri`, `client-id` and `client-secret` are only placeholders. You can either change them in here using the values provided by ZITADEL
or pass them later on as arguments when starting the application.
### Create example API
Create a `api` package with a `ExampleController.java` file with the content below. This will create an API with three endpoints / methods:
- `/api/healthz`: can be called by anyone and always returns `OK`
- `/api/tasks (GET)`: requires authorization and returns the available tasks
- `/api/tasks (POST)`: requires authorization with granted `admin` role and adds the task to the list
If authorization is required, the token must not be expired and the API has to be part of the audience (either client_id or project_id).
For tests we will use a Personal Access Token or the [Java Spring web example](../login/java-spring).
```java reference
https://github.com/zitadel/zitadel-java/blob/main/api/src/main/java/demo/app/api/ExampleController.java
```
## Test API
In case you've created your own application and depending on your development setup you might need to build the application first:
```bash
mvn clean package -DskipTests
```
You will need to provide the `introspection-uri` (your ZITADEL domain> /oauth/v2/introspect), the `client-id` and `client-secret` previously created:
```bash
java \
-Dspring.security.oauth2.resourceserver.opaquetoken.introspection-uri=<see configuration above> \
-Dspring.security.oauth2.resourceserver.opaquetoken.client-id=<see configuration above> \
-Dspring.security.oauth2.resourceserver.opaquetoken.client-secret=<see configuration above> \
-jar api/target/api-0.0.2-SNAPSHOT.jar
```
This could look like:
```bash
java \
-Dspring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://my-domain.zitadel.cloud/oauth/v2/introspect \
-Dspring.security.oauth2.resourceserver.opaquetoken.client-id=243861220627644836@example \
-Dspring.security.oauth2.resourceserver.opaquetoken.client-secret=WJKLF3kfPOi3optkg9vi3jmfjv8oj32nfiäohj!FSC09RWUSR \
-jar web/target/web-0.0.2-SNAPSHOT.jar
```
### Public endpoint
Now you can call the API by browser or curl. Try the healthz endpoint first:
```bash
curl -i http://localhost:18090/api/healthz
```
it should return something like:
```
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Mon, 15 Jan 2024 09:07:21 GMT
OK
```
### Task list
and the task list endpoint:
```bash
curl -i http://localhost:18090/api/tasks
```
it will return:
```
HTTP/1.1 401
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
WWW-Authenticate: Bearer
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Mon, 15 Jan 2024 09:07:55 GMT
```
Get a valid access_token for the API. You can either achieve this by getting an access token with the project_id in the audience
(e.g. by using the [Spring Boot web example](../login/java-spring)) use a PAT of a service account.
If you provide a valid Bearer Token:
```bash
curl -i -H "Authorization: Bearer ${token}" http://localhost:18090/api/tasks
```
it will return an empty list:
```
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 15 Jan 2024 09:15:10 GMT
[]
```
### Try to add a new task
Let's see what happens if you call the tasks endpoint:
```bash
curl -i -X POST -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" --data 'my new task' http://localhost:18090/api/tasks
```
it will complain with a permission denied (missing `admin` role):
```
HTTP/1.1 403
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
WWW-Authenticate: Bearer error="insufficient_scope", error_description="The request requires higher privileges than provided by the access token.", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Mon, 15 Jan 2024 09:24:39 GMT
```
### Add admin role
So let's create the role and grant it to the user. To do so, go to your project in ZITADEL Console
and create the role by selecting `Roles` in the navigation and then clicking on the `New Role` button.
Finally, create the role as shown below:
![Create project role in console](/img/java-spring/api-project-role.png)
After you have created the role, let's grant it the user, who requested the tasks.
Click on `Authorization` in the navigation and create a new one by selecting the user and the `admin` role.
After successful creation, it should look like:
![Created authorization in console](/img/java-spring/api-project-auth.png)
So you should now be able to add a new task:
```bash
curl -i -X POST -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" --data 'my new task' http://localhost:18090/api/tasks
```
which will report back the successful addition:
```
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Content-Length: 10
Date: Mon, 15 Jan 2024 09:26:11 GMT
task added
```
Let's now retrieve the task list again:
```bash
curl -i -H "Authorization: Bearer ${token}" http://localhost:18090/api/tasks
```
As you can see your new task is listed:
```
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 15 Jan 2024 09:26:48 GMT
["my new task"]
```

View File

@ -178,7 +178,7 @@ module.exports = {
selector: "div#", selector: "div#",
}, },
prism: { prism: {
additionalLanguages: ["csharp", "dart", "groovy", "regex"], additionalLanguages: ["csharp", "dart", "groovy", "regex", "java", "php"],
}, },
colorMode: { colorMode: {
defaultMode: "dark", defaultMode: "dark",

View File

@ -16,7 +16,8 @@ module.exports = {
"examples/login/flutter", "examples/login/flutter",
"examples/login/nextjs", "examples/login/nextjs",
"examples/login/go", "examples/login/go",
"examples/login/symfony" "examples/login/symfony",
"examples/login/java-spring",
], ],
collapsed: true, collapsed: true,
}, },
@ -28,6 +29,7 @@ module.exports = {
"examples/secure-api/python-flask", "examples/secure-api/python-flask",
"examples/secure-api/dot-net", "examples/secure-api/dot-net",
"examples/secure-api/nodejs-nestjs", "examples/secure-api/nodejs-nestjs",
"examples/secure-api/java-spring",
], ],
collapsed: true, collapsed: true,
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB