<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Keycloak Archive | INOTEQ GmbH</title>
	<atom:link href="https://www.inoteq.com/tag/keycloak/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.inoteq.com/tag/keycloak/</link>
	<description>IT Unternehmensberatung, Software Entwicklung, IT Infrastruktur aus Karlsruhe</description>
	<lastBuildDate>Thu, 15 Feb 2024 07:28:21 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.4.5</generator>

<image>
	<url>https://www.inoteq.com/wp-content/uploads/2019/12/cropped-Inoteq_Logo_trans_icon-1-32x32.png</url>
	<title>Keycloak Archive | INOTEQ GmbH</title>
	<link>https://www.inoteq.com/tag/keycloak/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Automatisches Front-End-Testing einer OAuth 2.0 Spring-Boot Anwendungen</title>
		<link>https://www.inoteq.com/2024/02/08/automatisches-front-end-testing-einer-oauth-2-0-spring-boot-anwendungen/</link>
					<comments>https://www.inoteq.com/2024/02/08/automatisches-front-end-testing-einer-oauth-2-0-spring-boot-anwendungen/#respond</comments>
		
		<dc:creator><![CDATA[Alexander Kusmin]]></dc:creator>
		<pubDate>Thu, 08 Feb 2024 15:09:39 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Keycloak]]></category>
		<category><![CDATA[OAuth2]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Testing]]></category>
		<guid isPermaLink="false">https://www.inoteq.com/?p=2754</guid>

					<description><![CDATA[<p>Dieser Beitrag vertieft die Thematik des automatischen Front-End-Testings einer OAuth 2.0 Spring Boot-Anwendung. Er fungiert als komplementäre Erweiterung zu unserem vorangegangenen Beitrag über Spring Boot mit OAuth 2.0 und Keycloak. Im Fokus stehen die Integration von Keycloak als zentraler OAuth 2.0 Dienst und die Rolle von Spring Boot als fundamentale Anwendungsplattform. Ein kurzer Rückblick auf [&#8230;]</p>
<p>Der Beitrag <a href="https://www.inoteq.com/2024/02/08/automatisches-front-end-testing-einer-oauth-2-0-spring-boot-anwendungen/">Automatisches Front-End-Testing einer OAuth 2.0 Spring-Boot Anwendungen</a> erschien zuerst auf <a href="https://www.inoteq.com">INOTEQ GmbH</a>.</p>
]]></description>
										<content:encoded><![CDATA[		<div data-elementor-type="wp-post" data-elementor-id="2754" class="elementor elementor-2754" data-elementor-post-type="post">
						<section class="elementor-section elementor-top-section elementor-element elementor-element-e45a7f1 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="e45a7f1" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-b443f44" data-id="b443f44" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-5ac51a7 elementor-widget elementor-widget-spacer" data-id="5ac51a7" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-d21d227 elementor-widget elementor-widget-html" data-id="d21d227" data-element_type="widget" data-widget_type="html.default">
				<div class="elementor-widget-container">
			<!-- Only for Code Snippet Styles -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/themes/prism-coy.min.css" integrity="sha256-VcuSs+n31yebPlEcehu6PvnidJ808ScFBsK8+tJKX+Q=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/components/prism-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/autoloader/prism-autoloader.min.js"></script>
<style>
    :not(pre) > code[class*=language-].inline {
        padding: 0 0.2rem;
        color: #000;
    }
</style>		</div>
				</div>
				<div class="elementor-element elementor-element-d6809f5 elementor-widget elementor-widget-theme-post-title elementor-page-title elementor-widget-heading" data-id="d6809f5" data-element_type="widget" data-widget_type="theme-post-title.default">
				<div class="elementor-widget-container">
			<h1 class="elementor-heading-title elementor-size-default">Automatisches Front-End-Testing einer OAuth 2.0 Spring-Boot Anwendungen</h1>		</div>
				</div>
				<div class="elementor-element elementor-element-f62244b elementor-widget elementor-widget-post-info" data-id="f62244b" data-element_type="widget" data-widget_type="post-info.default">
				<div class="elementor-widget-container">
					<ul class="elementor-inline-items elementor-icon-list-items elementor-post-info">
								<li class="elementor-icon-list-item elementor-repeater-item-170d69d elementor-inline-item" itemprop="datePublished">
						<a href="https://www.inoteq.com/2024/02/08/">
											<span class="elementor-icon-list-icon">
								<i aria-hidden="true" class="fas fa-calendar"></i>							</span>
									<span class="elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-date">
										Februar 8, 2024					</span>
									</a>
				</li>
				<li class="elementor-icon-list-item elementor-repeater-item-680cb58 elementor-inline-item" itemprop="author">
						<a href="https://www.inoteq.com/author/akusmin/">
											<span class="elementor-icon-list-icon">
								<i aria-hidden="true" class="far fa-user-circle"></i>							</span>
									<span class="elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-author">
										Alexander Kusmin					</span>
									</a>
				</li>
				<li class="elementor-icon-list-item elementor-repeater-item-0db5256 elementor-inline-item" itemprop="about">
										<span class="elementor-icon-list-icon">
								<i aria-hidden="true" class="fas fa-tags"></i>							</span>
									<span class="elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-terms">
										<span class="elementor-post-info__terms-list">
				<a href="https://www.inoteq.com/tag/keycloak/" class="elementor-post-info__terms-list-item">Keycloak</a>, <a href="https://www.inoteq.com/tag/oauth2/" class="elementor-post-info__terms-list-item">OAuth2</a>, <a href="https://www.inoteq.com/tag/spring/" class="elementor-post-info__terms-list-item">Spring</a>, <a href="https://www.inoteq.com/tag/testing/" class="elementor-post-info__terms-list-item">Testing</a>				</span>
					</span>
								</li>
				</ul>
				</div>
				</div>
				<div class="elementor-element elementor-element-a90db89 elementor-widget elementor-widget-text-editor" data-id="a90db89" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Dieser Beitrag vertieft die Thematik des automatischen Front-End-Testings einer OAuth 2.0 Spring Boot-Anwendung. Er fungiert als komplementäre Erweiterung zu unserem vorangegangenen Beitrag über <a href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" target="_blank" rel="noopener">Spring Boot mit OAuth 2.0 und Keycloak</a>. Im Fokus stehen die Integration von Keycloak als zentraler OAuth 2.0 Dienst und die Rolle von Spring Boot als fundamentale Anwendungsplattform.</p><p>Ein kurzer Rückblick auf die wesentlichen Konzepte von OAuth 2.0 sowie die spezifische Bedeutung von Keycloak bietet einen soliden Ausgangspunkt. In unserem vorherigen Beitrag haben wir bereits die Integration von Spring Boot und Keycloak beleuchtet, um eine sichere OAuth 2.0-Authentifizierung zu gewährleisten. Falls Sie diesen Beitrag bisher nicht verfolgt haben, empfiehlt es sich, einen Blick darauf zu werfen, da der vorliegende Beitrag nahtlos auf den dort behandelten Grundlagen aufbaut.</p><p>Die Analyse konzentriert sich auf fortgeschrittene Aspekte des automatischen Front-End-Testings. Insbesondere wird die Erstellung einer GitLab Pipeline für automatische Tests auf bewährten Prinzipien von Keycloak und Spring Boot basierend erkundet. Das Ziel ist es, Ihnen Einblicke in die strategischen Schritte zu geben, um eine effiziente und zuverlässige Testumgebung zu schaffen, die sich nahtlos in Ihre Entwicklungsprozesse integrieren lässt.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-8d73400 elementor-widget elementor-widget-theme-post-featured-image elementor-widget-image" data-id="8d73400" data-element_type="widget" data-widget_type="theme-post-featured-image.default">
				<div class="elementor-widget-container">
										<figure class="wp-caption">
										<img fetchpriority="high" decoding="async" width="2000" height="1200" src="https://www.inoteq.com/wp-content/uploads/2023/11/wp4.png" class="attachment-full size-full wp-image-2920" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/wp4.png 2000w, https://www.inoteq.com/wp-content/uploads/2023/11/wp4-300x180.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/wp4-1024x614.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/wp4-768x461.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/wp4-1536x922.png 1536w" sizes="(max-width: 2000px) 100vw, 2000px" />											<figcaption class="widget-image-caption wp-caption-text">KI generiertes Symbolbild: compelling, illustration, symbolizing automated pipeline testing, logo-style, clipboard with test results, pipeline</figcaption>
										</figure>
							</div>
				</div>
				<div class="elementor-element elementor-element-0abacf3 elementor-toc--minimized-on-tablet elementor-widget elementor-widget-table-of-contents" data-id="0abacf3" data-element_type="widget" data-settings="{&quot;exclude_headings_by_selector&quot;:&quot;.exclude-from-toc&quot;,&quot;headings_by_tags&quot;:[&quot;h2&quot;,&quot;h3&quot;,&quot;h4&quot;,&quot;h5&quot;,&quot;h6&quot;],&quot;marker_view&quot;:&quot;numbers&quot;,&quot;minimize_box&quot;:&quot;yes&quot;,&quot;minimized_on&quot;:&quot;tablet&quot;,&quot;hierarchical_view&quot;:&quot;yes&quot;,&quot;min_height&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;min_height_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;min_height_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}" data-widget_type="table-of-contents.default">
				<div class="elementor-widget-container">
					<div class="elementor-toc__header">
			<h4 class="elementor-toc__header-title">
				Table of Contents			</h4>
							<div class="elementor-toc__toggle-button elementor-toc__toggle-button--expand" role="button" tabindex="0" aria-controls="elementor-toc__0abacf3" aria-expanded="true" aria-label="Open table of contents"><i aria-hidden="true" class="fas fa-chevron-down"></i></div>
				<div class="elementor-toc__toggle-button elementor-toc__toggle-button--collapse" role="button" tabindex="0" aria-controls="elementor-toc__0abacf3" aria-expanded="true" aria-label="Close table of contents"><i aria-hidden="true" class="fas fa-chevron-up"></i></div>
					</div>
		<div id="elementor-toc__0abacf3" class="elementor-toc__body">
			<div class="elementor-toc__spinner-container">
				<i class="elementor-toc__spinner eicon-animation-spin eicon-loading" aria-hidden="true"></i>			</div>
		</div>
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-247900a elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="247900a" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-daa0597" data-id="daa0597" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-0d03c77 elementor-widget elementor-widget-spacer" data-id="0d03c77" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-a3ebc38 elementor-widget elementor-widget-heading" data-id="a3ebc38" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Keycloak für die Testumgebung</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-0268f17 elementor-widget elementor-widget-text-editor" data-id="0268f17" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Zum Testen der Spring Boot Anwendung ist eine Keycloak Konfiguration für die Testumgebung erforderlich. Später wird mithilfe von Testcontainers eine Keycloak-Instanz mit der gegebenen Konfiguration geladen, um die Authentifizierung in den Tests zu übernehmen. Zu diesem Zweck wird eine Import-Datei im JSON-Format benötigt. Ein einfacher Export der Konfiguration ist über die Keycloak-Benutzeroberfläche zwar möglich, allerdings werden aus Sicherheitsgründen die angelegten Benutzer nicht mit exportiert. Die Testumgebung wäre dann zwar in der Lage, sich mit dem OAuth 2.0 Server zu verbinden und normal zu starten, es stehen anschließend jedoch keine Benutzer für die Authentifizierung zur Verfügung.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-3dd3b3e elementor-widget elementor-widget-heading" data-id="3dd3b3e" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Realm-Export mit Benutzern</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-fc7fb45 elementor-widget elementor-widget-text-editor" data-id="fc7fb45" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Im Folgenden wird erklärt, wie man einen bereits vollständig konfigurierten Realm mit allen Benutzern exportiert, während der Keycloak-Server in einer Docker-Umgebung läuft. Im <a href="https://github.com/inoteq/spring-boot-keycloak-oauth2" target="_blank" rel="noopener">Repository</a> zum letzten Beitrag findet sich bereits eine aus dem vorkonfigurierten Demo-Realm exportierte <strong><i>realm.json</i></strong> Datei, die hier wieder verwendet wird.</p><p>Folgender Befehl wird im Terminal des Systems ausgeführt:</p><p><code class="language-bash inline">docker exec -it keycloak-demo bash</code></p><p>Darüber gelangen wir in das Terminal innerhalb des Containers, auf dem der Keycloak-Server läuft. Dabei wird hier der Container <em><strong>keycloak-demo</strong></em> gewählt. Innerhalb des Containers wird dann folgender Befehl ausgeführt:</p><p><code class="language-bash inline">./opt/keycloak/bin/kc.sh export --dir /tmp/export --realm demo-realm --users realm_file</code></p><p>Damit exportieren wir die Realm-Konfiguration mit allen Benutzerdaten als <strong><i>demo-realm-realm.json</i></strong> in das Verzeichnis <em><strong>/tmp/export</strong></em> innerhalb des Containers. Mit dem Befehl <code class="language-bash inline">exit</code> gelangt man wieder in das Terminal des eigenen Systems zurück. Zuletzt wird die exportierte Datei mit dem folgendem Befehl aus dem Container herauskopiert.</p><p><code class="language-bash inline">docker cp keycloak-demo:/tmp/export/demo-realm-realm.json ~/Desktop/realm.json</code></p><p>In diesem Beispiel wird es einfach auf den Desktop kopiert und gleichzeitig in <strong><i>realm.json</i></strong> umbenannt, später wird diese aber im Spring Boot-Projekt gebraucht.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-b50f9a4 elementor-widget elementor-widget-alert" data-id="b50f9a4" data-element_type="widget" data-widget_type="alert.default">
				<div class="elementor-widget-container">
					<div class="elementor-alert elementor-alert-warning" role="alert">
			<span class="elementor-alert-title">Sensible Daten</span>
							<span class="elementor-alert-description">Die exportierte Realm-Konfiguration kann sensible Daten wie Passwort-Hashes oder E-Mails enthalten. Aus diesem Grund sollte diese Variante ausschließlich für Entwicklungszwecke oder mit Testsysteme mit Testdaten verwendet werden.</span>
								</div>
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-1ac0dc1 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="1ac0dc1" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-67a783c" data-id="67a783c" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-8dc0c6a elementor-widget elementor-widget-spacer" data-id="8dc0c6a" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-8c0be78 elementor-widget elementor-widget-heading" data-id="8c0be78" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Playwright Frontend Tests</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-d36b81d elementor-widget elementor-widget-text-editor" data-id="d36b81d" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Um die Benutzeroberfläche der Anwendung zu testen, wird das Testframework <a href="https://playwright.dev/java/" target="_blank" rel="noopener">Playwright</a> verwendet.</p><p>Im Vergleich zu dem bekannteren Selenium bietet Playwright einige Vorteile, wie zum Beispiel eine bessere Performance und weniger erforderlichen Boilerplate-Code beim Schreiben der Tests. Playwright bietet eine verbesserte Browser-Automation und unterstützt mehrere Browser. Außerdem gibt es eine native Unterstützung für den Headless-Modus, bessere Möglichkeiten zur Manipulation von Webseiten und eine einfache Verwendung mit Pipelines für automatische Tests.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-60e90f4 elementor-widget elementor-widget-heading" data-id="60e90f4" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Konfiguration</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-999edee elementor-widget elementor-widget-text-editor" data-id="999edee" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Für die Verwendung von Playwright für Tests muss die <b><i>build.gradle</i></b> angepasst werden. Zunächst sollte sichergestellt werden, dass es eine Test-Task für Gradle existiert und die benötigten Abhängigkeiten für <a href="https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test" target="_blank" rel="noopener">Spring Boot Starter Tests</a> als auch <a href="https://mvnrepository.com/artifact/com.microsoft.playwright/playwright" target="_blank" rel="noopener">Playwright</a> für die Testimplementierung angegeben sind, als auch die Abhängigkeiten für die Verwendung von OAuth 2.0 selbst.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-ae00014 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="ae00014" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-1821" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1821" aria-expanded="false">build.gradle</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1821" aria-expanded="false">build.gradle</div>
					<div id="elementor-tab-content-1821" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1821" tabindex="0" hidden="false"><pre><code class="language-groovy">dependencies {
    ...
    testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.2'
    testImplementation 'com.microsoft.playwright:playwright:1.41.1'
}

...

tasks.named('test') {
    useJUnitPlatform()
}
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-971daba elementor-widget elementor-widget-text-editor" data-id="971daba" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Eine Testklasse wird erstellt und für die Spring Boot Anwendung sowie für Playwright konfiguriert. Zunächst wird Playwright so konfiguriert, dass der automatische Testprozess beobachtet werden kann, um mögliche Fehler zu sichten. Später sollte diese Option wieder ausgestellt werden, da die Tests so wesentlich schneller laufen.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-948a594 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="948a594" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-1551" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1551" aria-expanded="false">src/test/kotlin/DemoTest.kt</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1551" aria-expanded="false">src/test/kotlin/DemoTest.kt</div>
					<div id="elementor-tab-content-1551" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1551" tabindex="0" hidden="false"><pre><code class="language-kotlin">import ...

@SpringBootTest(
    classes = [DemoApplication::class],
    webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT
)
class DemoTest {
    companion object {
        // Playwright setup
        private var playwright: Playwright? = null
        private var browser: Browser? = null
        private var context: BrowserContext? = null
        private var page: Page? = null

        @JvmStatic
        @BeforeAll
        fun launchBrowser() {
            playwright = Playwright.create()
            
            browser = playwright?.chromium()?.launch(
                // Run playwright in headed mode with slow motion to see what's happening
                BrowserType.LaunchOptions().setHeadless(false).setSlowMo(100.0)
            )
        }

        @JvmStatic
        @AfterAll
        fun closeBrowser() {
            playwright?.close()
        }
    }

    @BeforeEach
    fun createContextAndPage() {
        context = browser?.newContext()
        page = context?.newPage()
    }

    @AfterEach
    fun closeContext() {
        context?.close()
    }
}
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-acca8d1 elementor-widget elementor-widget-text-editor" data-id="acca8d1" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Damit kann Playwright nun für Tests verwendet werden.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-fe028e7 elementor-widget elementor-widget-heading" data-id="fe028e7" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Beispieltests</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-c839f98 elementor-widget elementor-widget-text-editor" data-id="c839f98" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Wir halten uns bei diesem Beispiel an das Demo-Projekt aus dem letzten Beitrag und wollen die beiden Endpunkte <code class="language-plain inline">/public</code> und <code class="language-plain inline">/private</code> testen. Der <code class="language-plain inline">/public</code> Endpunkt ist immer aufrufbar, aber der <code class="language-plain inline">/private</code> Endpunkt ist erst aufrufbar, nachdem sich der Benutzer erfolgreich authentifiziert hat. Dafür müssen wir Playwright mitteilen, dass er nach dem Aufruf von <code class="language-plain inline">/private</code> auf eine Weiterleitung zum OAuth 2.0 Server warten soll, dort dann Benutzername und Passwort eingibt, bestätigt und zu guter Letzt wieder auf dem erwarteten Endpunkt <code class="language-plain inline">/private</code> landet.						</div>
				</div>
				<div class="elementor-element elementor-element-7391122 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="7391122" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-1211" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1211" aria-expanded="false">src/test/kotlin/DemoTest.kt</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1211" aria-expanded="false">src/test/kotlin/DemoTest.kt</div>
					<div id="elementor-tab-content-1211" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1211" tabindex="0" hidden="false"><pre><code class="language-kotlin">@Test
fun publicEndpointTest() {
    page?.navigate("http://localhost:8080/public")
    assertThat(page?.locator("body")).hasText("Hello from a public endpoint!")
}

@Test
fun privateEndpointTest() {
    // Unable to access private endpoint without logging in and redirects to login page
    page?.navigate("http://localhost:8080/private")

    // Login with example user 'John Doe' by filling the login form and submitting it
    page?.fill("#username", "john.doe")
    page?.fill("#password", "password")
    page?.locator("input[type=submit]")?.click()
    // Wait for redirect to previously requested private endpoint
    page?.waitForURL("http://localhost:8080/private?continue")

    // Check for expected content on private endpoint
    assertThat(page?.locator("body")).hasText("Hello from a private endpoint!")
}</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-945f459 elementor-widget elementor-widget-text-editor" data-id="945f459" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Um sicherzustellen, dass unsere Tests korrekt funktionieren, lassen wir den OAuth 2.0 Keycloak-Container im Hintergrund laufen und führen die Tests aus. Dabei wird ein neuer Chromium-Browser geöffnet, den Playwright manipuliert. Für einen kurzen Moment ist auch zu sehen, wie Playwright sich am OAuth 2.0 Server anmeldet.</p><p>Um die Auswirkungen von fehlendem OAuth 2.0 Zugriff zu zeigen, wird der Keycloak-Container gestoppt und die Tests erneut ausgeführt. Dadurch sollten die Tests fehlschlagen. Im nächsten Schritt wird der Zugriff wieder ermöglicht, indem bei den Tests ein Keycloak-Container erstellt und gestartet wird.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-1014992 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="1014992" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-7af1228" data-id="7af1228" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-c29614c elementor-widget elementor-widget-spacer" data-id="c29614c" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-8ab9cf3 elementor-widget elementor-widget-heading" data-id="8ab9cf3" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Keycloak Testcontainer</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-31071f5 elementor-widget elementor-widget-text-editor" data-id="31071f5" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Zunächst benötigen wir die richtige Abhängigkeit, um einen Keycloak-Container für Tests zu starten. Dafür gibt es den <a href="https://github.com/dasniko/testcontainers-keycloak" target="_blank" rel="noopener">Keycloak Testcontainer</a>, eine spezielle Variante des Testcontainers, der für Keycloak angepasst wurde. Dazu wird die folgende Zeile in <b><i>build.gradle</i></b> eingefügt:</p><p><code class="language-gradle inline">testImplementation 'com.github.dasniko:testcontainers-keycloak:3.2.0'</code></p><p>Wichtig ist, dass Docker auf Ihrem System läuft.</p><p>Die Testklasse wird so angepasst, dass unser gewünschter OAuth 2.0 Service wieder erreichbar ist, ohne dass wir den Container dafür erstellen und starten müssen. Dazu wird der Code wie folgt ergänzt:</p>						</div>
				</div>
				<div class="elementor-element elementor-element-99a6d9a elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="99a6d9a" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-1611" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1611" aria-expanded="false">src/test/kotlin/DemoTest.kt</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1611" aria-expanded="false">src/test/kotlin/DemoTest.kt</div>
					<div id="elementor-tab-content-1611" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1611" tabindex="0" hidden="false"><pre><code class="language-kotlin">companion object {
    // Start a Keycloak instance with an import file for the demo realm
    @JvmStatic
    private val keycloakContainer = KeycloakContainer().apply {
        withRealmImportFile("realm.json")
        portBindings = listOf("8081:8080")
        start()
    }

    // Register the Keycloak issuer URL as a dynamic property for the Spring Boot application
    @JvmStatic
    @DynamicPropertySource
    private fun registerResourceServerIssuerProperty(registry: DynamicPropertyRegistry) {
        registry.add("spring.security.oauth2.client.provider.demo-provider.issuer-uri") {
            "${keycloakContainer.authServerUrl}/realms/demo-realm"
        }
    }

    ...

    @JvmStatic
    @BeforeAll
    fun launchBrowser() {
        playwright = Playwright.create()
        browser = playwright?.chromium()?.launch() // Use headless mode
    }

   ...
}</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-b266315 elementor-widget elementor-widget-text-editor" data-id="b266315" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Es wird ein Keycloak-Container definiert, der die zuvor exportierte <b><i>realm.json</i></b> importiert. Diese Datei muss sich dazu im Verzeichnis <b><i>src/test/resources </i></b>befinden. Zudem wird vor dem Start die Portbindung definiert.</p><p>Testcontainer werden immer mit einem zufälligen Port gestartet. Die Anwendung benötigt jedoch diesen Port, um den OAuth 2.0 Dienst richtig zu konfigurieren. Für diesen Zweck wird im Code die dynamische Eigenschaft für den OAuth 2.0-Dienst angepasst, indem wir die URL des Testcontainers einschließlich des Ports vor dem verwendeten Realm angeben.</p><p>Die Tests sind jetzt unabhängig von der lokalen Umgebung ausführbar. Während des Testprozesses wird ein neuer Container in Docker erstellt und nach Abschluss der Tests wieder gelöscht.</p><p>Es empfiehlt sich, Playwright wieder headless zu benutzen, da dadurch eine wesentlich bessere Performance erreicht wird. Das ist vor allem im nächsten Schritt wichtig.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-2802df5 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="2802df5" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-8cf8bd3" data-id="8cf8bd3" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-9f0c98e elementor-widget elementor-widget-spacer" data-id="9f0c98e" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-b9d6ec9 elementor-widget elementor-widget-heading" data-id="b9d6ec9" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">GitLab Pipeline</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-49b2e44 elementor-widget elementor-widget-text-editor" data-id="49b2e44" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Das Ziel war bisher, die Tests zu automatisieren und für eine GitLab-Pipeline ausführbar zu machen. Dafür ist es wichtig, dass der GitLab-Runner Docker-Container ausführen kann.</p><p>Playwright bietet ein Image an, das für die Zwecke dieses Projekts geeignet ist. Auf der <a href="https://playwright.dev/java/docs/ci#gitlab-ci" target="_blank" rel="noopener">Playwright-Website</a> gibt es genauere Details zur kontinuierlichen Integration, einschließlich GitHub und anderen Plattformen. Auch für Testcontainers werden <a href="https://java.testcontainers.org/supported_docker_environment/continuous_integration/gitlab_ci/" target="_blank" rel="noopener">ausführliche Beispiele</a> gezeict, wie man z.B. eine GitLab Pipeline mit Testcontainers konfiguriert. In unserem Fall orientieren wir uns an der Variante, die Docker-in-Docker verwendet.</p><p>Sobald die benötigten Anpassungen vorgenommen wurden, lassen sich mittels <code class="language-bash inline">./gradlew test</code> die Tests ausführen.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-9069fba elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="9069fba" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-1511" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1511" aria-expanded="false">gitlab-ci.yml</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1511" aria-expanded="false">gitlab-ci.yml</div>
					<div id="elementor-tab-content-1511" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1511" tabindex="0" hidden="false"><pre><code class="language-yml">image: mcr.microsoft.com/playwright/java:v1.41.0-jammy

services:
  - name: docker:dind
    command: [ "--tls=false" ]

variables:
  DOCKER_HOST: "tcp://docker:2375"
  DOCKER_TLS_CERTDIR: ""
  DOCKER_DRIVER: overlay2

before_script:
  - GRADLE_USER_HOME="$(pwd)/.gradle"
  - export GRADLE_USER_HOME

stages:
  - test

tests:
  stage: test
  script:
    - ./gradlew test
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-b9bf388 elementor-widget elementor-widget-text-editor" data-id="b9bf388" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Wie man nun sehen kann, läuft die Pipeline erfolgreich durch. Allerdings ist es erwähnenswert, dass sie einige Zeit dafür benötigt. Mit einer größeren Anzahl an Tests steigt auch die Zeit, die die Pipeline benötigt, um diese auszuführen. Dabei sei gesagt, dass die meiste Zeit für die Vorbereitung der Pipeline benötigt wird, einschließlich der größeren Abhängigkeiten wie dem Playwright Browser.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-8b042b4 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="8b042b4" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-1451" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1451" aria-expanded="false">GitLab Runner</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1451" aria-expanded="false">GitLab Runner</div>
					<div id="elementor-tab-content-1451" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1451" tabindex="0" hidden="false"><pre><code class="language-log">Running with gitlab-runner 14.7.0 (98daeee0)
  on gitlab-runner-01 y7AkGoZD
Preparing the "docker" executor 00:34
...
Preparing environment 00:00
...
Getting source from Git repository 00:02
...
Executing "step_script" stage of the job script 03:52
Using docker image sha256:df9599e540016f99fbcd1d862224ae9d1cf30df17fa5d367c969ee37cc060afb for mcr.microsoft.com/playwright/java:v1.41.0-jammy with digest mcr.microsoft.com/playwright/java@sha256:390fdf6801be59dd7bc6a3fad6344ae9d795b6585d102af293b57d61076d0824 ...
$ GRADLE_USER_HOME="$(pwd)/.gradle"
$ export GRADLE_USER_HOME
$ ./gradlew test
Downloading https://services.gradle.org/distributions/gradle-8.5-bin.zip
............10%.............20%............30%.............40%.............50%............60%.............70%.............80%............90%.............100%
Welcome to Gradle 8.5!
Here are the highlights of this release:
 - Support for running on Java 21
 - Faster first use with Kotlin DSL
 - Improved error and warning messages
For more details see https://docs.gradle.org/8.5/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
&gt; Task :checkKotlinGradlePluginConfigurationErrors
&gt; Task :processResources
&gt; Task :processTestResources
&gt; Task :compileKotlin
&gt; Task :compileJava NO-SOURCE
&gt; Task :classes
&gt; Task :compileTestKotlin
&gt; Task :compileTestJava NO-SOURCE
&gt; Task :testClasses
&gt; Task :test
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 3m 50s
6 actionable tasks: 6 executed
Cleaning up project directory and file based variables 00:01
Job succeeded</code></pre></div>
							</div>
		</div>
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-01f7774 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="01f7774" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-c412524" data-id="c412524" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-9689723 elementor-widget elementor-widget-spacer" data-id="9689723" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-7fdc40d elementor-widget elementor-widget-heading" data-id="7fdc40d" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Fazit</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-d14bd30 elementor-widget elementor-widget-text-editor" data-id="d14bd30" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die vorgestellten Tests zeichnen sich durch ihre hohe Realitätsnähe aus, da sie auf einer dynamischen Keycloak-Testumgebung basieren. Dies vereinfacht die Entwicklung und trägt zur Sicherstellung einer zuverlässigen Authentifizierung bei. Jedoch ist es wichtig zu beachten, dass die GitLab Pipeline, aufgrund ihrer umfassenden Abhängigkeiten wie dem Playwright Browser, eine längere Ausführungszeit aufweisen kann. Trotz dieser potenziellen Einschränkung bieten die realitätsnahen Tests einen unschätzbaren Wert für die Entwicklung von sicheren und zuverlässigen OAuth 2.0 Spring Boot-Anwendungen.</p>
<p>Für weitere Details steht das <a href="https://github.com/inoteq/spring-boot-keycloak-oauth2-testing">GitHub Repository</a> zur Verfügung, das alle im Artikel beschriebenen Schritte und Konfigurationen enthält.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-44c973b elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="44c973b" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-5b14c43" data-id="5b14c43" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-9d09d69 elementor-widget elementor-widget-spacer" data-id="9d09d69" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-354e095 elementor-widget elementor-widget-heading" data-id="354e095" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Referenzierte Artikel</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-98051f8 elementor-grid-1 elementor-grid-tablet-1 elementor-posts--thumbnail-left elementor-hidden-tablet elementor-hidden-mobile elementor-grid-mobile-1 elementor-widget elementor-widget-posts" data-id="98051f8" data-element_type="widget" data-settings="{&quot;classic_columns&quot;:&quot;1&quot;,&quot;classic_columns_tablet&quot;:&quot;1&quot;,&quot;classic_columns_mobile&quot;:&quot;1&quot;,&quot;classic_row_gap&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:35,&quot;sizes&quot;:[]},&quot;classic_row_gap_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;classic_row_gap_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}" data-widget_type="posts.classic">
				<div class="elementor-widget-container">
					<div class="elementor-posts-container elementor-posts elementor-posts--skin-classic elementor-grid">
				<article class="elementor-post elementor-grid-item post-1744 post type-post status-publish format-standard has-post-thumbnail hentry category-blog tag-keycloak tag-oauth-2-0 tag-spring">
				<a class="elementor-post__thumbnail__link" href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" tabindex="-1" >
			<div class="elementor-post__thumbnail"><img decoding="async" width="2000" height="1200" src="https://www.inoteq.com/wp-content/uploads/2023/11/wp1.png" class="attachment-full size-full wp-image-2922" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/wp1.png 2000w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-300x180.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-1024x614.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-768x461.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-1536x922.png 1536w" sizes="(max-width: 2000px) 100vw, 2000px" /></div>
		</a>
				<div class="elementor-post__text">
				<p class="elementor-post__title">
			<a href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" >
				Integration von OAuth 2.0 in Spring Boot mit Keycloak			</a>
		</p>
				<div class="elementor-post__meta-data">
					<span class="elementor-post-author">
			Alexander Kusmin		</span>
				<span class="elementor-post-date">
			30. November 2023		</span>
				</div>
				<div class="elementor-post__excerpt">
			<p>Dieser Leitfaden beschreibt die Integration von OAuth 2.0 in eine Spring Boot Anwendung mit Keycloak. Nach einer kurzen Einführung in die Spring Boot Plattform liegt der Schwerpunkt auf der genauen Konfiguration von Keycloak als vertrauenswürdigen OAuth 2.0 Server und der Absicherung bestimmter Endpunkte in der Anwendung mit Anmeldedaten. Der Beitrag ist dabei so strukturiert, dass Entwickler Schritt für Schritt eine sichere Authentifizierung in einer Spring Boot-Anwendung implementieren können. Inhaltsverzeichnis Spring</p>
		</div>
		
		<a class="elementor-post__read-more" href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" aria-label="Read more about Integration von OAuth 2.0 in Spring Boot mit Keycloak" tabindex="-1" >
			Weiterlesen »		</a>

				</div>
				</article>
				</div>
		
				</div>
				</div>
				<div class="elementor-element elementor-element-1b24a2c elementor-grid-1 elementor-grid-tablet-1 elementor-posts--thumbnail-left elementor-hidden-desktop elementor-hidden-mobile elementor-grid-mobile-1 elementor-widget elementor-widget-posts" data-id="1b24a2c" data-element_type="widget" data-settings="{&quot;classic_columns&quot;:&quot;1&quot;,&quot;classic_columns_tablet&quot;:&quot;1&quot;,&quot;classic_columns_mobile&quot;:&quot;1&quot;,&quot;classic_row_gap&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:35,&quot;sizes&quot;:[]},&quot;classic_row_gap_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;classic_row_gap_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}" data-widget_type="posts.classic">
				<div class="elementor-widget-container">
					<div class="elementor-posts-container elementor-posts elementor-posts--skin-classic elementor-grid">
				<article class="elementor-post elementor-grid-item post-1744 post type-post status-publish format-standard has-post-thumbnail hentry category-blog tag-keycloak tag-oauth-2-0 tag-spring">
				<a class="elementor-post__thumbnail__link" href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" tabindex="-1" >
			<div class="elementor-post__thumbnail"><img decoding="async" width="2000" height="1200" src="https://www.inoteq.com/wp-content/uploads/2023/11/wp1.png" class="attachment-full size-full wp-image-2922" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/wp1.png 2000w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-300x180.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-1024x614.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-768x461.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-1536x922.png 1536w" sizes="(max-width: 2000px) 100vw, 2000px" /></div>
		</a>
				<div class="elementor-post__text">
				<p class="elementor-post__title">
			<a href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" >
				Integration von OAuth 2.0 in Spring Boot mit Keycloak			</a>
		</p>
				<div class="elementor-post__meta-data">
					<span class="elementor-post-author">
			Alexander Kusmin		</span>
				<span class="elementor-post-date">
			30. November 2023		</span>
				</div>
				<div class="elementor-post__excerpt">
			<p>Dieser Leitfaden beschreibt die Integration von OAuth 2.0 in eine Spring Boot Anwendung mit Keycloak. Nach einer kurzen Einführung in die Spring Boot Plattform liegt der Schwerpunkt auf der genauen Konfiguration von Keycloak als vertrauenswürdigen</p>
		</div>
		
		<a class="elementor-post__read-more" href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" aria-label="Read more about Integration von OAuth 2.0 in Spring Boot mit Keycloak" tabindex="-1" >
			Weiterlesen »		</a>

				</div>
				</article>
				</div>
		
				</div>
				</div>
				<div class="elementor-element elementor-element-decdf13 elementor-grid-1 elementor-grid-tablet-1 elementor-hidden-desktop elementor-hidden-tablet elementor-grid-mobile-1 elementor-posts--thumbnail-top elementor-widget elementor-widget-posts" data-id="decdf13" data-element_type="widget" data-settings="{&quot;classic_columns&quot;:&quot;1&quot;,&quot;classic_columns_tablet&quot;:&quot;1&quot;,&quot;classic_columns_mobile&quot;:&quot;1&quot;,&quot;classic_row_gap&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:35,&quot;sizes&quot;:[]},&quot;classic_row_gap_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;classic_row_gap_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}" data-widget_type="posts.classic">
				<div class="elementor-widget-container">
					<div class="elementor-posts-container elementor-posts elementor-posts--skin-classic elementor-grid">
				<article class="elementor-post elementor-grid-item post-1744 post type-post status-publish format-standard has-post-thumbnail hentry category-blog tag-keycloak tag-oauth-2-0 tag-spring">
				<a class="elementor-post__thumbnail__link" href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" tabindex="-1" >
			<div class="elementor-post__thumbnail"><img decoding="async" width="2000" height="1200" src="https://www.inoteq.com/wp-content/uploads/2023/11/wp1.png" class="attachment-full size-full wp-image-2922" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/wp1.png 2000w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-300x180.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-1024x614.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-768x461.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-1536x922.png 1536w" sizes="(max-width: 2000px) 100vw, 2000px" /></div>
		</a>
				<div class="elementor-post__text">
				<p class="elementor-post__title">
			<a href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" >
				Integration von OAuth 2.0 in Spring Boot mit Keycloak			</a>
		</p>
				<div class="elementor-post__meta-data">
					<span class="elementor-post-author">
			Alexander Kusmin		</span>
				<span class="elementor-post-date">
			30. November 2023		</span>
				</div>
				<div class="elementor-post__excerpt">
			<p>Dieser Leitfaden beschreibt die Integration von OAuth 2.0 in eine Spring Boot Anwendung mit Keycloak. Nach einer kurzen Einführung in die Spring Boot Plattform liegt der Schwerpunkt auf der genauen Konfiguration von Keycloak als vertrauenswürdigen OAuth 2.0 Server und der Absicherung bestimmter Endpunkte in der Anwendung mit Anmeldedaten. Der Beitrag ist dabei so strukturiert, dass Entwickler Schritt für Schritt eine sichere Authentifizierung in einer Spring Boot-Anwendung implementieren können. Inhaltsverzeichnis Spring Boot: Setup und Grundkonfiguration Konfiguration Die Vorbereitung der Spring Boot</p>
		</div>
		
		<a class="elementor-post__read-more" href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/" aria-label="Read more about Integration von OAuth 2.0 in Spring Boot mit Keycloak" tabindex="-1" >
			Weiterlesen »		</a>

				</div>
				</article>
				</div>
		
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-7c4cd4d elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="7c4cd4d" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-f80ad6a" data-id="f80ad6a" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-e5f9e74 elementor-widget elementor-widget-spacer" data-id="e5f9e74" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				</div>
		<p>Der Beitrag <a href="https://www.inoteq.com/2024/02/08/automatisches-front-end-testing-einer-oauth-2-0-spring-boot-anwendungen/">Automatisches Front-End-Testing einer OAuth 2.0 Spring-Boot Anwendungen</a> erschien zuerst auf <a href="https://www.inoteq.com">INOTEQ GmbH</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.inoteq.com/2024/02/08/automatisches-front-end-testing-einer-oauth-2-0-spring-boot-anwendungen/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Integration von OAuth 2.0 in Spring Boot mit Keycloak</title>
		<link>https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/</link>
					<comments>https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/#respond</comments>
		
		<dc:creator><![CDATA[Alexander Kusmin]]></dc:creator>
		<pubDate>Thu, 30 Nov 2023 07:00:00 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Keycloak]]></category>
		<category><![CDATA[OAuth 2.0]]></category>
		<category><![CDATA[Spring]]></category>
		<guid isPermaLink="false">https://www.inoteq.com/?p=1744</guid>

					<description><![CDATA[<p>Dieser Leitfaden beschreibt die Integration von OAuth 2.0 in eine Spring Boot Anwendung mit Keycloak. Nach einer kurzen Einführung in die Spring Boot Plattform liegt der Schwerpunkt auf der genauen Konfiguration von Keycloak als vertrauenswürdigen OAuth 2.0 Server und der Absicherung bestimmter Endpunkte in der Anwendung mit Anmeldedaten. Der Beitrag ist dabei so strukturiert, dass [&#8230;]</p>
<p>Der Beitrag <a href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/">Integration von OAuth 2.0 in Spring Boot mit Keycloak</a> erschien zuerst auf <a href="https://www.inoteq.com">INOTEQ GmbH</a>.</p>
]]></description>
										<content:encoded><![CDATA[		<div data-elementor-type="wp-post" data-elementor-id="1744" class="elementor elementor-1744" data-elementor-post-type="post">
						<section class="elementor-section elementor-top-section elementor-element elementor-element-ec639a5 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="ec639a5" data-element_type="section" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-824d4d9" data-id="824d4d9" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-1f19b9e elementor-widget elementor-widget-spacer" data-id="1f19b9e" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-a029d14 elementor-widget elementor-widget-html" data-id="a029d14" data-element_type="widget" data-widget_type="html.default">
				<div class="elementor-widget-container">
			<!-- Only for Code Snippet Styles -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/themes/prism-coy.min.css" integrity="sha256-VcuSs+n31yebPlEcehu6PvnidJ808ScFBsK8+tJKX+Q=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/components/prism-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/autoloader/prism-autoloader.min.js"></script>
<style>
    :not(pre) > code[class*=language-].inline {
        padding: 0 0.2rem;
        color: #000;
    }
</style>		</div>
				</div>
				<div class="elementor-element elementor-element-a814a42 elementor-widget elementor-widget-theme-post-title elementor-page-title elementor-widget-heading" data-id="a814a42" data-element_type="widget" data-widget_type="theme-post-title.default">
				<div class="elementor-widget-container">
			<h1 class="elementor-heading-title elementor-size-default">Integration von OAuth 2.0 in Spring Boot mit Keycloak</h1>		</div>
				</div>
				<div class="elementor-element elementor-element-4453699 elementor-widget elementor-widget-post-info" data-id="4453699" data-element_type="widget" data-widget_type="post-info.default">
				<div class="elementor-widget-container">
					<ul class="elementor-inline-items elementor-icon-list-items elementor-post-info">
								<li class="elementor-icon-list-item elementor-repeater-item-265ff67 elementor-inline-item" itemprop="datePublished">
						<a href="https://www.inoteq.com/2023/11/30/">
											<span class="elementor-icon-list-icon">
								<i aria-hidden="true" class="fas fa-calendar"></i>							</span>
									<span class="elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-date">
										November 30, 2023					</span>
									</a>
				</li>
				<li class="elementor-icon-list-item elementor-repeater-item-5924a19 elementor-inline-item" itemprop="author">
						<a href="https://www.inoteq.com/author/akusmin/">
											<span class="elementor-icon-list-icon">
								<i aria-hidden="true" class="far fa-user-circle"></i>							</span>
									<span class="elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-author">
										Alexander Kusmin					</span>
									</a>
				</li>
				<li class="elementor-icon-list-item elementor-repeater-item-2866628 elementor-inline-item" itemprop="about">
										<span class="elementor-icon-list-icon">
								<i aria-hidden="true" class="fas fa-tags"></i>							</span>
									<span class="elementor-icon-list-text elementor-post-info__item elementor-post-info__item--type-terms">
										<span class="elementor-post-info__terms-list">
				<a href="https://www.inoteq.com/tag/keycloak/" class="elementor-post-info__terms-list-item">Keycloak</a>, <a href="https://www.inoteq.com/tag/oauth-2-0/" class="elementor-post-info__terms-list-item">OAuth 2.0</a>, <a href="https://www.inoteq.com/tag/spring/" class="elementor-post-info__terms-list-item">Spring</a>				</span>
					</span>
								</li>
				</ul>
				</div>
				</div>
				<div class="elementor-element elementor-element-3fee39a elementor-widget elementor-widget-text-editor" data-id="3fee39a" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Dieser Leitfaden beschreibt die Integration von <a href="https://de.wikipedia.org/wiki/OAuth">OAuth 2.0</a> in eine <a href="https://spring.io/projects/spring-boot/">Spring Boot</a> Anwendung mit <a href="https://www.keycloak.org/" target="_blank" rel="noopener">Keycloak</a>. Nach einer kurzen Einführung in die Spring Boot Plattform liegt der Schwerpunkt auf der genauen Konfiguration von Keycloak als vertrauenswürdigen OAuth 2.0 Server und der Absicherung bestimmter Endpunkte in der Anwendung mit Anmeldedaten.</p><p>Der Beitrag ist dabei so strukturiert, dass Entwickler Schritt für Schritt eine sichere Authentifizierung in einer Spring Boot-Anwendung implementieren können.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-1fddcb6 elementor-widget elementor-widget-theme-post-featured-image elementor-widget-image" data-id="1fddcb6" data-element_type="widget" data-widget_type="theme-post-featured-image.default">
				<div class="elementor-widget-container">
										<figure class="wp-caption">
										<img decoding="async" width="2000" height="1200" src="https://www.inoteq.com/wp-content/uploads/2023/11/wp1.png" class="attachment-full size-full wp-image-2922" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/wp1.png 2000w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-300x180.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-1024x614.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-768x461.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/wp1-1536x922.png 1536w" sizes="(max-width: 2000px) 100vw, 2000px" />											<figcaption class="widget-image-caption wp-caption-text">KI generiertes Symbolbild: compelling, light-hearted tone, humorous, illustration, logo-style, spring boot (footwear), security key, keylock, keycloak oauth2, symbolizing secure access</figcaption>
										</figure>
							</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-38ab89f elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="38ab89f" data-element_type="section">
						<div class="elementor-container elementor-column-gap-no">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-b50792b" data-id="b50792b" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-9ef30cb elementor-toc--minimized-on-desktop elementor-widget elementor-widget-table-of-contents" data-id="9ef30cb" data-element_type="widget" data-settings="{&quot;exclude_headings_by_selector&quot;:&quot;.exclude-from-toc&quot;,&quot;minimized_on&quot;:&quot;desktop&quot;,&quot;headings_by_tags&quot;:[&quot;h2&quot;,&quot;h3&quot;,&quot;h4&quot;,&quot;h5&quot;,&quot;h6&quot;],&quot;marker_view&quot;:&quot;numbers&quot;,&quot;minimize_box&quot;:&quot;yes&quot;,&quot;hierarchical_view&quot;:&quot;yes&quot;,&quot;min_height&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;min_height_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;min_height_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}" data-widget_type="table-of-contents.default">
				<div class="elementor-widget-container">
					<div class="elementor-toc__header">
			<h4 class="elementor-toc__header-title">
				Inhaltsverzeichnis			</h4>
							<div class="elementor-toc__toggle-button elementor-toc__toggle-button--expand" role="button" tabindex="0" aria-controls="elementor-toc__9ef30cb" aria-expanded="true" aria-label="Open table of contents"><i aria-hidden="true" class="fas fa-chevron-down"></i></div>
				<div class="elementor-toc__toggle-button elementor-toc__toggle-button--collapse" role="button" tabindex="0" aria-controls="elementor-toc__9ef30cb" aria-expanded="true" aria-label="Close table of contents"><i aria-hidden="true" class="fas fa-chevron-up"></i></div>
					</div>
		<div id="elementor-toc__9ef30cb" class="elementor-toc__body">
			<div class="elementor-toc__spinner-container">
				<i class="elementor-toc__spinner eicon-animation-spin eicon-loading" aria-hidden="true"></i>			</div>
		</div>
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-74fdfab elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="74fdfab" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-3f3966c" data-id="3f3966c" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-96f6788 elementor-widget elementor-widget-spacer" data-id="96f6788" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-820e96a elementor-widget elementor-widget-heading" data-id="820e96a" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Spring Boot: Setup und Grundkonfiguration</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-d1751ae elementor-widget elementor-widget-heading" data-id="d1751ae" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Konfiguration</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-ccc8dbe elementor-widget elementor-widget-text-editor" data-id="ccc8dbe" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die Vorbereitung der Spring Boot Anwendung für die Integration von OAuth 2.0 beginnt mit der Einrichtung des Projektes.</p><p>Für diesen Leitfaden wurde folgende Konfiguration gewählt:</p><ul><li>Spring Boot v3.2.0</li><li>Kotlin als Programmiersprache für Spring</li><li>Gradle (Groovy) für das Build-Tool</li></ul><p>Als Vorbereitung für die spätere Integration von OAuth 2.0 werden zudem noch folgende Abhängigkeiten benötigt:</p><ul><li><a href="https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web/3.2.0" target="_blank" rel="noopener">Spring Web v3.2.0</a></li><li><a href="https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security/3.2.0" target="_blank" rel="noopener">Spring Security v3.2.0</a></li><li><a href="https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-oauth2-client/3.2.0" target="_blank" rel="noopener">OAuth2 Client v3.2.0</a></li></ul><p>Diese Konfiguration bildet die Grundlage für die Entwicklung der Webanwendung mit integriertem OAuth 2.0 als Sicherheitsmechanismus. Die spezifische Konfiguration kann als neues leeres Projekt <a style="font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight ); background-color: #ffffff;" href="https://start.spring.io/#!type=gradle-project&amp;language=kotlin&amp;platformVersion=3.2.0&amp;packaging=jar&amp;jvmVersion=21&amp;groupId=com.example&amp;artifactId=demo&amp;name=demo&amp;description=Demo%20project%20for%20Spring%20Boot&amp;packageName=com.example.demo&amp;dependencies=web,security,oauth2-client" target="_blank" rel="noopener">hier über den Spring Initializr</a> direkt geladen werden.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-ca0c0e8 elementor-widget elementor-widget-heading" data-id="ca0c0e8" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Anwendung für OAuth 2.0 vorbereiten</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-542df6e elementor-widget elementor-widget-text-editor" data-id="542df6e" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Bevor die Anwendung erstmalig gestartet wird, sollten noch die folgenden Anpassungen vorgenommen werden. Durch die Einbindung der Spring Security Abhängigkeit, ist bereits die gesamte Anwendung mit allen Endpunkten standardmäßig mit einer simplen User/Passwort-Authentifizierung abgesichert. Da OAuth 2.0 später nur für bestimmte Endpunkte verwendet werden soll, ist eine entsprechende Konfiguration von Spring Security erforderlich, bei der diese grundlegende Authentifizierung durch eine Annotation in der Datei <b><i>DemoApplication.kt</i></b> vorerst deaktiviert wird. Zu Testzwecken wird ein Endpunkt in einem neuen Controller <b><i>DemoController.kt</i></b> definiert. Der Endpunkt und die Deaktivierung der Authentifizierung werden zu einem späteren Zeitpunkt wieder entfernt, wenn OAuth 2.0 konfiguriert wird.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-9eea9ed elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="9eea9ed" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-1661" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1661" aria-expanded="false">src/main/kotlin/.../DemoApplication.kt</div>
									<div id="elementor-tab-title-1662" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="2" role="tab" tabindex="-1" aria-controls="elementor-tab-content-1662" aria-expanded="false">src/main/kotlin/.../DemoController.kt</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1661" aria-expanded="false">src/main/kotlin/.../DemoApplication.kt</div>
					<div id="elementor-tab-content-1661" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1661" tabindex="0" hidden="false"><pre><code class="language-kotlin">package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.boot.runApplication

// Deaktivierung der default Authentifizierung von Spring Security
@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
class DemoApplication

fun main(args: Array) {
    runApplication(*args)
}
</code></pre></div>
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="false" data-tab="2" role="tab" tabindex="-1" aria-controls="elementor-tab-content-1662" aria-expanded="false">src/main/kotlin/.../DemoController.kt</div>
					<div id="elementor-tab-content-1662" class="elementor-tab-content elementor-clearfix" data-tab="2" role="tabpanel" aria-labelledby="elementor-tab-title-1662" tabindex="0" hidden="hidden"><pre><code class="language-kotlin">package com.example.demo

import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class DemoController {
    @RequestMapping("/")
    fun helloWorld() = "Hello World!"
}
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-0c8dbd5 elementor-widget elementor-widget-text-editor" data-id="0c8dbd5" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Mit dem Befehl <code class="language-bash inline">./gradlew bootRun</code> wird die Anwendung über das Terminal gestartet und ist anschließend unter der Adresse <a href="http://localhost:8080" target="_blank" rel="noopener">http://localhost:8080</a> erreichbar.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-825a46a elementor-widget elementor-widget-image" data-id="825a46a" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
										<figure class="wp-caption">
										<img decoding="async" width="768" height="447" src="https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-12.27.53-768x447.png" class="attachment-medium_large size-medium_large wp-image-2078" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-12.27.53-768x447.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-12.27.53-300x175.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-12.27.53-1024x596.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-12.27.53.png 1061w" sizes="(max-width: 768px) 100vw, 768px" />											<figcaption class="widget-image-caption wp-caption-text">Ausgabe von "Hello World!" auf http://localhost:8080</figcaption>
										</figure>
							</div>
				</div>
				<div class="elementor-element elementor-element-f99b2cf elementor-widget elementor-widget-text-editor" data-id="f99b2cf" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Als Vorbereitung für die spätere Integration von OAuth 2.0 werden zwei weitere Endpunkte implementiert — ein öffentlicher Endpunkt <code class="language-plain inline">/public</code> und ein privater Endpunkt <code class="language-plain inline">/private</code>, welcher später nur für authentifizierte Benutzer aufrufbar sein soll. Unser &#8222;Hello World!&#8220; Endpunkt wird wieder entfernt:</p>						</div>
				</div>
				<div class="elementor-element elementor-element-2d26e30 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="2d26e30" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-4731" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-4731" aria-expanded="false">./src/main/kotlin/.../DemoController.kt</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-4731" aria-expanded="false">./src/main/kotlin/.../DemoController.kt</div>
					<div id="elementor-tab-content-4731" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-4731" tabindex="0" hidden="false"><pre><code class="language-kotlin">package com.example.demo

import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class DemoController {
    @RequestMapping("/", "/public")
    fun publicEndpoint() = "Hello from a public endpoint!"

    @RequestMapping("/private")
    fun privateEndpoint() = "Hello from a private endpoint!"
}
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-41e88b2 elementor-widget elementor-widget-text-editor" data-id="41e88b2" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Im Folgenden wollen wir mit OAuth 2.0 den Endpunkt <code class="language-plain inline">/private</code> absichern, der in diesem Zustand noch ohne Authentifizierung erreichbar ist.						</div>
				</div>
				<div class="elementor-element elementor-element-ffd0789 elementor-widget elementor-widget-alert" data-id="ffd0789" data-element_type="widget" data-widget_type="alert.default">
				<div class="elementor-widget-container">
					<div class="elementor-alert elementor-alert-warning" role="alert">
			<span class="elementor-alert-title">Mögliche Herrausforderungen</span>
							<span class="elementor-alert-description">Bei der Integration von Spring Security und OAuth 2.0 können Herausforderungen wie Konfigurationskonflikte und unerwartetes Verhalten auftreten. Es ist ratsam sicherzustellen, dass die Versionen der verwendeten Abhängigkeiten konsistent sind. Eine Überprüfung der Logdateien auf mögliche Fehlermeldungen ist ratsam.</span>
								</div>
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-8cde8de elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="8cde8de" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-0a9c3ef" data-id="0a9c3ef" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-576ce23 elementor-widget elementor-widget-spacer" data-id="576ce23" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-d5c74ac elementor-widget elementor-widget-heading" data-id="d5c74ac" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Installation &amp; Konfiguration von Keycloak</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-2042828 elementor-widget elementor-widget-text-editor" data-id="2042828" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><a href="https://www.keycloak.org/" target="_blank" rel="noopener">Keycloak</a> ist eine Open-Source-Identitäts- und Zugriffsmanagementlösung. Es fungiert als OAuth 2.0 Server und ermöglicht die Authentifizierung von Benutzern, die Verwaltung von Anwendungs-Berechtigungen und die Bereitstellung von Single Sign-On für Anwendungen.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-42605d9 elementor-widget elementor-widget-heading" data-id="42605d9" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Einrichtung mit Docker</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-d1b28e2 elementor-widget elementor-widget-text-editor" data-id="d1b28e2" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Die Verwendung von Docker ermöglicht den Betrieb einer konsistente und isolierte Umgebung für Keycloak. Zusätzlich wird die Einrichtung erleichtert und sichergestellt, dass alle erforderlichen Abhängigkeiten vorhanden sind und reibungslos funktionieren. <span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">Da bereits eine ausführliche Anleitung </span><a style="font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight ); background-color: #ffffff;" href="https://www.keycloak.org/getting-started/getting-started-docker" target="_blank" rel="noopener">auf der offiziellen Keycloak-Seite</a><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> zu finden ist, wird im Folgenden nur noch auf die relevanten Punkte eingegangen. Bevor Keycloak in einem Docker-Container gestartet werden kann, sollten sichergestellt werden, dass Docker auf dem System installiert und einsatzbereit ist. Dies kann mit dem Befehl </span><code style="color: var( --e-global-color-text ); font-weight: var( --e-global-typography-text-font-weight ); font-size: 18px;" class="language-plain inline">docker −−version</code><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> getestet werden, wobei Die Ausgabe die Installierte Docker-Version anzeigen muss. Eine Installationsanleitung für Docker selbst kann der </span><a style="font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight ); background-color: #ffffff;" href="https://docs.docker.com/engine/install/" target="_blank" rel="noopener">offziellen Seite</a><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> entnommen werden.</span>

<span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">Zur eigentlichen Installation und Starten von Keycloak verwenden wir folgenden Befehl:</span>						</div>
				</div>
				<div class="elementor-element elementor-element-9a02a17 elementor-widget elementor-widget-html" data-id="9a02a17" data-element_type="widget" data-widget_type="html.default">
				<div class="elementor-widget-container">
			<pre><code style="white-space: normal; word-break: break-word;" class="language-sh">docker run -p 8081:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak start-dev</code></pre>		</div>
				</div>
				<div class="elementor-element elementor-element-64c4573 elementor-widget elementor-widget-text-editor" data-id="64c4573" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Der Befehl setzt sich dabei folgendermaßen zusammen:</p><ul><li><code class="language-plain inline">docker run</code> Anweisung an Docker den Container mit den Parametern zu starten</li><li><code class="language-plain inline">-p 8081:8080</code> Portmapping von 8081 (Container außen) zu 8080 (Container innen)</li><li><code class="language-plain inline">-e KEYCLOAK_ADMIN=admin</code> Benutzername für das Keycloak Webinterface</li><li><code class="language-plain inline">-e KEYCLOAK_ADMIN_PASSWORD=admin</code> Passwort für das Keycloak Webinterface</li><li><code class="language-plain inline">quay.io/keycloak/keycloak</code> URL auf das Docker-Image für Keycloak</li><li><code class="language-plain inline">start-dev</code> Startet Keycloak innerhalb des Docker-Containers</li></ul>						</div>
				</div>
				<div class="elementor-element elementor-element-6413a8e elementor-arrows-position-inside elementor-pagination-position-outside elementor-widget elementor-widget-image-carousel" data-id="6413a8e" data-element_type="widget" data-settings="{&quot;slides_to_show&quot;:&quot;1&quot;,&quot;autoplay&quot;:&quot;no&quot;,&quot;infinite&quot;:&quot;no&quot;,&quot;navigation&quot;:&quot;both&quot;,&quot;effect&quot;:&quot;slide&quot;,&quot;speed&quot;:500}" data-widget_type="image-carousel.default">
				<div class="elementor-widget-container">
					<div class="elementor-image-carousel-wrapper swiper-container" dir="ltr">
			<div class="elementor-image-carousel swiper-wrapper" aria-live="polite">
								<div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="1 von 1"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-13.15.49-768x833.png" alt="Ausgabe von Docker für Keycloak" /><figcaption class="elementor-image-carousel-caption">Ausgabe von Docker für Keycloak</figcaption></figure></div>			</div>
					</div>
				</div>
				</div>
				<section class="elementor-section elementor-inner-section elementor-element elementor-element-3c3343d elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="3c3343d" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-66 elementor-inner-column elementor-element elementor-element-a373291" data-id="a373291" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-beb93e5 elementor-widget elementor-widget-text-editor" data-id="beb93e5" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">Sofern lokal noch kein Keycloak Docker-Image existiert, wird dieses automatisch heruntergeladen. Wenn der Befehl ausgeführt wird, startet ein neuer Keycloak-Container, welcher über </span><a style="font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight ); background-color: #ffffff;" href="http://localhost:8081" target="_blank" rel="noopener">http://localhost:8081</a><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> erreichbar ist. In der Administrationskonsole unter </span><a style="font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight ); background-color: #ffffff;" href="http://localhost:8081/admin" target="_blank" rel="noopener">http://localhost:8081/admin</a><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> kann sich mit den definierten Benutzerdaten angemeldet werden, um Keycloak für den OAuth 2.0 Server zu konfigurieren.</span></p>						</div>
				</div>
					</div>
		</div>
				<div class="elementor-column elementor-col-33 elementor-inner-column elementor-element elementor-element-7254df5" data-id="7254df5" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-3e5a55c elementor-widget elementor-widget-image" data-id="3e5a55c" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
														<a href="https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-13.15.54.png" data-elementor-open-lightbox="yes" data-elementor-lightbox-title="Keycloak" data-e-action-hash="#elementor-action%3Aaction%3Dlightbox%26settings%3DeyJpZCI6MjExMywidXJsIjoiaHR0cHM6XC9cL3d3dy5pbm90ZXEuY29tXC93cC1jb250ZW50XC91cGxvYWRzXC8yMDIzXC8xMVwvU2NyZWVuc2hvdC0yMDI0LTAxLTA5LWF0LTEzLjE1LjU0LnBuZyJ9">
							<img loading="lazy" decoding="async" width="800" height="786" src="https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-13.15.54-1024x1006.png" class="attachment-large size-large wp-image-2113" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-13.15.54-1024x1006.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-13.15.54-300x295.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-13.15.54-768x754.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-13.15.54-1536x1509.png 1536w, https://www.inoteq.com/wp-content/uploads/2023/11/Screenshot-2024-01-09-at-13.15.54.png 2034w" sizes="(max-width: 800px) 100vw, 800px" />								</a>
													</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<div class="elementor-element elementor-element-349897e elementor-widget elementor-widget-heading" data-id="349897e" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">OAuth 2.0 Server Konfiguration</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-98011df elementor-widget elementor-widget-text-editor" data-id="98011df" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>In der Administrationskonsole wird zuerst neuer <em><strong>Realm</strong></em> für die Anwendung erstellt. Bei einem Realm in Keycloak handelt es sich um eine isolierte Sicherheitsdomäne, die Benutzer, Ressourcen und Einstellungen in einem OAuth 2.0 Server organisiert. Auf diese Weise ist eine flexible Verwaltung verschiedener Anwendungen in ein und derselben Keycloak-Instanz möglich. Standardmäßig ist der build-in Realm <code class="language-plain inline">master</code> ausgewählt. Der Reiter wird angeklickt und über die Schaltfläche <em><strong>Create realm</strong></em> wird ein neuer Realm erstellt. Beim Anlegen wird ein Name vergeben <code class="language-plain inline">demo-realm</code> vergeben , und sichergestellt, dass dieser aktiv ist.</p><p><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">Nun müssen wir für die Anwendung ein </span><em style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"><strong>Mandant</strong></em><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> anlegen. Ein Mandant in Keycloak repräsentiert eine Anwendung, die OAuth 2.0 oder OpenID Connect zur Authentifizierung und Autorisierung verwendet. Jeder Mandant hat eine eindeutige Identität und Konfiguration und kann Ressourcen in einem bestimmten Realm schützen. Beispiele für Mandanten sind Webanwendungen, mobile Anwendungen oder Dienste, die Zugriff auf geschützte Ressourcen benötigen. Zur Erstellung eines neuen Mandanten kann im Menüpunkt </span><strong style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif;"><em>Clients</em></strong><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> der Button </span><strong style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif;"><em>Create client </em></strong><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">angeklickt werden, worauf ein Formular erscheint, das in drei Schritte gegliedert ist:</span></p><ol><li>Schritt: Für den Client wird der Typ <strong><em>OpenID Connect</em></strong> ausgewählt und die Client-ID <code class="language-plain inline">demo-client</code> vergeben .</li><li>Schritt: In diesem Schritt muss sichergestellt werden, dass unter &#8222;<em>Authentication flow</em>&#8220; die Option <strong><em>Standard flow</em></strong> aktiviert ist. Die Aktivierung der Option &#8222;Standard flow&#8220; in Keycloak ist entscheidend, um die Sicherheit zu erhöhen, indem der Authentifizierungsprozess in zwei Schritte aufgeteilt wird: Der Benutzer authentifiziert sich am Autorisierungsserver und tauscht sicher einen temporären Autorisierungscode gegen Zugriffstoken aus. Dies minimiert das Risiko, sensible Informationen am Frontend preiszugeben, insbesondere bei serverseitigen Anwendungen wie Spring Boot.</li><li>Schritt: Hier wird im Feld <em><b>Valid redirect URIs</b></em> die URL <code class="language-plain inline">http://localhost:8080/*</code> eingetragen und alle Eingaben mit einem Klick auf <strong><em>Save</em></strong> bestätigt. Dieser Eintrag ist erforderlich, damit Keycloak nach der Benutzerauthentifizierung die korrekte Rücksendeadresse für den Authorisierungscode erhält. Der Stern ermöglicht die Annahme gültiger Redirect-URIs für alle Pfade unter <code class="language-plain inline">http://localhost:8080/</code>. Dies bietet Flexibilität für verschiedene Endpunkte und unterstützt die sichere Rückgabe von Authentifizierungsantworten.</li></ol><div> </div><p>Damit wäre Keycloak bereits als OAuth 2.0 Server für die Anwendung konfiguriert.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-8450c1a elementor-arrows-position-inside elementor-pagination-position-outside elementor-widget elementor-widget-image-carousel" data-id="8450c1a" data-element_type="widget" data-settings="{&quot;slides_to_show&quot;:&quot;1&quot;,&quot;autoplay&quot;:&quot;no&quot;,&quot;infinite&quot;:&quot;no&quot;,&quot;navigation&quot;:&quot;both&quot;,&quot;effect&quot;:&quot;slide&quot;,&quot;speed&quot;:500}" data-widget_type="image-carousel.default">
				<div class="elementor-widget-container">
					<div class="elementor-image-carousel-wrapper swiper-container" dir="ltr">
			<div class="elementor-image-carousel swiper-wrapper" aria-live="polite">
								<div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="1 von 3"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-09.38.03-768x755.png" alt="Schritt 1 - Erstellung eines Mandanten in Keycloak" /><figcaption class="elementor-image-carousel-caption">Schritt 1 - Erstellung eines Mandanten in Keycloak</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="2 von 3"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-09.38.33-768x755.png" alt="Schritt 2 - Erstellung eines Mandanten in Keycloak" /><figcaption class="elementor-image-carousel-caption">Schritt 2 - Erstellung eines Mandanten in Keycloak</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="3 von 3"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-09.38.59-768x755.png" alt="Schritt 3 - Erstellung eines Mandanten in Keycloak" /><figcaption class="elementor-image-carousel-caption">Schritt 3 - Erstellung eines Mandanten in Keycloak</figcaption></figure></div>			</div>
												<div class="elementor-swiper-button elementor-swiper-button-prev" role="button" tabindex="0">
						<i aria-hidden="true" class="eicon-chevron-left"></i>					</div>
					<div class="elementor-swiper-button elementor-swiper-button-next" role="button" tabindex="0">
						<i aria-hidden="true" class="eicon-chevron-right"></i>					</div>
				
									<div class="swiper-pagination"></div>
									</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-1b159ff elementor-widget elementor-widget-heading" data-id="1b159ff" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Rollenverwaltung</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-8d6d796 elementor-widget elementor-widget-text-editor" data-id="8d6d796" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die Benutzer müssen mit einem Rollenattribut versehen werden, welches über den Token an die Anwendung weitergegeben werden muss. Zu diesem Zweck muss der Mandant noch weiter konfiguriert werden.</p><p>Um eine neue Rolle anzulegen, wird über den Menüpunkt <strong><em>Clients</em></strong> in den angelegten Mandanten <code class="language-plain inline">demo-client</code> navigiert. Hier wird über den Reiter <strong><em>Roles</em></strong> und den Button <strong><em>Create role</em></strong> eine neue Rolle angelegt. Für die neue Rolle wird der Rollenname <code class="language-plain inline">demo-role</code> gewählt und anschließend mit dem Button <strong><em>Save</em></strong> bestätigt. Der Mandant muss nun so konfiguriert werden, dass diese Rolle im Token enthalten ist. Nach erfolgreicher Authentifizierung generiert Keycloak das Token, welches Informationen über den Benutzer sowie gewährte Berechtigungen enthält. Die Anwendung nutzt das Token zur Identifikation des Benutzers und für entsprechende Aktionen basierend auf den Tokeninformationen. Dazu wird im Reiter &#8222;<em>Client scopes</em>&#8220; zu &#8222;<em>demo-client-dedicated</em>&#8220; navigiert und dort mit &#8222;<em>Add predifined mapper</em>&#8220; ein neuer Token-Mapper für <strong><em>client roles</em></strong> definiert. Das Ganze wird mit &#8222;<em>Add</em>&#8220; bestätigt. Ein Klick auf den neu angelegten Eintrag &#8222;<em>client roles</em>&#8220; öffnet weitere Einstellungen. Hier wird für &#8222;<em>Token Claim Name</em>&#8220; ein Wert festgelegt, z.B. <code class="language-plain inline">roles</code> , die Option &#8222;<em>Add to ID token</em>&#8220; <strong>aktiviert</strong> und die Änderungen mit &#8222;<em>Save</em>&#8220; bestätigt. Nun enthält das Token in der Anwendung über ein Attribut mit dem Schlüssel &#8222;<em>roles</em>&#8220; alle Rollen des Benutzers.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-b800550 elementor-arrows-position-inside elementor-pagination-position-outside elementor-widget elementor-widget-image-carousel" data-id="b800550" data-element_type="widget" data-settings="{&quot;slides_to_show&quot;:&quot;1&quot;,&quot;autoplay&quot;:&quot;no&quot;,&quot;infinite&quot;:&quot;no&quot;,&quot;navigation&quot;:&quot;both&quot;,&quot;effect&quot;:&quot;slide&quot;,&quot;speed&quot;:500}" data-widget_type="image-carousel.default">
				<div class="elementor-widget-container">
					<div class="elementor-image-carousel-wrapper swiper-container" dir="ltr">
			<div class="elementor-image-carousel swiper-wrapper" aria-live="polite">
								<div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="1 von 4"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-09.39.27-768x755.png" alt="Navigation zum Mandanten-Geltungsbereich" /><figcaption class="elementor-image-carousel-caption">Navigation zum Mandanten-Geltungsbereich</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="2 von 4"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-09.39.39-768x755.png" alt="Ansicht zu Hinzufügen eines neuen Token-Mappers" /><figcaption class="elementor-image-carousel-caption">Ansicht zu Hinzufügen eines neuen Token-Mappers</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="3 von 4"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-09.39.52-768x755.png" alt="Ausgewählter Token-Mapper für Mandanten-Rollen" /><figcaption class="elementor-image-carousel-caption">Ausgewählter Token-Mapper für Mandanten-Rollen</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="4 von 4"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-09.40.31-768x755.png" alt="Konfiguration des Token-Mappers" /><figcaption class="elementor-image-carousel-caption">Konfiguration des Token-Mappers</figcaption></figure></div>			</div>
												<div class="elementor-swiper-button elementor-swiper-button-prev" role="button" tabindex="0">
						<i aria-hidden="true" class="eicon-chevron-left"></i>					</div>
					<div class="elementor-swiper-button elementor-swiper-button-next" role="button" tabindex="0">
						<i aria-hidden="true" class="eicon-chevron-right"></i>					</div>
				
									<div class="swiper-pagination"></div>
									</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-8546b84 elementor-widget elementor-widget-heading" data-id="8546b84" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Benutzerverwaltung</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-c21f7ad elementor-widget elementor-widget-text-editor" data-id="c21f7ad" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Um sich später in der Spring Boot Anwendung anmelden zu können, muss ein Benutzer definiert werden. Dazu wird ein neuer Benutzer im Realm angelegt. Das Formular zur Eingabe der Benutzerdaten wird über den Menüpunkt <strong><em>Users</em></strong> und den Button <strong><em>Add user</em></strong> aufgerufen. Im Rahmen dieses Leitfadens wird ein Benutzer mit dem Benutzernamen <code class="language-plain inline">john.doe</code> , dem Vor- und Nachnamen <code class="language-plain inline">John Doe</code> und der E-Mail-Adresse <code class="language-plain inline">john.doe@example.com</code> angelegt. Es ist nicht erforderlich, weitere Einstellungen zu ändern.</p><p><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">Für den neu angelegten Benutzer muss noch ein Passwort festgelegt werden. Dazu reicht es zunächst aus, im Benutzer unter dem Reiter </span><strong style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif;"><em>Credentials</em></strong><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> und dem Button </span><strong style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif;"><em>Set password</em></strong><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> ein neues, nicht temporäres Passwort zu setzen, z.B. </span><code class="language-plain inline" style="font-weight: var( --e-global-typography-text-font-weight ); font-size: 18px;">password</code><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> . Unter dem Reiter </span><strong style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif;"><em>Role mapping</em></strong><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> wird dem Benutzer die zuvor angelegte Gruppe zugewiesen. Dazu wird auf </span><strong style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif;"><em>Assign role</em></strong><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> geklickt und der Filter so eingestellt, dass die Mandantenrollen angezeigt werden. Anschließend wird die Rolle </span><code class="language-plain inline" style="font-weight: var( --e-global-typography-text-font-weight ); font-size: 18px;">(demo-client) demo-role</code><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> ausgewählt und mit </span><strong style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif;"><em>Assign</em></strong><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> bestätigt.</span></p><p><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">Unter &#8222;</span><a style="font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight ); background-color: #ffffff;" href="http://localhost:8081/realms/demo-realm/account" target="_blank" rel="noopener">http://localhost:8081/realms/demo-realm/account</a><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">&#8220; kann durch Anmelden mit dem angelegten Benutzer überprüft werden, ob dieser korrekt angelegt und konfiguriert wurde.</span></p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-4dc4e86 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="4dc4e86" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-7143e2a" data-id="7143e2a" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-2e7c0e2 elementor-widget elementor-widget-spacer" data-id="2e7c0e2" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-af8f6d2 elementor-widget elementor-widget-heading" data-id="af8f6d2" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">OAuth 2.0 Konfiguration in Spring Boot</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-bcb971c elementor-widget elementor-widget-heading" data-id="bcb971c" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Anpassen der Anwendungseigenschaften</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-000c56c elementor-widget elementor-widget-text-editor" data-id="000c56c" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Der erste Schritt bei der Konfiguration von OAuth 2.0 in der Anwendung ist die Anpassung der Datei <b><em>application.yml</em></b>. Dabei wird ein Provider <code>demo-provider</code> definiert, der auf den Realm verweist, und ein Client <code class="language-plain inline">demo-client</code> registriert:						</div>
				</div>
				<div class="elementor-element elementor-element-27d1fd5 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="27d1fd5" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-4171" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-4171" aria-expanded="false">./application.yml</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-4171" aria-expanded="false">./application.yml</div>
					<div id="elementor-tab-content-4171" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-4171" tabindex="0" hidden="false"><pre><code class="language-yml">spring:
  security:
    oauth2:
      client:
        provider:
          # provider name used for registration
          demo-provider:
            # Keycloak realm URL: "{keycloak-url}/realms/{realm-name}"
            issuer-uri: http://localhost:8081/realms/demo-realm
        registration:
          # client-id from Keycloak client configuration
          demo-client:
            # provider from above
            provider: demo-provider
            # client-id from Keycloak client configuration
            client-id: demo-client
            scope: openid
            # default redirect URI: "{baseUrl}/login/oauth2/code/{registrationId}"
            redirect-uri: http://localhost:8080/login/oauth2/code/demo-client
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-96d20f7 elementor-widget elementor-widget-heading" data-id="96d20f7" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Spring Security Konfiguration</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-11e3031 elementor-widget elementor-widget-text-editor" data-id="11e3031" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Zusätzlich muss noch eine Konfiguration für Spring Security erstellt werden. Dazu wird eine neue Klasse <em><strong>SecurityConfiguration.kt</strong></em> erstellt, in der die Filterkette so konfiguriert wird, dass für den Endpunkt <code class="language-plain inline">/private</code> ein Benutzer mit OAuth 2.0 erfolgreich angemeldet sein muss. Mit der Syntax <code class="language-plain inline">/private/**</code> werden alle Endpunkte gesichert. Es müssen nicht alle einzeln angegeben werden.						</div>
				</div>
				<div class="elementor-element elementor-element-b677605 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="b677605" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-1911" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1911" aria-expanded="false">./src/main/kotlin/.../SecurityConfiguration.kt</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1911" aria-expanded="false">./src/main/kotlin/.../SecurityConfiguration.kt</div>
					<div id="elementor-tab-content-1911" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1911" tabindex="0" hidden="false"><pre><code class="language-kotlin">package com.example.demo

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.Customizer
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.web.SecurityFilterChain

@Configuration
@EnableWebSecurity
class SecurityConfiguration {
    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain =
        http
            .authorizeHttpRequests {
                it
                    // Only allow authenticated users here
                    .requestMatchers("/private/**")
                    .fullyAuthenticated()
                    // Allow all other requests
                    .anyRequest()
                    .permitAll()
            }
            // Use OAuth 2.0 Login to handle authentication
            .oauth2Login(Customizer.withDefaults())
            .build()
}
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-5fca981 elementor-widget elementor-widget-text-editor" data-id="5fca981" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Wurde die automatische Konfiguration von Spring Security per Annotation deaktiviert, wie wir es anfangs zu Testzwecken gemacht haben, muss sichergestellt werden, dass sie wieder aktiviert wird. Um dies zu erreichen, muss der Parameter aus der <code class="language-plain inline">@SpringBootApplication()</code> Annotation in der Datei <strong><b><i>DemoApplication.kt</i></b></strong> entfernt werden. Die Annotation selbst muss bestehen bleiben.

<span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">Die Anwendung kann nun gestartet werden. Unter dem Endpunkt </span><code class="language-plain inline" style="font-weight: var( --e-global-typography-text-font-weight ); font-size: 18px;">/public</code><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> sollte keine Authentifizierung abgefragt werden und es wird lediglich &#8222;Hello from a public endpoint!&#8220; angezeigt. Wird zum Endpunkt </span><code class="language-plain inline" style="font-weight: var( --e-global-typography-text-font-weight ); font-size: 18px;">/private</code><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> navigiert, so navigiert die Anwendung zunächst zum OAuth 2.0 Server. Hier kann eine Anmeldung mit dem Benutzer </span><code class="language-plain inline" style="font-weight: var( --e-global-typography-text-font-weight ); font-size: 18px;">john.doe</code><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> und dem Passwort </span><code class="language-plain inline" style="font-weight: var( --e-global-typography-text-font-weight ); font-size: 18px;">password</code><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> erfolgen. Anschließend sollte eine automatische Weiterleitung zurück zur urpsürnglichen Seite erfolgen. Nun wird der Text &#8222;Hello from a private endpoint!&#8220; angezeigt.</span>						</div>
				</div>
				<div class="elementor-element elementor-element-a714aad elementor-arrows-position-inside elementor-pagination-position-outside elementor-widget elementor-widget-image-carousel" data-id="a714aad" data-element_type="widget" data-settings="{&quot;slides_to_show&quot;:&quot;1&quot;,&quot;autoplay&quot;:&quot;no&quot;,&quot;infinite&quot;:&quot;no&quot;,&quot;navigation&quot;:&quot;both&quot;,&quot;effect&quot;:&quot;slide&quot;,&quot;speed&quot;:500}" data-widget_type="image-carousel.default">
				<div class="elementor-widget-container">
					<div class="elementor-image-carousel-wrapper swiper-container" dir="ltr">
			<div class="elementor-image-carousel swiper-wrapper" aria-live="polite">
								<div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="1 von 3"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-10.10.25-768x606.png" alt="Ausgabe auf http://localhost:8080/ bzw. http://localhost:8080/public" /><figcaption class="elementor-image-carousel-caption">Ausgabe auf http://localhost:8080/ bzw. http://localhost:8080/public</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="2 von 3"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-10.11.03-768x606.png" alt="Anmeldung beim Versuch den privaten Endpunkt aufzurufen" /><figcaption class="elementor-image-carousel-caption">Anmeldung beim Versuch den privaten Endpunkt aufzurufen</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="3 von 3"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-10.11.11-768x606.png" alt="Ausgabe auf http://localhost:8080/private, wenn sich erfolgreich angemeldet wurde" /><figcaption class="elementor-image-carousel-caption">Ausgabe auf http://localhost:8080/private, wenn sich erfolgreich angemeldet wurde</figcaption></figure></div>			</div>
												<div class="elementor-swiper-button elementor-swiper-button-prev" role="button" tabindex="0">
						<i aria-hidden="true" class="eicon-chevron-left"></i>					</div>
					<div class="elementor-swiper-button elementor-swiper-button-next" role="button" tabindex="0">
						<i aria-hidden="true" class="eicon-chevron-right"></i>					</div>
				
									<div class="swiper-pagination"></div>
									</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-5cf4d5d elementor-widget elementor-widget-heading" data-id="5cf4d5d" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Ausgabe der Informationen vom Token</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-7c24d5f elementor-widget elementor-widget-text-editor" data-id="7c24d5f" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Sobald ein Benutzer die Anwendung aufruft, wird in der Anwendung ein Benutzerobjekt erzeugt. Wenn der Benutzer sich nicht authentifiziert hat, z.B. am öffentlichen Endpunkt, wird der Benutzer als <b><i>anonym</i></b> behandelt. Sobald jedoch eine erfolgreiche Anmeldung über OAuth 2.0 erfolgt ist, wird ein entsprechendes Objekt mit dem erhaltenen Token erzeugt. Aus diesem Objekt können die Benutzerinformationen ausgelesen und für die Anwendung verwendet werden.<br></p>
<p><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">Ein wird nun ein weiterer Endpunkt zur Ausgabe dieser Daten hinzugefügt. Da alle Endpunkte bereits unter </span><code class="language-plain inline" style="font-weight: var( --e-global-typography-text-font-weight ); font-size: 18px;">/private</code><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );"> abgesichert sind, wird der neue Endpunkt unter &#8222;</span><a style="font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight ); background-color: #ffffff;" href="http://localhost:8080/private/user">http://localhost:8080/private/user</a><span style="color: var( --e-global-color-text ); font-family: var( --e-global-typography-text-font-family ), Sans-serif; font-weight: var( --e-global-typography-text-font-weight );">&#8220; abgelegt. Die Benutzerinformationen können über den Kontext geladen werden. Dabei ist zu beachten, dass eine Konvertierung in einen OAuth2User vorgenommen wird. Die Umwandlung eines Principal-Objekts in ein OAuth2User-Objekt in Spring Boot ist sinnvoll, wenn erweiterte Benutzerinformationen aus dem OAuth 2.0-Token benötigt werden. Das OAuth2User-Objekt stellt standardisierte Methoden für den Zugriff auf diese Informationen bereit und erleichtert somit eine einheitliche Verarbeitung von OAuth 2.0-basierten Benutzerdaten in Spring-Boot-Anwendungen. Nach der Umwandlung kann auf die Attribute des Benutzers zugegriffen werden. In diesem Fall werden Name, E-Mail-Adresse und alle zugehörigen Rollen ausgegeben.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-5b630ef elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="5b630ef" data-element_type="widget" data-widget_type="tabs.default">
				<div class="elementor-widget-container">
					<div class="elementor-tabs">
			<div class="elementor-tabs-wrapper" role="tablist" >
									<div id="elementor-tab-title-9581" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-9581" aria-expanded="false">./src/main/kotlin/.../DemoController.kt</div>
							</div>
			<div class="elementor-tabs-content-wrapper" role="tablist" aria-orientation="vertical">
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-9581" aria-expanded="false">./src/main/kotlin/.../DemoController.kt</div>
					<div id="elementor-tab-content-9581" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-9581" tabindex="0" hidden="false"><pre><code class="language-kotlin">...
@RequestMapping("/private/user")
    fun userEndpoint(): String {
        val user = SecurityContextHolder.getContext().authentication.principal as OAuth2User
        return "Name: ${user.attributes["name"]}, " +
            "Email: ${user.attributes["email"]}, " +
            "Roles: ${user.attributes["roles"]}"
    }
...</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-57361db elementor-widget elementor-widget-text-editor" data-id="57361db" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Die Anwendung kann neu gestartet und der neue Endpunkt getestet werden. Wird zum Endpunkt <code class="language-plain inline">/private/user</code> navigiert, findet eine erneute Authentifizierung statt. Möglicherweise geschieht dies automatisch, da das Cookie vom OAuth-Server noch gespeichert ist. Auf der Seite wird dann folgendes angezeigt:						</div>
				</div>
				<div class="elementor-element elementor-element-d2a8e52 elementor-widget elementor-widget-image" data-id="d2a8e52" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
										<figure class="wp-caption">
										<img loading="lazy" decoding="async" width="768" height="606" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-10.11.23-768x606.png" class="attachment-medium_large size-medium_large wp-image-2168" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-10.11.23-768x606.png 768w, https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-10.11.23-300x237.png 300w, https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-10.11.23-1024x808.png 1024w, https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-10.11.23.png 1061w" sizes="(max-width: 768px) 100vw, 768px" />											<figcaption class="widget-image-caption wp-caption-text">Ausgabe auf http://localhost:8080/private/user</figcaption>
										</figure>
							</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-f4bb66e elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="f4bb66e" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-f95c884" data-id="f95c884" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-7d593ee elementor-widget elementor-widget-spacer" data-id="7d593ee" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-b2e76de elementor-widget elementor-widget-heading" data-id="b2e76de" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Entwicklungsperspektiven</h2>		</div>
				</div>
				<section class="elementor-section elementor-inner-section elementor-element elementor-element-6a99a63 elementor-section-full_width elementor-section-content-top elementor-section-height-default elementor-section-height-default" data-id="6a99a63" data-element_type="section">
						<div class="elementor-container elementor-column-gap-no">
					<div class="elementor-column elementor-col-100 elementor-inner-column elementor-element elementor-element-4f32b32" data-id="4f32b32" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-0cba619 elementor-widget elementor-widget-text-editor" data-id="0cba619" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die Integration von Keycloak in Spring Boot ermöglicht eine effiziente und sichere Authentifizierung. Entwickler profitieren vom externen Authentifizierungsmanagement, können sich auf Kernfunktionen konzentrieren und Sicherheitsaspekte einfach implementieren. Die Flexibilität durch die Konfigurierbarkeit von Keycloak erlaubt die Anpassung an spezifische Anforderungen. Die Basis von Keycloak legt zudem den Grundstein für zukünftige Erweiterungen, ohne die Anwendungslogik stark verändern zu müssen. Die Flexibilität von Spring Boot ermöglicht es, auch andere OAuth 2.0 Server wie Azure, ADFS und mehr zu integrieren. Dadurch können Entwickler die Authentifizierungslösung an die spezifischen Anforderungen ihrer Anwendung anpassen.</p><p>Für weitere Details steht das <a href="https://github.com/inoteq/spring-boot-keycloak-oauth2" target="_blank" rel="noopener">GitHub Repository</a> zur Verfügung, das alle im Artikel beschriebenen Schritte und Konfigurationen enthält, einschließlich eines vorkonfigurierten Keycloak Containers mit Docker.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-4582ebd elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="4582ebd" data-element_type="section">
						<div class="elementor-container elementor-column-gap-default">
					<div class="elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-5c48a3f" data-id="5c48a3f" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-7c6e6ad elementor-widget elementor-widget-text-editor" data-id="7c6e6ad" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Eine detaillierte Beschreibung der Funktionsweise von OAuth 2.0 und dem Zusammenspiel der verschiedenen Komponenten findet sich in dem verlinkten Artikel.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-985898c elementor-grid-1 elementor-grid-tablet-1 elementor-posts--thumbnail-left elementor-hidden-tablet elementor-hidden-mobile elementor-grid-mobile-1 elementor-widget elementor-widget-posts" data-id="985898c" data-element_type="widget" data-settings="{&quot;classic_columns&quot;:&quot;1&quot;,&quot;classic_columns_tablet&quot;:&quot;1&quot;,&quot;classic_columns_mobile&quot;:&quot;1&quot;,&quot;classic_row_gap&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:35,&quot;sizes&quot;:[]},&quot;classic_row_gap_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;classic_row_gap_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}" data-widget_type="posts.classic">
				<div class="elementor-widget-container">
					<div class="elementor-posts-container elementor-posts elementor-posts--skin-classic elementor-grid">
				<article class="elementor-post elementor-grid-item post-1479 post type-post status-publish format-standard has-post-thumbnail hentry category-blog">
				<a class="elementor-post__thumbnail__link" href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" tabindex="-1" >
			<div class="elementor-post__thumbnail"><img loading="lazy" decoding="async" width="574" height="400" src="https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background.png" class="attachment-full size-full wp-image-1391" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background.png 574w, https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background-300x209.png 300w" sizes="(max-width: 574px) 100vw, 574px" /></div>
		</a>
				<div class="elementor-post__text">
				<p class="elementor-post__title">
			<a href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" >
				Sichern einer Spring Boot WebApp mit OAuth 2.0​			</a>
		</p>
				<div class="elementor-post__meta-data">
					<span class="elementor-post-author">
			Aymen Khalki		</span>
				<span class="elementor-post-date">
			6. Mai 2021		</span>
				</div>
				<div class="elementor-post__excerpt">
			<p>Authentifizierung ist der erste Schritt in jedem Sicherheitssystem. Es ist der Prozess mit dem die Identität eines Clients verifiziert wird. Ob der Client Zugriff auf bestimmte Ressourcen erhält, wird allerdings durch den Autorisierungsprozess festgestellt. In diesem Artikel werden wir eine API mit Spring Boot erstellen und das OAuth 2.0 Authentifizierungsprotokoll verwenden, um diese API abzusichern. Table of Contents Eingehende Erläuterungen und Definitionen OAuth 2.0 OAuth 2.0 (Open Authentication) ist ein</p>
		</div>
		
		<a class="elementor-post__read-more" href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" aria-label="Read more about Sichern einer Spring Boot WebApp mit OAuth 2.0​" tabindex="-1" >
			Weiterlesen »		</a>

				</div>
				</article>
				</div>
		
				</div>
				</div>
				<div class="elementor-element elementor-element-cdf4163 elementor-grid-1 elementor-grid-tablet-1 elementor-posts--thumbnail-left elementor-hidden-desktop elementor-hidden-mobile elementor-grid-mobile-1 elementor-widget elementor-widget-posts" data-id="cdf4163" data-element_type="widget" data-settings="{&quot;classic_columns&quot;:&quot;1&quot;,&quot;classic_columns_tablet&quot;:&quot;1&quot;,&quot;classic_columns_mobile&quot;:&quot;1&quot;,&quot;classic_row_gap&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:35,&quot;sizes&quot;:[]},&quot;classic_row_gap_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;classic_row_gap_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}" data-widget_type="posts.classic">
				<div class="elementor-widget-container">
					<div class="elementor-posts-container elementor-posts elementor-posts--skin-classic elementor-grid">
				<article class="elementor-post elementor-grid-item post-1479 post type-post status-publish format-standard has-post-thumbnail hentry category-blog">
				<a class="elementor-post__thumbnail__link" href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" tabindex="-1" >
			<div class="elementor-post__thumbnail"><img loading="lazy" decoding="async" width="300" height="209" src="https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background-300x209.png" class="attachment-medium size-medium wp-image-1391" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background-300x209.png 300w, https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background.png 574w" sizes="(max-width: 300px) 100vw, 300px" /></div>
		</a>
				<div class="elementor-post__text">
				<p class="elementor-post__title">
			<a href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" >
				Sichern einer Spring Boot WebApp mit OAuth 2.0​			</a>
		</p>
				<div class="elementor-post__meta-data">
					<span class="elementor-post-author">
			Aymen Khalki		</span>
				<span class="elementor-post-date">
			6. Mai 2021		</span>
				</div>
				<div class="elementor-post__excerpt">
			<p>Authentifizierung ist der erste Schritt in jedem Sicherheitssystem. Es ist der Prozess mit dem die Identität eines Clients verifiziert wird. Ob der Client Zugriff auf bestimmte Ressourcen erhält, wird allerdings durch den Autorisierungsprozess festgestellt. In</p>
		</div>
		
		<a class="elementor-post__read-more" href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" aria-label="Read more about Sichern einer Spring Boot WebApp mit OAuth 2.0​" tabindex="-1" >
			Weiterlesen »		</a>

				</div>
				</article>
				</div>
		
				</div>
				</div>
				<div class="elementor-element elementor-element-81ae1bc elementor-grid-1 elementor-grid-tablet-1 elementor-hidden-desktop elementor-hidden-tablet elementor-grid-mobile-1 elementor-posts--thumbnail-top elementor-widget elementor-widget-posts" data-id="81ae1bc" data-element_type="widget" data-settings="{&quot;classic_columns&quot;:&quot;1&quot;,&quot;classic_columns_tablet&quot;:&quot;1&quot;,&quot;classic_columns_mobile&quot;:&quot;1&quot;,&quot;classic_row_gap&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:35,&quot;sizes&quot;:[]},&quot;classic_row_gap_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;classic_row_gap_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}" data-widget_type="posts.classic">
				<div class="elementor-widget-container">
					<div class="elementor-posts-container elementor-posts elementor-posts--skin-classic elementor-grid">
				<article class="elementor-post elementor-grid-item post-1479 post type-post status-publish format-standard has-post-thumbnail hentry category-blog">
				<a class="elementor-post__thumbnail__link" href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" tabindex="-1" >
			<div class="elementor-post__thumbnail"><img loading="lazy" decoding="async" width="300" height="209" src="https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background-300x209.png" class="attachment-medium size-medium wp-image-1391" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background-300x209.png 300w, https://www.inoteq.com/wp-content/uploads/2021/04/inoteq-polygon-background.png 574w" sizes="(max-width: 300px) 100vw, 300px" /></div>
		</a>
				<div class="elementor-post__text">
				<p class="elementor-post__title">
			<a href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" >
				Sichern einer Spring Boot WebApp mit OAuth 2.0​			</a>
		</p>
				<div class="elementor-post__meta-data">
					<span class="elementor-post-author">
			Aymen Khalki		</span>
				<span class="elementor-post-date">
			6. Mai 2021		</span>
				</div>
				<div class="elementor-post__excerpt">
			<p>Authentifizierung ist der erste Schritt in jedem Sicherheitssystem. Es ist der Prozess mit dem die Identität eines Clients verifiziert wird. Ob der Client Zugriff auf bestimmte Ressourcen erhält, wird allerdings durch den Autorisierungsprozess festgestellt. In diesem Artikel werden wir eine API mit Spring Boot erstellen und das OAuth 2.0 Authentifizierungsprotokoll verwenden, um diese API abzusichern. Table of Contents Eingehende Erläuterungen und Definitionen OAuth 2.0 OAuth 2.0 (Open Authentication) ist ein von der IETF entwickeltes Standardprotokoll, das die Authentifizierung und Autorisierung von</p>
		</div>
		
		<a class="elementor-post__read-more" href="https://www.inoteq.com/2021/05/06/springboot-authentifizierung/" aria-label="Read more about Sichern einer Spring Boot WebApp mit OAuth 2.0​" tabindex="-1" >
			Weiterlesen »		</a>

				</div>
				</article>
				</div>
		
				</div>
				</div>
				<div class="elementor-element elementor-element-34f4387 elementor-widget elementor-widget-spacer" data-id="34f4387" data-element_type="widget" data-widget_type="spacer.default">
				<div class="elementor-widget-container">
					<div class="elementor-spacer">
			<div class="elementor-spacer-inner"></div>
		</div>
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				</div>
		<p>Der Beitrag <a href="https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/">Integration von OAuth 2.0 in Spring Boot mit Keycloak</a> erschien zuerst auf <a href="https://www.inoteq.com">INOTEQ GmbH</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.inoteq.com/2023/11/30/oauth2-keycloak-locally/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
