<?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>Spring Archive | INOTEQ GmbH</title>
	<atom:link href="https://www.inoteq.com/tag/spring/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.inoteq.com/tag/spring/</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>Spring Archive | INOTEQ GmbH</title>
	<link>https://www.inoteq.com/tag/spring/</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>Kotlin vs. Java im Praxischeck: Ein Spring Boot Erfahrungsbericht</title>
		<link>https://www.inoteq.com/2024/02/01/kotlin-vs-java-vor-und-nachteile-im-fokus-einer-spring-boot-anwendung/</link>
					<comments>https://www.inoteq.com/2024/02/01/kotlin-vs-java-vor-und-nachteile-im-fokus-einer-spring-boot-anwendung/#respond</comments>
		
		<dc:creator><![CDATA[Alexander Kusmin]]></dc:creator>
		<pubDate>Thu, 01 Feb 2024 14:16:59 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[Spring]]></category>
		<guid isPermaLink="false">https://www.inoteq.com/?p=2000</guid>

					<description><![CDATA[<p>Kürzlich habe ich für einen Kunden ein Fullstack Spring Boot Projekt entwickelt, bei dem ich eine gewisse Freiheit bei der Wahl der Technologie und der Programmiersprache hatte. Es stellte sich rasch die Frage, ob ich Java oder Kotlin verwenden sollte. Ich habe mich bewusst für Kotlin als Programmiersprache entschieden, wobei die Entscheidung nicht nur auf [&#8230;]</p>
<p>Der Beitrag <a href="https://www.inoteq.com/2024/02/01/kotlin-vs-java-vor-und-nachteile-im-fokus-einer-spring-boot-anwendung/">Kotlin vs. Java im Praxischeck: Ein Spring Boot Erfahrungsbericht</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="2000" class="elementor elementor-2000" data-elementor-post-type="post">
						<section class="elementor-section elementor-top-section elementor-element elementor-element-1088501 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="1088501" 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-2496c09" data-id="2496c09" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-48d72d0 elementor-widget elementor-widget-spacer" data-id="48d72d0" 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-9ba4411 elementor-widget elementor-widget-html" data-id="9ba4411" data-element_type="widget" data-widget_type="html.default">
				<div class="elementor-widget-container">
			<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>
<!-- Only for Code Snippet Styles -->		</div>
				</div>
				<div class="elementor-element elementor-element-789a6d3 elementor-widget elementor-widget-theme-post-title elementor-page-title elementor-widget-heading" data-id="789a6d3" data-element_type="widget" data-widget_type="theme-post-title.default">
				<div class="elementor-widget-container">
			<h1 class="elementor-heading-title elementor-size-default">Kotlin vs. Java im Praxischeck: Ein Spring Boot Erfahrungsbericht</h1>		</div>
				</div>
				<div class="elementor-element elementor-element-2dbccee elementor-widget elementor-widget-post-info" data-id="2dbccee" 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-db2a2ac elementor-inline-item" itemprop="datePublished">
						<a href="https://www.inoteq.com/2024/02/01/">
											<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 1, 2024					</span>
									</a>
				</li>
				<li class="elementor-icon-list-item elementor-repeater-item-7e68cc4 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-40c882e 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/java/" class="elementor-post-info__terms-list-item">Java</a>, <a href="https://www.inoteq.com/tag/kotlin/" class="elementor-post-info__terms-list-item">Kotlin</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-20a9deb elementor-widget elementor-widget-text-editor" data-id="20a9deb" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Kürzlich habe ich für einen Kunden ein Fullstack Spring Boot Projekt entwickelt, bei dem ich eine gewisse Freiheit bei der Wahl der Technologie und der Programmiersprache hatte. Es stellte sich rasch die Frage, ob ich Java oder Kotlin verwenden sollte. Ich habe mich bewusst für Kotlin als Programmiersprache entschieden, wobei die Entscheidung nicht nur auf technologischer Neugier basierte, sondern auch darauf, die Vor- und Nachteile von Kotlin im Vergleich zu Java in einem realen Projekt zu entdecken. In diesem Beitrag teile ich meine Erfahrungen und Erkenntnisse, während ich einen Blick auf die Unterschiede beider Sprachen werfe.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-5cc3eb0 elementor-widget elementor-widget-theme-post-featured-image elementor-widget-image" data-id="5cc3eb0" 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/wp3.png" class="attachment-full size-full wp-image-2919" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/wp3.png 2000w, https://www.inoteq.com/wp-content/uploads/2023/11/wp3-300x180.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/wp3-1024x614.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/wp3-768x461.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/wp3-1536x922.png 1536w" sizes="(max-width: 2000px) 100vw, 2000px" />											<figcaption class="widget-image-caption wp-caption-text">KI generiertes Symbolbild: compelling, illustration, symbolizing kotlin and java, logo-style, kotlin as a rocket, java as a locomotive</figcaption>
										</figure>
							</div>
				</div>
				<div class="elementor-element elementor-element-c273fac elementor-toc--minimized-on-tablet elementor-widget elementor-widget-table-of-contents" data-id="c273fac" 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__c273fac" 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__c273fac" 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__c273fac" 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-946ebcb elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="946ebcb" 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-a2c736d" data-id="a2c736d" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-e8d142e elementor-widget elementor-widget-spacer" data-id="e8d142e" 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-b5cfe4c elementor-widget elementor-widget-heading" data-id="b5cfe4c" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Grundlegende Unterschiede</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-af6dfcc elementor-widget elementor-widget-text-editor" data-id="af6dfcc" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Java und Kotlin werden beide in der Java Virtual Machine (JVM) ausgeführt, unterscheiden sich jedoch in Syntax, Semantik und Programmierparadigmen. Java wurde 1995 veröffentlicht und ist eine der bekanntesten und etabliertesten Programmiersprachen und folgt dem imperativen, objektorientierten Paradigma. Kotlin hingegen wurde 2011 gezielt entwickelt, um einige der Schwächen von Java zu beheben. </p><p>Die Syntax ist klarer, kompakter und fördert eine höhere Lesbarkeit des Codes. Ein wichtiger Aspekt ist die Reduzierung von Boilerplate-Code, damit sich Entwickler stärker auf die logischen Aspekte ihrer Anwendung konzentrieren können. </p><p>Ein Beispiel für die Vereinfachung der Syntax in Kotlin ist die Deklaration von Datenklassen. In Java müssen Entwickler viel Code für Getter, Setter, Equals, HashCode und ToString schreiben. In Kotlin kann dies mit nur einer Zeile erledigt werden, was zu einer saubereren und wartungsfreundlicheren Codebasis führt. In Java existieren mittlerweile ähnliche Lösungen, wie beispielsweise <a href="https://docs.oracle.com/en/java/javase/14/language/records.html" target="_blank" rel="noopener">Java Records</a>.</p><p>Hier ein Beispiel für den Unterschied von normalen Java-Klassen und Kotlin Daten-Klassen, orientiert nach dem Beispiel von <a href="https://www.baeldung.com/kotlin/data-classes" target="_blank" rel="noopener">Baeldung</a>.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-023c123 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="023c123" 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-2341" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-2341" aria-expanded="false">Java</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-2341" aria-expanded="false">Java</div>
					<div id="elementor-tab-content-2341" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-2341" tabindex="0" hidden="false"><pre><code class="language-java">public class Task {
    private int id;
    private String description;
    private int priority;

    public Task(int id, String description, int priority) {
        this.id = id;
        this.description = description;
        this.priority = priority;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    public float getPriority() {
        return priority;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = Integer.hashCode(this.id) * prime;
        result = prime * result + Integer.hashCode(this.priority);
        result = prime * result + ((this.description == null) ? 0 : this.description.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object var1) {
        if (this != var1) {
            if (var1 instanceof Task) {
                Task var2 = (Task) var1;
                if (this.id == var2.id
                  &amp;&amp; Intrinsics.areEqual(this.description, var2.description)
                  &amp;&amp; this.priority == var2.priority) {
                    return true;
                }
            }
            return false;
        } else {
            return true;
        }
    }

    @Override
    public String toString() {
        return "Task [id=" + id + ", description=" + description + ", priority=" + priority + "]";
    }
}</code></pre>
&nbsp;</div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-50ab037 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="50ab037" 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-8451" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-8451" aria-expanded="false">Kotlin</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-8451" aria-expanded="false">Kotlin</div>
					<div id="elementor-tab-content-8451" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-8451" tabindex="0" hidden="false"><pre><code class="language-kt">data class Task(
    var id: Int,
    var description: String,
    var priority: Int
)</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-8edbaf0 elementor-widget elementor-widget-text-editor" data-id="8edbaf0" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die Syntax von Kotlin ist in einigen Punkten einfacher und intuitiver. Beispielsweise werden Lambda-Ausdrücke als normale Funktionen definiert, während sie in Java als anonyme Klassen definiert werden.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-ef7da52 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="ef7da52" 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-2511" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-2511" aria-expanded="false">Java</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-2511" aria-expanded="false">Java</div>
					<div id="elementor-tab-content-2511" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-2511" tabindex="0" hidden="false"><pre><code class="language-java">public final String[] listOfNames = new String[]{"Alice", "Bob", "Mallory"}
listOfNames.forEach { name -> println(name) }</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-52ec52f elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="52ec52f" 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-8691" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-8691" aria-expanded="false">Kotlin</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-8691" aria-expanded="false">Kotlin</div>
					<div id="elementor-tab-content-8691" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-8691" tabindex="0" hidden="false"><pre><code class="language-kt">val listOfNames = listOf("Alice", "Bob", "Mallory")
listOfNames.forEach { println(it) }</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-a4a94de elementor-widget elementor-widget-text-editor" data-id="a4a94de" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die Semantik von Kotlin ist in einigen Punkten strenger als die von Java. Ein entscheidender Unterschied hier ist die Integration von Null-Sicherheit. Im Gegensatz zu Java, das das Arbeiten mit Null-Werten ohne explizite Behandlung erlaubt und häufig zu NullPointerExceptions führt, integriert Kotlin von Haus aus Null-Sicherheit, wodurch unerwünschte Null-Referenzen reduziert werden.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-b5cdd5d elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="b5cdd5d" 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-1901" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1901" aria-expanded="false">Java</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-1901" aria-expanded="false">Java</div>
					<div id="elementor-tab-content-1901" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1901" tabindex="0" hidden="false"><pre><code class="language-java">public final String name = null
System.out.println(name.length) // NullPointerException</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-9c82354 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="9c82354" 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-1641" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1641" aria-expanded="false">Kotlin</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-1641" aria-expanded="false">Kotlin</div>
					<div id="elementor-tab-content-1641" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1641" tabindex="0" hidden="false"><pre><code class="language-kt">val name: String? = null
println(name?.length) // null</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-28bac5b elementor-widget elementor-widget-text-editor" data-id="28bac5b" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Kotlin bietet eine umfangreiche Sammlung von Funktionen, die in Java nicht verfügbar sind. Zum Beispiel gibt es die Möglichkeit für Inline-Funktionen oder Erweiterungen von fremden Klassen mit eigenen Methoden. Auf weitere Unterschiede möchte ich hier nicht näher eingehen, diese können auf der <a href="https://kotlinlang.org/docs/comparison-to-java.html" target="_blank" rel="noopener">offiziellen Kotlin-Website</a> eingesehen werden.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-7df93fc elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="7df93fc" 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-e286ec7" data-id="e286ec7" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-c263e2e elementor-widget elementor-widget-spacer" data-id="c263e2e" 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-22b3ee2 elementor-widget elementor-widget-heading" data-id="22b3ee2" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Kotlin in der Industrie und Community</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-8dce1cb elementor-widget elementor-widget-text-editor" data-id="8dce1cb" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Kotlin ist äußerst relevant und hat in den letzten Jahren erheblich an Bedeutung gewonnen, insbesondere in der Android-Entwicklung. Gründe hierfür sind:</p><ul><li><b>Offizielle Google-Unterstützung</b><br />Google hat Kotlin als offizielle Programmiersprache für die Android-Entwicklung anerkannt. Seit 2017 ist Kotlin eine offizielle Sprache für die Entwicklung von Android-Apps und hat seitdem breite Akzeptanz gefunden.</li><li><b>Interoperabilität mit Java</b><br />Kotlin wurde entwickelt, um nahtlos mit Java zu interagieren. Dadurch können Entwickler bestehenden Java-Code in Kotlin integrieren und umgekehrt.</li><li><b>Moderne Sprachfunktionen</b><br />Kotlin bietet moderne Funktionen wie Null-Sicherheit, Erweiterungsfunktionen, Datenklassen und eine klare Syntax, die die Produktivität von Entwicklern verbessern. Daher ist Kotlin eine attraktive Alternative zu anderen JVM-Sprachen.</li><li><b>Community-Unterstützung</b><br />Die Kotlin-Community wächst kontinuierlich. Es gibt zahlreiche Ressourcen, Bibliotheken und Frameworks, die speziell für Kotlin entwickelt wurden. Die aktive Beteiligung der Entwicklergemeinschaft trägt zur Weiterentwicklung und Verbesserung der Sprache bei.</li><li><b>Branchenakzeptanz</b><br />Viele namhafte Unternehmen wie Google, Netflix, Uber und JetBrains setzen Kotlin erfolgreich in ihren Projekten ein. Dies zeigt, dass Kotlin nicht nur in der Entwicklergemeinschaft, sondern auch in der Industrie an Bedeutung gewinnt.</li><li><b>Plattformunabhängigkeit und -kompatibilität</b><br />Das Kotlin Multiplatform Project ermöglicht es, Code auf mehreren Plattformen zu kompilieren, z. B. auf Android, iOS, Web und Desktop. </li></ul><p> </p><p>Insgesamt ist Kotlin eine leistungsfähige Sprache, die für verschiedene Anwendungsbereiche geeignet ist.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-eddaa71 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="eddaa71" 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-1d05640" data-id="1d05640" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-bad4114 elementor-widget elementor-widget-spacer" data-id="bad4114" 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-a4a1dbf elementor-widget elementor-widget-heading" data-id="a4a1dbf" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Kotlin in der Praxis</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-801417e elementor-widget elementor-widget-text-editor" data-id="801417e" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die Entscheidung, welche Sprache in einem Projekt verwendet werden soll, hängt wesentlich von den Vorlieben des Entwicklers ab. In meinem Fullstack Spring Boot-Projekt mit Kotlin konnte ich diese Unterschiede aus erster Hand erleben und beurteilen, wie sie sich auf die Entwicklungsdynamik und Codequalität auswirken. </p><p>Um Kotlin zu verstehen, ist es hilfreich, die Herausforderungen zu betrachten, denen Entwickler in der Java-Entwicklung täglich gegenüberstanden. Kotlin ist keine Alternative zu Java, sondern eine moderne Ergänzung, die auf die Schwächen von Java eingeht und neue Ansätze für die Softwareentwicklung bietet. </p><p>Kotlin unterbricht nicht die Verbindung zur etablierten JVM-Welt. Die Zielgruppe für Kotlin umfasst verschiedene Entwicklerprofile und -situationen.</p><ul><li><b>Entwickler, die die Vorteile moderner Sprachen suchen</b><br />Kotlin bietet Funktionen wie Typinferenz, Erweiterungsfunktionen und Datenklassen, die die Entwicklung erleichtern und den Code kompakter und lesbarer machen. Entwickler, die moderne Programmierkonzepte bevorzugen, finden in Kotlin eine zeitgemäße Lösung.</li><li><b>Android Entwickler</b><br />Die offizielle Unterstützung von Kotlin durch Google hat dazu geführt, dass viele Android-Entwickler auf Kotlin umgestiegen sind. Die Sprache integriert sich nahtlos in die Android-Plattform und verbessert die Lesbarkeit und Produktivität.</li><li><b>Unternehmen, die Java-Code weiterentwickeln wollen</b><br />Kotlin ist so konzipiert, dass es mit vorhandenem Java-Code interoperabel ist. Dadurch können Unternehmen schrittweise von Java zu Kotlin wechseln, ohne ihre bestehende Codebase vollständig zu überarbeiten. Kotlin ist somit eine attraktive Option für Organisationen, die ihre Java-Anwendungen modernisieren möchten.</li></ul><p> </p><p>Die Vielseitigkeit von Kotlin macht es zu einer interessanten Wahl für verschiedene Entwicklungsszenarien, sodass ich mich für Kotlin entschied.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-ac7a31c elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="ac7a31c" 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-c9eca55" data-id="c9eca55" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-425662d elementor-widget elementor-widget-spacer" data-id="425662d" 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-4e3edb1 elementor-widget elementor-widget-heading" data-id="4e3edb1" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Gemeinsamkeiten und Interoperabilität</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-c00d015 elementor-widget elementor-widget-text-editor" data-id="c00d015" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Obwohl Kotlin und Java sich in Syntax und Designphilosophie unterscheiden, teilen sie einige grundlegende Gemeinsamkeiten, die ihre Interoperabilität und Integration ermöglichen.</p><ul><li><b>Lauffähigkeit auf der Java Virtual Machine (JVM)</b><br />Beide Sprachen werden in Bytecode übersetzt und sind daher auf der JVM ausführbar. Diese Kompatibilität ermöglicht es Entwicklern, Kotlin- und Java-Code in derselben Anwendung zu verwenden, was einen schrittweisen Übergang oder die gemeinsame Nutzung von Bibliotheken ermöglicht.</li><li><b>Interoperabilität</b><br />Kotlin ist so konzipiert, dass es nahtlos mit Java interagiert. Das bedeutet, dass bestehende Java-Bibliotheken ohne Probleme in Kotlin-Projekten verwendet werden können und umgekehrt. Dies erleichtert den Übergang von Java zu Kotlin und unterstützt eine schrittweise Migration.</li><li><b>Verwendung des gleichen Bytecodes</b><br />Da beide Sprachen denselben Bytecode erzeugen, können Entwickler von den Vorteilen beider Sprachen profitieren, ohne sich auf eine einzige festlegen zu müssen.</li><li><b>Entwicklungsumgebung und Build-Tools</b><br />Kotlin und Java können mit denselben Entwicklungsumgebungen und Build-Tools verwendet werden. Dies erleichtert die Integration von Kotlin in bestehende Entwicklungsworkflows.</li></ul><p> </p><p>Diese Gemeinsamkeiten erleichtern die Entscheidung für oder gegen Kotlin, insbesondere bei einer schrittweisen Einführung oder Integration in bestehende Java-Projekte.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-7cc80ee elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="7cc80ee" 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-bbc5203" data-id="bbc5203" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-b66f8ac elementor-widget elementor-widget-spacer" data-id="b66f8ac" 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-6639279 elementor-widget elementor-widget-heading" data-id="6639279" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Nachteile von Kotlin</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-4ad621e elementor-widget elementor-widget-text-editor" data-id="4ad621e" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Trotz der vielen Vorteile von Kotlin gibt es auch einige Nachteile, insbesondere im Kontext von Spring Boot. </p>
<p>Kotlin ist trotz seiner 13 Jahre eine noch relativ neue Sprache, was bedeutet, dass sie noch nicht so weit verbreitet ist wie Java. Es ist wichtig, dass Entwickler, die mit Kotlin arbeiten, Erfahrung mit der Sprache haben oder bereit sind, sie zu erlernen. Dies liegt daran, dass es in Kotlin einige Unterschiede zu Java gibt, die verstanden werden müssen. Zudem kann es zu Problemen bei der Suche nach Dokumentation, Bibliotheken und Unterstützung kommen. Beispielsweise gibt es für Java eine große Auswahl an Bibliotheken, die von einer großen Community von Entwicklern unterstützt werden. </p>
<p>Die Dokumentation von Kotlin erwies sich gelegentlich als weniger hilfreich, insbesondere bei komplexeren Themen oder spezifischen Spring-Anforderungen. Dies führte dazu, dass ich auf eine Kombination aus Trial-and-Error und der Suche nach alternativen Lösungen angewiesen war. Bei einigen Abhängigkeiten, wie zum Beispiel <a href="https://projectlombok.org/" target="_blank" rel="noopener">Lombok</a> oder <a href="https://site.mockito.org/" target="_blank" rel="noopener">Mockito</a> für Tests, traten Probleme auf, die nicht unmittelbar gelöst werden konnten. Die Anpassung an alternative Bibliotheken, wie <a href="https://mockk.io/" target="_blank" rel="noopener">Mockk</a> oder andere, wurde notwendig, um reibungslose Testläufe zu gewährleisten. </p>
<p>Eine der Hürden war die begrenzte Verfügbarkeit von Online-Ressourcen. Oftmals waren die vorhandenen Informationen veraltet oder bezogen sich auf veraltete Versionen, da Kotlin regelmäßig Updates erhält, welche die Sprache auch oft stark verändert. Dies erschwerte die Suche nach aktuellen Lösungen für spezifische Probleme im Spring-Ökosystem. </p>
<p>Kotlin-Code kann in einigen Fällen auch schwieriger zu debuggen sein als Java-Code. Beispielsweise kann ein NullPointerException in Kotlin schwieriger zu finden sein als in Java. Dies liegt daran, dass Kotlin eine Null-safe Sprache ist und daher NullPointerExceptions wesentlich seltener auftreten und im Code daher schwieriger zu finden sind.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-be00bd4 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="be00bd4" 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-856ca37" data-id="856ca37" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-11e9e7e elementor-widget elementor-widget-spacer" data-id="11e9e7e" 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-c722c41 elementor-widget elementor-widget-heading" data-id="c722c41" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Vorteile von Kotlin</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-548d262 elementor-widget elementor-widget-text-editor" data-id="548d262" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Trotz der genannten Herausforderungen bietet Kotlin zahlreiche Vorteile, die die Entscheidung für diese Sprache rechtfertigen. Ein herausragendes Merkmal von Kotlin ist die Möglichkeit, denselben Funktionalitätsgrad wie in Java mit weniger Code auszudrücken. Dadurch wird der Code lesbarer und wartbarer. </p><p>Die klare und prägnante Syntax, die Integration von Null-Sicherheit, sowie Erweiterungsfunktionen und Datenklassen ermöglichen eine effiziente und wartungsfreundliche Codebasis. Kotlin fördert eine moderne Herangehensweise an die Softwareentwicklung und verbessert die Lesbarkeit des Codes erheblich. </p><p>Die breite Akzeptanz und Unterstützung in der Entwicklergemeinschaft, insbesondere im Android-Bereich, unterstreichen die Relevanz und Zukunftsfähigkeit von Kotlin als Programmiersprache.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-bc6c1d6 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="bc6c1d6" 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-e5a55ad" data-id="e5a55ad" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-7c7095c elementor-widget elementor-widget-spacer" data-id="7c7095c" 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-3f00418 elementor-widget elementor-widget-heading" data-id="3f00418" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Kotlin im Spring Boot Projekt: Eine strategische Entscheidung</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-10e54fc elementor-widget elementor-widget-text-editor" data-id="10e54fc" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die Entscheidung, Kotlin für ein Spring Boot-Projekt zu wählen, basierte auf überzeugenden Faktoren, die zu einer verbesserten Entwicklungsqualität und Effizienz beitrugen. Die klare und prägnante Syntax von Kotlin trug erheblich zur Lesbarkeit des Codes bei. Durch den Einsatz von Kotlin konnten komplexe Java-Konstruktionen in einfachere und lesbarere Ausdrücke übersetzt werden. </p><p>Dank der statischen Typisierung von Kotlin kann der Compiler viele Fehler bereits während der Entwicklungszeit erkennen. Die Typinferenz von Kotlin ermöglicht es Entwicklern, sichereren Code zu schreiben, indem sie in vielen Fällen auf explizite Typdeklarationen verzichten können.</p><p>Die nahtlose Integration von Kotlin und Java ermöglicht einen schrittweisen Übergang und die Nutzung von bestehendem Java-Code. Besonders in einem Spring-Projekt, in dem viele Bibliotheken und Frameworks in Java geschrieben sind, ist dies von großer Bedeutung. Die Interoperabilität erleichtert die Migration zu Kotlin, ohne dass vorhandene Ressourcen verloren gehen.</p><p>Kotlin bietet außerdem Erweiterungsfunktionen, mit denen Entwickler bestehende Klassen um neue Funktionen erweitern können, ohne den Originalcode zu ändern. Dies trägt zur Modularität des Codes bei und kann auch die Performance optimieren, indem spezifische Funktionen effizienter implementiert werden.</p><p>Bei der Entscheidung sollten vor allem die individuellen Anforderungen berücksichtigt werden. Kotlin kann zum Beispiel eine gute Wahl sein für Anwendungen mit kurzen Entwicklungszeiten und -kosten, Anwendungen, die von Entwicklern mit Erfahrung in anderen objektorientierten Sprachen entwickelt werden, oder Anwendungen, die auf mehreren Plattformen ausgeführt werden sollen.</p><p>Insgesamt trugen diese Aspekte dazu bei, dass die Verwendung von Kotlin in einem Spring-Projekt zu einer angenehmeren und produktiveren Entwicklungszeit führte. Die Entscheidung für Kotlin war eine strategische Wahl, die die Vorteile moderner Sprachen mit der bewährten Stabilität von Spring kombinierte.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-5371329 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="5371329" 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-c66ec61" data-id="c66ec61" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-3b3e86d elementor-widget elementor-widget-spacer" data-id="3b3e86d" 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-63c5acb elementor-widget elementor-widget-heading" data-id="63c5acb" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Fazit und persönliche Meinung</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-a93a496 elementor-widget elementor-widget-text-editor" data-id="a93a496" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Kotlin hat sich in meinem Fullstack Spring Boot-Projekt als überzeugende Alternative zu Java erwiesen. Die klare Syntax, die Reduzierung von Boilerplate-Code, die Integration von Null-Sicherheit und die nahtlose Interoperabilität mit Java trugen dazu bei, die Entwicklungszeit zu optimieren und die Codequalität zu verbessern.</p><p>Die Entscheidung für Kotlin war nicht nur technologisch, sondern auch strategisch. Kotlin ist aufgrund seiner Vielseitigkeit und modernen Sprachfunktionen eine attraktive Option für verschiedene Entwicklungszenarien. Obwohl Java nach wie vor eine robuste Sprache ist, bietet Kotlin eine zeitgemäße Alternative, insbesondere für Entwickler, die effizienter arbeiten und die Vorteile moderner Programmierkonzepte nutzen möchten.</p><p>In der dynamischen Landschaft der Softwareentwicklung bleibt die Entscheidung zwischen Java und Kotlin letztlich eine Frage der spezifischen Anforderungen und Präferenzen. Kotlin hat sich jedoch als eine Sprache etabliert, die nicht nur die Schwächen von Java adressiert, sondern auch neue Maßstäbe in Bezug auf Effizienz und Lesbarkeit setzt.</p><p>Aus meiner persönlichen Perspektive war die Entscheidung, Kotlin für das Projekt zu wählen, durchweg positiv. Trotz des potenziellen Mangels an Online-Ressourcen und der anfänglichen Lernkurve empfand ich die Vorteile von Kotlin als überzeugend. Die moderne Sprachfunktionalität, die Reduzierung von Boilerplate-Code und die nahtlose Integration mit Java machten den Umstieg lohnenswert. Die Entscheidung, sich auf neue Erfahrungen einzulassen und die Vorteile von Kotlin zu nutzen, trug nicht nur zur erfolgreichen Umsetzung des Projekts bei, sondern erweiterte auch meinen persönlichen Horizont in Bezug auf moderne Programmiersprachen.</p>						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-cf22e49 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="cf22e49" 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-96a87ad" data-id="96a87ad" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-3019981 elementor-widget elementor-widget-spacer" data-id="3019981" 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/01/kotlin-vs-java-vor-und-nachteile-im-fokus-einer-spring-boot-anwendung/">Kotlin vs. Java im Praxischeck: Ein Spring Boot Erfahrungsbericht</a> erschien zuerst auf <a href="https://www.inoteq.com">INOTEQ GmbH</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.inoteq.com/2024/02/01/kotlin-vs-java-vor-und-nachteile-im-fokus-einer-spring-boot-anwendung/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Entwicklung einer Spring Boot-Anwendung mit Thymeleaf und Tailwind CSS</title>
		<link>https://www.inoteq.com/2023/12/15/entwicklung-einer-spring-boot-anwendung-mit-thymeleaf-und-tailwind-css/</link>
					<comments>https://www.inoteq.com/2023/12/15/entwicklung-einer-spring-boot-anwendung-mit-thymeleaf-und-tailwind-css/#respond</comments>
		
		<dc:creator><![CDATA[Alexander Kusmin]]></dc:creator>
		<pubDate>Fri, 15 Dec 2023 09:47:55 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Tailwind CSS]]></category>
		<category><![CDATA[Thymeleaf]]></category>
		<guid isPermaLink="false">https://www.inoteq.com/?p=1918</guid>

					<description><![CDATA[<p>Moderne Webentwicklung erfordert nicht nur eine robuste Backend-Infrastruktur, sondern auch leistungsstarke Werkzeuge für die Gestaltung ansprechender Benutzeroberflächen. Dieser Beitrag enthält eine Schritt-für-Schritt-Anleitung zur Entwicklung einer Web-Anwendung mit Spring Boot mit Thymeleaf und Tailwind CSS via Gradle-Node-Plugin. Diese Kombination ermöglicht die Implementierung dynamischer Inhalte sowie die Erstellung responsiver und modernen Benutzeroberflächen. Inhaltsverzeichnis Setup von Spring Boot Es [&#8230;]</p>
<p>Der Beitrag <a href="https://www.inoteq.com/2023/12/15/entwicklung-einer-spring-boot-anwendung-mit-thymeleaf-und-tailwind-css/">Entwicklung einer Spring Boot-Anwendung mit Thymeleaf und Tailwind CSS</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="1918" class="elementor elementor-1918" data-elementor-post-type="post">
						<section class="elementor-section elementor-top-section elementor-element elementor-element-f66b86b elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="f66b86b" 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-8621065" data-id="8621065" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-1519bd7 elementor-widget elementor-widget-spacer" data-id="1519bd7" 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-4077a37 elementor-widget elementor-widget-html" data-id="4077a37" 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-364bd83 elementor-widget elementor-widget-theme-post-title elementor-page-title elementor-widget-heading" data-id="364bd83" data-element_type="widget" data-widget_type="theme-post-title.default">
				<div class="elementor-widget-container">
			<h1 class="elementor-heading-title elementor-size-default">Entwicklung einer Spring Boot-Anwendung mit Thymeleaf und Tailwind CSS</h1>		</div>
				</div>
				<div class="elementor-element elementor-element-ee9bfad elementor-widget elementor-widget-post-info" data-id="ee9bfad" 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-251e497 elementor-inline-item" itemprop="datePublished">
						<a href="https://www.inoteq.com/2023/12/15/">
											<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">
										Dezember 15, 2023					</span>
									</a>
				</li>
				<li class="elementor-icon-list-item elementor-repeater-item-7b70c4e 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-42532e0 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/spring/" class="elementor-post-info__terms-list-item">Spring</a>, <a href="https://www.inoteq.com/tag/tailwind-css/" class="elementor-post-info__terms-list-item">Tailwind CSS</a>, <a href="https://www.inoteq.com/tag/thymeleaf/" class="elementor-post-info__terms-list-item">Thymeleaf</a>				</span>
					</span>
								</li>
				</ul>
				</div>
				</div>
				<div class="elementor-element elementor-element-f0d808c elementor-widget elementor-widget-text-editor" data-id="f0d808c" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Moderne Webentwicklung erfordert nicht nur eine robuste Backend-Infrastruktur, sondern auch leistungsstarke Werkzeuge für die Gestaltung ansprechender Benutzeroberflächen. Dieser Beitrag enthält eine Schritt-für-Schritt-Anleitung zur Entwicklung einer Web-Anwendung mit Spring Boot mit <a href="https://www.thymeleaf.org/">Thymeleaf</a> und <a href="https://tailwindcss.com/">Tailwind CSS</a> via <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 );"><a href="https://github.com/node-gradle/gradle-node-plugin">Gradle-Node-Plugin</a></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 );">. Diese Kombination ermöglicht die Implementierung dynamischer Inhalte sowie die Erstellung responsiver und modernen Benutzeroberflächen.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-f60e6cf elementor-widget elementor-widget-theme-post-featured-image elementor-widget-image" data-id="f60e6cf" data-element_type="widget" data-widget_type="theme-post-featured-image.default">
				<div class="elementor-widget-container">
										<figure class="wp-caption">
										<img loading="lazy" decoding="async" width="2000" height="1200" src="https://www.inoteq.com/wp-content/uploads/2023/11/wp2.png" class="attachment-full size-full wp-image-2921" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/11/wp2.png 2000w, https://www.inoteq.com/wp-content/uploads/2023/11/wp2-300x180.png 300w, https://www.inoteq.com/wp-content/uploads/2023/11/wp2-1024x614.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/11/wp2-768x461.png 768w, https://www.inoteq.com/wp-content/uploads/2023/11/wp2-1536x922.png 1536w" sizes="(max-width: 2000px) 100vw, 2000px" />											<figcaption class="widget-image-caption wp-caption-text">KI generiertes Symbolbild: compelling, light-hearted tone, illustration, logo-style, thymeleaf being painted, symbolizing tailwindcss design</figcaption>
										</figure>
							</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-922a717 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="922a717" 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-5858cc2" data-id="5858cc2" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-6ac10c4 elementor-toc--minimized-on-desktop elementor-widget elementor-widget-table-of-contents" data-id="6ac10c4" 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__6ac10c4" 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__6ac10c4" 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__6ac10c4" 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-9099f1f elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="9099f1f" 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-6b9b81a" data-id="6b9b81a" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-3b727c9 elementor-widget elementor-widget-spacer" data-id="3b727c9" 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-1c3df64 elementor-widget elementor-widget-heading" data-id="1c3df64" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Setup von Spring Boot</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-1ac048f elementor-widget elementor-widget-text-editor" data-id="1ac048f" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Es wird eine Spring Boot Anwendung mit folgender Konfiguration benötigt:
<ul>
 	<li>Spring Boot Version 3.2.0</li>
 	<li>Kotlin als Programmiersprache für Spring</li>
 	<li>Kotlin (Groovy) für das Build-Tool</li>
 	<li>Abhängigkeit <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>
</ul>
Diese Konfiguration kann <a href="https://start.spring.io/#!type=gradle-project&amp;language=kotlin&amp;platformVersion=3.2.1&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" target="_blank" rel="noopener">hier über den Spring Initializr</a> direkt heruntergeladen werden.

Mit dem Befehl <code class="language-bash inline">./gradlew bootRun</code> wird die Anwendung gestartet und anschließend unter der Adresse <a href="http://localhost:8080" target="_blank" rel="noopener">http://localhost:8080</a> erreichbar.						</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-05d6dc3 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="05d6dc3" 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-9bf3151" data-id="9bf3151" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-7d05a52 elementor-widget elementor-widget-spacer" data-id="7d05a52" 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-6211f0b elementor-widget elementor-widget-heading" data-id="6211f0b" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Integration von Thymeleaf</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-0a15ec7 elementor-widget elementor-widget-text-editor" data-id="0a15ec7" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<a href="https://www.thymeleaf.org/">Thymeleaf</a> ist eine leistungsfähige Template-Engine für die Webentwicklung und ermöglicht die Integration dynamischer Inhalte in Seiten.

In der Datei <strong><i>build.gradle</i></strong> wird die Thymeleaf-Abhängigkeit eingebunden:						</div>
				</div>
				<div class="elementor-element elementor-element-8e27b30 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="8e27b30" 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-1491" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1491" 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-1491" aria-expanded="false">./build.gradle</div>
					<div id="elementor-tab-content-1491" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1491" tabindex="0" hidden="false"><pre><code class="“language-groovy”">...
dependencies {
    ...

    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.0'
}
...</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-0e46026 elementor-widget elementor-widget-heading" data-id="0e46026" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Thymeleaf-Templates</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-85bc578 elementor-widget elementor-widget-text-editor" data-id="85bc578" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Zu Beginn wird im Ressourcenverzeichnis ein neues Thymeleaf-Template <em><strong>src/main/resources/templates/index.html</strong></em> erstellt. Dieses Template zeigt einen Text als Überschrift an, der mithilfe von Thymeleaf aus den Ressourcen geladen wird. Dazu legen wir im Ressourcenverzeichnis eine Datei <em><strong>src/main/resources/messages.properties</strong></em> an, in der die Texte definiert werden. An sich mag dies für unser Beispiel etwas umständlich sein, jedoch ermöglicht dieser Ansatz später eine einfachere Übersetzung der Anwendung, indem der Inhalt der <em><strong>messages.properties</strong></em> pro Sprache vorliegt.						</div>
				</div>
				<div class="elementor-element elementor-element-cdae251 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="cdae251" 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-2151" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-2151" aria-expanded="false">index.html</div>
									<div id="elementor-tab-title-2152" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="2" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2152" aria-expanded="false">messages.properties</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-2151" aria-expanded="false">index.html</div>
					<div id="elementor-tab-content-2151" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-2151" tabindex="0" hidden="false"><pre><code class="“language-html”">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Index&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1 th:text="#{myIndexText}"&gt;ignored&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</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-2152" aria-expanded="false">messages.properties</div>
					<div id="elementor-tab-content-2152" class="elementor-tab-content elementor-clearfix" data-tab="2" role="tabpanel" aria-labelledby="elementor-tab-title-2152" tabindex="0" hidden="hidden"><pre><code class="“language-properties”">myIndexText=This is index!</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-32f0f89 elementor-widget elementor-widget-text-editor" data-id="32f0f89" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Mit <code class="language-plain inline">th:</code> werden Thymeleaf-Anweisungen eingeleitet. In diesem Fall wird Thymeleaf angewiesen, den Text-Inhalt für das Element zu definieren. Mit <code class="language-plain inline">#{...}</code> werden Ressourcen über einen eindeutigen Schlüssel angefordert, hier der Text <code class="language-plain inline">This is index</code> mit dem Schlüssel <code  class="language-plain inline">myIndexText</code> aus der zuvor definierten <em><strong>messages.properties</strong></em>.

Auf der <a href="https://www.thymeleaf.org/documentation.html" target="_blank" rel="noopener">offiziellen Seite von Thymeleaf</a> sind alle Funktionen ausführlich und mit vielen Beispielen dokumentiert.						</div>
				</div>
				<div class="elementor-element elementor-element-c4eeac8 elementor-widget elementor-widget-heading" data-id="c4eeac8" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Spring Boot Controller</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-95cca38 elementor-widget elementor-widget-text-editor" data-id="95cca38" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Damit die Spring Boot Anwendung auch die Seite anzeigt, muss ein Controller erstellt werden, welcher das Template für den Endpunkt rendert.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-a785e3e elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="a785e3e" 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-1751" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1751" aria-expanded="false">./src/main/.../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-1751" aria-expanded="false">./src/main/.../DemoController.kt</div>
					<div id="elementor-tab-content-1751" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1751" tabindex="0" hidden="false"><pre><code class="“language-kotlin”">package com.example.demo

import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping

@Controller
@RequestMapping
class DemoController {
    @GetMapping("/")
    fun myIndexEndpoint(model: Model): String {
        return "index"
    }
}
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-6ed8b84 elementor-widget elementor-widget-image" data-id="6ed8b84" 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="373" src="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.17.41-2-1752x2048-1-768x373.png" class="attachment-medium_large size-medium_large wp-image-2631" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.17.41-2-1752x2048-1-768x373.png 768w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.17.41-2-1752x2048-1-300x146.png 300w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.17.41-2-1752x2048-1-1024x497.png 1024w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.17.41-2-1752x2048-1-1536x745.png 1536w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.17.41-2-1752x2048-1.png 1752w" sizes="(max-width: 768px) 100vw, 768px" />											<figcaption class="widget-image-caption wp-caption-text">Index-Seite auf http://localhost:8080</figcaption>
										</figure>
							</div>
				</div>
				<div class="elementor-element elementor-element-b48932f elementor-widget elementor-widget-heading" data-id="b48932f" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Erweiterung des Beispiels mit weiteren Thymeleaf Funktionen</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-c1e23e8 elementor-widget elementor-widget-text-editor" data-id="c1e23e8" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Die Seite unter <a href="http://localhost:8080" target="_blank" rel="noopener">http://localhost:8080</a> wird nun wie folgt erweitert:
<ul>
 	<li>Dem Template wird eine Liste von Strings übergeben. Diese Strings stellen weitere Endpunkte namens <code class="language-plain inline">/foo</code>, <code class="language-plain inline">/bar</code> und <code class="language-plain inline">/baz</code> dar.</li>
 	<li>Der bisherige Controller wird für die weiteren Endpunkte entsprechend erweitert.</li>
 	<li>Die Endpunkte zeigen wie zuvor die Index-Seite nur einen Text an. Es werden dafür Templates erstellt.</li>
 	<li>Das aktuelle Index-Template verweist auf diese Endpunkte mit einer Navigation.</li>
</ul>						</div>
				</div>
				<div class="elementor-element elementor-element-1935e38 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="1935e38" 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-2641" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-2641" aria-expanded="false">DemoController.kt</div>
									<div id="elementor-tab-title-2642" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="2" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2642" aria-expanded="false">messages.properties</div>
									<div id="elementor-tab-title-2643" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="3" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2643" aria-expanded="false">index.html</div>
									<div id="elementor-tab-title-2644" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="4" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2644" aria-expanded="false">foo.html</div>
									<div id="elementor-tab-title-2645" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="5" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2645" aria-expanded="false">bar.html</div>
									<div id="elementor-tab-title-2646" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="6" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2646" aria-expanded="false">baz.html</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-2641" aria-expanded="false">DemoController.kt</div>
					<div id="elementor-tab-content-2641" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-2641" tabindex="0" hidden="false"><pre><code class="“language-kotlin”">...
@Controller
@RequestMapping
class DemoController {
    @GetMapping("/")
    fun myIndexEndpoint(model: Model): String {
        model.addAttribute("myEndpoints", listOf("foo", "bar", "baz"))
        return "index"
    }

    @GetMapping("/foo")
    fun fooEndpoint(): String = "foo"

    @GetMapping("/bar")
    fun barEndpoint(): String = "bar"

    @GetMapping("/baz")
    fun bazEndpoint(): String = "baz"
}
</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-2642" aria-expanded="false">messages.properties</div>
					<div id="elementor-tab-content-2642" class="elementor-tab-content elementor-clearfix" data-tab="2" role="tabpanel" aria-labelledby="elementor-tab-title-2642" tabindex="0" hidden="hidden"><pre><code class=“language-properties”>myIndexText=This is index!
myNavigateText=Navigate to {0}
myFooText=This is foo!
myBarText=This is bar!
myBazText=This is baz!
</code></pre></div>
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="false" data-tab="3" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2643" aria-expanded="false">index.html</div>
					<div id="elementor-tab-content-2643" class="elementor-tab-content elementor-clearfix" data-tab="3" role="tabpanel" aria-labelledby="elementor-tab-title-2643" tabindex="0" hidden="hidden"><pre><code class="“language-html”">...
&lt;body&gt;
...
&lt;nav&gt;
    &lt;a th:each="endpoint: ${myEndpoints}"
       th:text="#{myNavigateText(${endpoint})}"
       th:href="@{/{endpoint}(endpoint=${endpoint})}"&gt;ignored&lt;/a&gt;
&lt;/nav&gt;
&lt;/body&gt;
...
</code></pre></div>
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="false" data-tab="4" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2644" aria-expanded="false">foo.html</div>
					<div id="elementor-tab-content-2644" class="elementor-tab-content elementor-clearfix" data-tab="4" role="tabpanel" aria-labelledby="elementor-tab-title-2644" tabindex="0" hidden="hidden"><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Foo&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1 th:text="#{myFooText}"&gt;ignored&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="false" data-tab="5" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2645" aria-expanded="false">bar.html</div>
					<div id="elementor-tab-content-2645" class="elementor-tab-content elementor-clearfix" data-tab="5" role="tabpanel" aria-labelledby="elementor-tab-title-2645" tabindex="0" hidden="hidden"><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Bar&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1 th:text="#{myBarText}"&gt;ignored&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="false" data-tab="6" role="tab" tabindex="-1" aria-controls="elementor-tab-content-2646" aria-expanded="false">baz.html</div>
					<div id="elementor-tab-content-2646" class="elementor-tab-content elementor-clearfix" data-tab="6" role="tabpanel" aria-labelledby="elementor-tab-title-2646" tabindex="0" hidden="hidden"><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Baz&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1 th:text="#{myBazText}"&gt;ignored&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-763986f elementor-widget elementor-widget-text-editor" data-id="763986f" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Unsere Anwendung sieht nun folgendermaßen aus:</p>						</div>
				</div>
				<div class="elementor-element elementor-element-cdf0ee7 elementor-widget elementor-widget-image-carousel" data-id="cdf0ee7" data-element_type="widget" data-settings="{&quot;slides_to_show&quot;:&quot;2&quot;,&quot;autoplay&quot;:&quot;no&quot;,&quot;infinite&quot;:&quot;no&quot;,&quot;slides_to_scroll&quot;:&quot;1&quot;,&quot;navigation&quot;:&quot;none&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 2"><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-15.17.41-768x391.png" alt="Index-Seite auf http://localhost:8080" /><figcaption class="elementor-image-carousel-caption">Index-Seite auf http://localhost:8080</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="2 von 2"><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-15.18.36-1-768x391.png" alt="Seite &quot;foo&quot; auf http://localhost:8080/foo, Seite &quot;bar&quot; und &quot;baz&quot; analog dazu" /><figcaption class="elementor-image-carousel-caption">Seite "foo" auf http://localhost:8080/foo, Seite "bar" und "baz" analog dazu</figcaption></figure></div>			</div>
							
									</div>
				</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-0035aa9 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="0035aa9" 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-bd27b30" data-id="bd27b30" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-c60ab59 elementor-widget elementor-widget-spacer" data-id="c60ab59" 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-2c14b80 elementor-widget elementor-widget-heading" data-id="2c14b80" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Styling mittels Tailwind CSS</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-a8e32e4 elementor-widget elementor-widget-text-editor" data-id="a8e32e4" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Die Integration von Tailwind CSS in die Spring Boot Anwendung bietet eine flexible und leistungsstarke Styling-Option für das Frontend. Dieses Kapitel beschreibt die Integration und Verwendung von Tailwind CSS.</p>						</div>
				</div>
				<div class="elementor-element elementor-element-13810a2 elementor-widget elementor-widget-heading" data-id="13810a2" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Verwendung des Gradle Plugin für Node und Konfiguration von Tailwind CSS<span style="color: var( --e-global-color-primary ); font-family: var( --e-global-typography-primary-font-family ), Sans-serif; font-size: 1.75rem; font-weight: var( --e-global-typography-primary-font-weight );"></span><br></h3>		</div>
				</div>
				<div class="elementor-element elementor-element-0f1455e elementor-widget elementor-widget-text-editor" data-id="0f1455e" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Für die Integration von Tailwind CSS in die Spring Boot Anwendung wird das <a href="https://github.com/node-gradle/gradle-node-plugin">Gradle Node Plugin</a> verwendet. Dies ermöglicht eine Verwaltung der Node.js-Abhängigkeiten wie z.B. <a href="https://tailwindcss.com/">Tailwind CSS</a> direkt aus Gradle heraus.

Um das Plugin in das Projekt einzubinden, muss die Gradle-Konfiguration folgendermaßen angepasst werden:
<ul>
 	<li>Wir fügen das Plugin <code class="language-plain inline">gradle-node-plugin</code> hinzu.</li>
 	<li>Es wird ein neuer Gradle-Task registriert, die den Tailwind CSS zur Generierung der <em><strong>output.css</strong></em> ausführt.</li>
 	<li>Die Gradle-Task <code class="language-plain inline">npmInstall</code> wird so angepasst, dass npm die benötigten Pakete selbständig installiert.</li>
 	<li>Die Gradle-Task für die Kompilierung mit Kotlin wird so angepasst, dass Tailwind CSS immer vor der Kompilierung ausgeführt wird.</li>
</ul>
Zusätzlich müssen die beiden Dateien <em><strong>tailwind.config.js</strong></em> und <em><strong>package.json</strong></em> entsprechend angelegt werden.						</div>
				</div>
				<div class="elementor-element elementor-element-79ffe51 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="79ffe51" 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-1271" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1271" aria-expanded="false">build.gradle</div>
									<div id="elementor-tab-title-1272" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="2" role="tab" tabindex="-1" aria-controls="elementor-tab-content-1272" aria-expanded="false">tailwind.config.js</div>
									<div id="elementor-tab-title-1273" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="3" role="tab" tabindex="-1" aria-controls="elementor-tab-content-1273" aria-expanded="false">package.json</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-1271" aria-expanded="false">build.gradle</div>
					<div id="elementor-tab-content-1271" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1271" tabindex="0" hidden="false"><pre><code class="“language-groovy”">...
plugins {
    ...
    // Gradle plugin for Node
    id 'com.github.node-gradle.node' version '7.0.1'
}
...
// Necessary for node-gradle plugin
node {
    download = true
}

// This task will run `tailwindcss` command and generate the output.css file from styles.css
tasks.register('tailwind', NpxTask) {
    command = 'tailwindcss'
    args = ['-i', './src/main/resources/static/styles.css', '-o', './src/main/resources/static/output.css']

    // This task depends on npmInstall task
    dependsOn(tasks.npmInstall)
}

// Make npmInstall task depend on npmSetup task and adds package.json as input
tasks.npmInstall {
    inputs.file('package.json')
    dependsOn(tasks.npmSetup)
}

tasks.withType(KotlinCompile).configureEach {
    ...
    // Update the output.css file every time after compiling Kotlin code
    dependsOn(tasks.tailwind)
}
...
</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-1272" aria-expanded="false">tailwind.config.js</div>
					<div id="elementor-tab-content-1272" class="elementor-tab-content elementor-clearfix" data-tab="2" role="tabpanel" aria-labelledby="elementor-tab-title-1272" tabindex="0" hidden="hidden"><pre><code class="“language-js”">/** @type {import('tailwindcss').Config} */
module.exports = {
    content: ["./src/**/*.{html,js}"],
}</code></pre></div>
									<div class="elementor-tab-title elementor-tab-mobile-title" aria-selected="false" data-tab="3" role="tab" tabindex="-1" aria-controls="elementor-tab-content-1273" aria-expanded="false">package.json</div>
					<div id="elementor-tab-content-1273" class="elementor-tab-content elementor-clearfix" data-tab="3" role="tabpanel" aria-labelledby="elementor-tab-title-1273" tabindex="0" hidden="hidden"><pre><code class="“language-json”">{
  "devDependencies": {
    "@tailwindcss/forms": "^0.5.7",
    "tailwindcss": "^3.3.6"
  }
}</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-7b6cfc5 elementor-widget elementor-widget-text-editor" data-id="7b6cfc5" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Der Gradle-Befehl <code class="language-bash inline">./gradlew tailwind</code> ist nun ausführbar und sollte bei korrekter Konfiguration folgende Ausgabe erzeugen:						</div>
				</div>
				<div class="elementor-element elementor-element-7a2e21c elementor-widget elementor-widget-image" data-id="7a2e21c" 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="356" src="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-15.28.01-768x356.png" class="attachment-medium_large size-medium_large wp-image-2637" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-15.28.01-768x356.png 768w, https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-15.28.01-300x139.png 300w, https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-15.28.01-1024x475.png 1024w, https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-15.28.01-1536x713.png 1536w, https://www.inoteq.com/wp-content/uploads/2024/01/Screenshot-2024-01-10-at-15.28.01.png 2026w" sizes="(max-width: 768px) 100vw, 768px" />											<figcaption class="widget-image-caption wp-caption-text">Ausgabe der Gradle-Task für Tailwind</figcaption>
										</figure>
							</div>
				</div>
				<div class="elementor-element elementor-element-d8419a9 elementor-widget elementor-widget-heading" data-id="d8419a9" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Verwendung von Tailwind CSS</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-8d940f7 elementor-widget elementor-widget-text-editor" data-id="8d940f7" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							Unser Beispiel wird nun mit Hilfe von Tailwind CSS erweitert. Dazu können die Tailwind CSS Klassen direkt im Template verwendet oder in der <em><b>styles.css</b></em> unter <strong><i>src/main/resources/static</i></strong> gesammelt werden. Die dort enthaltenen Klassen werden dann von Tailwind CSS in <em><b>output.css</b></em> gebündelt. Für dieses Beispiel wird auch die einfache Unterstützung von Responsive Design durch Tailwind CSS genutzt. So soll die Index-Seite auf einem kleineren Bildschirm die Menüpunkte zeilenweise auflisten, ansonsten spaltenweise. Wichtig ist, dass die <strong><em>output.css</em></strong> in den Thymeleaf-Templates korrekt verlinkt wird.						</div>
				</div>
				<div class="elementor-element elementor-element-a951778 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="a951778" 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-1771" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1771" aria-expanded="false">styles.css</div>
									<div id="elementor-tab-title-1772" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="false" data-tab="2" role="tab" tabindex="-1" aria-controls="elementor-tab-content-1772" aria-expanded="false">index.html</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-1771" aria-expanded="false">styles.css</div>
					<div id="elementor-tab-content-1771" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1771" tabindex="0" hidden="false"><pre><code class=“language-css”>@tailwind base;
@tailwind components;
@tailwind utilities;

body {
    @apply bg-gray-200 w-full p-4 flex flex-col gap-4 items-center justify-center;
}

.headline {
    @apply text-4xl font-extrabold leading-none tracking-tight;
}

.headline-white {
    @apply text-white;
}

.btn {
    @apply font-bold py-2 px-4 rounded;
}
.btn-blue {
    @apply bg-blue-500 text-white;
}
.btn-blue:hover {
    @apply bg-blue-700;
}
</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-1772" aria-expanded="false">index.html</div>
					<div id="elementor-tab-content-1772" class="elementor-tab-content elementor-clearfix" data-tab="2" role="tabpanel" aria-labelledby="elementor-tab-title-1772" tabindex="0" hidden="hidden"><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;Index&lt;/title&gt;
    &lt;link href=&quot;/output.css&quot;
          rel=&quot;stylesheet&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1 class=&quot;headline&quot;
    th:text=&quot;#{myIndexText}&quot;&gt;ignored&lt;/h1&gt;
&lt;nav class=&quot;flex flex-col sm:flex-row gap-1&quot;&gt;
    &lt;a class=&quot;btn btn-blue&quot;
       th:each=&quot;endpoint: ${myEndpoints}&quot;
       th:text=&quot;#{myNavigateText(${endpoint})}&quot;
       th:href=&quot;@{/{endpoint}(endpoint=${endpoint})}&quot;&gt;ignored&lt;/a&gt;
&lt;/nav&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
				<div class="elementor-element elementor-element-271304d elementor-widget elementor-widget-image-carousel" data-id="271304d" data-element_type="widget" data-settings="{&quot;slides_to_show&quot;:&quot;2&quot;,&quot;slides_to_scroll&quot;:&quot;1&quot;,&quot;navigation&quot;:&quot;none&quot;,&quot;autoplay&quot;:&quot;no&quot;,&quot;infinite&quot;:&quot;no&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 2"><figure class="swiper-slide-inner"><img decoding="async" class="swiper-slide-image" src="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.15.59-768x475.png" alt="Index-Seite für Anzeigen mit einer horizontalen Auflösung größer als oder gleich 640px." /><figcaption class="elementor-image-carousel-caption">Index-Seite für Anzeigen mit einer horizontalen Auflösung größer als oder gleich 640px.</figcaption></figure></div><div class="swiper-slide" role="group" aria-roledescription="slide" aria-label="2 von 2"><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-15.16.15-768x584.png" alt="Index-Seite für Anzeigen mit einer horizontalen Auflösung kleiner als 640px." /><figcaption class="elementor-image-carousel-caption">Index-Seite für Anzeigen mit einer horizontalen Auflösung kleiner als 640px.</figcaption></figure></div>			</div>
							
									</div>
				</div>
				</div>
				<section class="elementor-section elementor-inner-section elementor-element elementor-element-f1225ad elementor-section-full_width elementor-section-content-middle elementor-section-height-default elementor-section-height-default" data-id="f1225ad" 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-100da93" data-id="100da93" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-9282e45 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="9282e45" 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-1531" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1531" aria-expanded="false">foo.html</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-1531" aria-expanded="false">foo.html</div>
					<div id="elementor-tab-content-1531" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1531" tabindex="0" hidden="false"><pre><code class="“language-html”">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Foo&lt;/title&gt;
    &lt;link href="/output.css"
          rel="stylesheet"&gt;
&lt;/head&gt;
&lt;body class="bg-red-500"&gt;
&lt;h1 class="headline headline-white"
    th:text="#{myFooText}"&gt;ignored&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
					</div>
		</div>
				<div class="elementor-column elementor-col-33 elementor-inner-column elementor-element elementor-element-c717879" data-id="c717879" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-ea3be8f elementor-widget elementor-widget-spacer" data-id="ea3be8f" 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-c30791e elementor-widget elementor-widget-image" data-id="c30791e" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
													<img loading="lazy" decoding="async" width="768" height="898" src="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.22-768x898.png" class="attachment-medium_large size-medium_large wp-image-2258" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.22-768x898.png 768w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.22-257x300.png 257w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.22-876x1024.png 876w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.22-1314x1536.png 1314w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.22-1752x2048.png 1752w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.22.png 2024w" sizes="(max-width: 768px) 100vw, 768px" />													</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-inner-section elementor-element elementor-element-25fb55a elementor-section-full_width elementor-section-content-middle elementor-section-height-default elementor-section-height-default" data-id="25fb55a" 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-af1f7b6" data-id="af1f7b6" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-d9174fd elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="d9174fd" 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-2271" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-2271" aria-expanded="false">bar.html</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-2271" aria-expanded="false">bar.html</div>
					<div id="elementor-tab-content-2271" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-2271" tabindex="0" hidden="false"><pre><code class="“language-html”">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Bar&lt;/title&gt;
    &lt;link href="/output.css"
          rel="stylesheet"&gt;
&lt;/head&gt;
&lt;body class="bg-green-500"&gt;
&lt;h1 class="headline headline-white"
    th:text="#{myBarText}"&gt;ignored&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
					</div>
		</div>
				<div class="elementor-column elementor-col-33 elementor-inner-column elementor-element elementor-element-f66614c" data-id="f66614c" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-578ef69 elementor-widget elementor-widget-spacer" data-id="578ef69" 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-27b305f elementor-widget elementor-widget-image" data-id="27b305f" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
													<img loading="lazy" decoding="async" width="768" height="898" src="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.30-768x898.png" class="attachment-medium_large size-medium_large wp-image-2259" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.30-768x898.png 768w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.30-257x300.png 257w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.30-876x1024.png 876w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.30-1314x1536.png 1314w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.30-1752x2048.png 1752w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.30.png 2024w" sizes="(max-width: 768px) 100vw, 768px" />													</div>
				</div>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-inner-section elementor-element elementor-element-1a74e9a elementor-section-full_width elementor-section-content-middle elementor-section-height-default elementor-section-height-default" data-id="1a74e9a" 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-706dac0" data-id="706dac0" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-8b8b154 elementor-tabs-view-horizontal elementor-widget elementor-widget-tabs" data-id="8b8b154" 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-1461" class="elementor-tab-title elementor-tab-desktop-title" aria-selected="true" data-tab="1" role="tab" tabindex="0" aria-controls="elementor-tab-content-1461" aria-expanded="false">baz.html</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-1461" aria-expanded="false">baz.html</div>
					<div id="elementor-tab-content-1461" class="elementor-tab-content elementor-clearfix" data-tab="1" role="tabpanel" aria-labelledby="elementor-tab-title-1461" tabindex="0" hidden="false"><pre><code class="“language-html”">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;Baz&lt;/title&gt;
    &lt;link href=&quot;/output.css&quot;
          rel=&quot;stylesheet&quot;&gt;
&lt;/head&gt;
&lt;body class=&quot;bg-blue-500&quot;&gt;
&lt;h1 class=&quot;headline headline-white&quot;
    th:text=&quot;#{myBazText}&quot;&gt;ignored&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
							</div>
		</div>
				</div>
				</div>
					</div>
		</div>
				<div class="elementor-column elementor-col-33 elementor-inner-column elementor-element elementor-element-10285ae" data-id="10285ae" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-e571567 elementor-widget elementor-widget-spacer" data-id="e571567" 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-e0d3894 elementor-widget elementor-widget-image" data-id="e0d3894" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
													<img loading="lazy" decoding="async" width="768" height="898" src="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.43-768x898.png" class="attachment-medium_large size-medium_large wp-image-2260" alt="" srcset="https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.43-768x898.png 768w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.43-257x300.png 257w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.43-876x1024.png 876w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.43-1314x1536.png 1314w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.43-1752x2048.png 1752w, https://www.inoteq.com/wp-content/uploads/2023/12/Screenshot-2024-01-10-at-15.16.43.png 2024w" sizes="(max-width: 768px) 100vw, 768px" />													</div>
				</div>
					</div>
		</div>
					</div>
		</section>
					</div>
		</div>
					</div>
		</section>
				<section class="elementor-section elementor-top-section elementor-element elementor-element-2607b93 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="2607b93" 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-533c700" data-id="533c700" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-c5994da elementor-widget elementor-widget-spacer" data-id="c5994da" 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-30dc1d7 elementor-widget elementor-widget-heading" data-id="30dc1d7" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h2 class="elementor-heading-title elementor-size-default">Schlusswort</h2>		</div>
				</div>
				<div class="elementor-element elementor-element-96fd2c9 elementor-widget elementor-widget-text-editor" data-id="96fd2c9" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>Es ist wichtig anzumerken, dass dieser Ansatz, wie jeder andere auch, Vor- und Nachteile hat. Thymeleaf ermöglicht eine nahtlose Integration von Backend- und Frontend-Logik, könnte aber bei komplexeren Frontend-Anforderungen an seine Grenzen stoßen. Tailwind CSS bietet eine unübertroffene Flexibilität beim Styling, aber die umfangreichen Klassen können zu überladenem HTML-Code führen.</p><p>Für weitere Details steht das <a href="https://github.com/inoteq/spring-boot-thymeleaf-tailwind-css" target="_blank" rel="noopener">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-446d063 elementor-section-boxed elementor-section-height-default elementor-section-height-default" data-id="446d063" 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-2e6c8af" data-id="2e6c8af" data-element_type="column">
			<div class="elementor-widget-wrap elementor-element-populated">
						<div class="elementor-element elementor-element-d202c85 elementor-widget elementor-widget-spacer" data-id="d202c85" 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/12/15/entwicklung-einer-spring-boot-anwendung-mit-thymeleaf-und-tailwind-css/">Entwicklung einer Spring Boot-Anwendung mit Thymeleaf und Tailwind CSS</a> erschien zuerst auf <a href="https://www.inoteq.com">INOTEQ GmbH</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.inoteq.com/2023/12/15/entwicklung-einer-spring-boot-anwendung-mit-thymeleaf-und-tailwind-css/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 loading="lazy" 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>
