<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ulrich VACHON</title>
    <description>The latest articles on DEV Community by Ulrich VACHON (@ulrich).</description>
    <link>https://dev.to/ulrich</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F473359%2Fc08c9ba9-d3de-47cf-a09e-a8f0a1ee5a1d.jpg</url>
      <title>DEV Community: Ulrich VACHON</title>
      <link>https://dev.to/ulrich</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ulrich"/>
    <language>en</language>
    <item>
      <title>Playing with the embryonic connections in Java with the Foreign Function</title>
      <dc:creator>Ulrich VACHON</dc:creator>
      <pubDate>Sun, 02 Jun 2024 22:41:02 +0000</pubDate>
      <link>https://dev.to/ulrich/playing-with-the-embryonic-connections-in-java-with-the-foreign-function-24k7</link>
      <guid>https://dev.to/ulrich/playing-with-the-embryonic-connections-in-java-with-the-foreign-function-24k7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Today we will use the Foreign Function (FFI) in Java 22 to call a native code aims to initiate embryonic connections against any servers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡An example is ready to run on my github.com/ulrich space available in the repository : &lt;a href="https://github.com/ulrich/java-rawsocket"&gt;https://github.com/ulrich/java-rawsocket&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is embryonic connections ? 🎓
&lt;/h2&gt;

&lt;p&gt;We use embryonic connections capabilities when we want to initiate an aborted TCP three-way handshake for example. If you remember your Network courses at the Faculty (coucou Paris 8 ❤️) there are a couple of years (for me), you remember the following sequences while the TCP-IP tries to open a network connection :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1️⃣              SYN (seq 1000) -&amp;gt; |
2️⃣                                | &amp;lt;- SYN / ACK (seq 2000, ACK 1001)
3️⃣    ACK (seq 1001, ACK 2001) -&amp;gt; |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the process is achieved the hosts can talk together. If we don't need to establish a well formed connection between hosts, we can abort the sequence by sending a reset (RST) instruction at the end of the TCP three-way handshake, like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1️⃣              SYN (seq 1000) -&amp;gt; |
2️⃣                                | &amp;lt;- SYN / ACK (seq 2000, ACK 1001)
3️⃣                        RST -&amp;gt;  X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several reasons to create embryonic connections and from my side I had to implement this feature for a client there is some years...&lt;/p&gt;

&lt;p&gt;Anyway, if you want to explore Embryonic Connection go ahead in the dedicated Wikipedia page : &lt;a href="https://en.wikipedia.org/wiki/TCP_half-open"&gt;https://en.wikipedia.org/wiki/TCP_half-open&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Foreign Function in Java ? 🎓
&lt;/h2&gt;

&lt;p&gt;The best way to resume the FFI in Java is to mention the official definition from Oracle company : &lt;code&gt;The Foreign Function and Memory (FFM) API enables Java programs to interoperate with code and data outside the Java runtime.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In other terms, we want "to bend the game" and forgot the ancestor ways (JNI, JNA, JNR...) to use native code outside the Java Virtal Machine.&lt;/p&gt;

&lt;p&gt;The promise behind FFI is to ease the use cases when a Java needs to (down)call and (up)call a native program or library.&lt;/p&gt;

&lt;p&gt;This article is not an introduction about FFI and the best to learn about it is to follow the Oracle documentation here : &lt;a href="https://docs.oracle.com/en/java/javase/22/core/foreign-function-and-memory-api.html"&gt;https://docs.oracle.com/en/java/javase/22/core/foreign-function-and-memory-api.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  FFI for embryonic connection ? 🚀
&lt;/h2&gt;

&lt;p&gt;I think you've guessed why we need native program to establish an embryonic connection. Indeed, in Java we unfortunately don't have the ability to create low-level network packets. So the use of the &lt;strong&gt;netinet/tcp.h&lt;/strong&gt; library is reserved for lower-level languages such as C (our example) and so FFI comes to the rescue !&lt;/p&gt;

&lt;p&gt;Please deep dive in the SocketTester.java code with me 🔍&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public int run() throws Throwable {
    log.info("Running SocketTester for destination address {}:{}", destinationAddress, destinationPort);

1️⃣    try (Arena confinedArena = Arena.ofConfined()) {
        SymbolLookup symbolLookup =
                    SymbolLookup.libraryLookup(Config.getNativeLibraryFile(), confinedArena);

2️⃣        MemorySegment function =
                    symbolLookup.find(Config.getNativeFunctionName())
                            .orElseThrow(() -&amp;gt; new IllegalStateException("Unable to find the native function: " + Config.getNativeFunctionName()));

3️⃣       MethodHandle methodHandle = Linker.nativeLinker()
                .downcallHandle(
                            function,
                            Config.getNativeFunctionDescriptor());

4️⃣       return (int) methodHandle.invoke(
                    confinedArena.allocateFrom(sourceAddress),
                    confinedArena.allocateFrom(destinationAddress),
                    confinedArena.allocateFrom(ValueLayout.OfInt.JAVA_INT, destinationPort),
                    confinedArena.allocateFrom(ValueLayout.OfInt.JAVA_INT, readTimeout),
                    confinedArena.allocateFrom(ValueLayout.OfInt.JAVA_INT, writeTimeout));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the point 1️⃣ we declare that we want to use the restricted Arena only available for the thread which creates the Arena. We can compare Arena with a closed box responsible to control the lifecycle of native memory segments. For the moment FFI allows the following scopes : Global, Automatic, Confined and Shared. Please take a look into Javadoc : &lt;a href="https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/foreign/Arena.html"&gt;https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/foreign/Arena.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the point 2️⃣ we have to load the native library under the .so form.&lt;/p&gt;

&lt;p&gt;At the point 3️⃣ we initialize a downcall handler used to communicate from Java to Native code. FFI allows to use upcall handler stub which enable you to pass Java code as a function pointer to a foreign function.&lt;/p&gt;

&lt;p&gt;At the point 4️⃣ we invoke the native code by passing the expected parameters previously created by the different &lt;strong&gt;allocateFrom&lt;/strong&gt; methods.&lt;/p&gt;

&lt;p&gt;To test the embryonic connection with Foreign Function code, you can use the mentioned project upper.&lt;/p&gt;

&lt;p&gt;If all the process is successful, we will obtain the following trace in your terminal :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO (java)] Running SocketTester for destination address 127.0.0.1:8080
[INFO (native)] Selected source port number: 35940
[INFO (native)] TCP header sequence number: 272214228
[INFO (native)] Successfully sent 60 bytes SYN!
[INFO (native)] Received bytes: 40
[INFO (native)] Destination port: 35940
[INFO (native)] Successfully received 40 bytes
[INFO (native)] Received syn: 0, ack: 1, rst: 1
[INFO (native)] TCP header sequence number response: 0
[INFO (native)] TCP header ack sequence number response: 272214229
[INFO (native)] tcph-&amp;gt;syn: 0
[INFO (native)] tcph-&amp;gt;ack: 16777216
[INFO (native)] SYN ACK received -&amp;gt; Success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have a good day.&lt;/p&gt;

&lt;p&gt;Image par &lt;a href="https://pixabay.com/fr/users/jackmac34-483877/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1882817"&gt;jacqueline macou&lt;/a&gt; de &lt;a href="https://pixabay.com/fr//?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1882817"&gt;Pixabay&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>native</category>
      <category>foreignfunction</category>
    </item>
    <item>
      <title>Develop your Tomcat App with Docker Compose Watch</title>
      <dc:creator>Ulrich VACHON</dc:creator>
      <pubDate>Mon, 15 Apr 2024 23:35:37 +0000</pubDate>
      <link>https://dev.to/ulrich/develop-your-tomcat-app-with-docker-compose-watch-3608</link>
      <guid>https://dev.to/ulrich/develop-your-tomcat-app-with-docker-compose-watch-3608</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Today we will see how &lt;u&gt;Build&lt;/u&gt;, &lt;u&gt;Run&lt;/u&gt;, &lt;u&gt;Deploy&lt;/u&gt; and &lt;u&gt;Modify&lt;/u&gt; in immediate mode a simple Java App deployed on Tomcat via the &lt;strong&gt;Docker Compose Watch&lt;/strong&gt; feature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The purpose of this article is not to introduce the development of some Tomcat App via Spring or whatever platform but instead of that to explain how we can use the Docker's tools to make easier it.&lt;/p&gt;

&lt;p&gt;💡A Docker Compose stack is ready to run on my github.com/ulrich space available in the repository : &lt;a href="https://github.com/ulrich/develop-java-app-with-compose-watch"&gt;https://github.com/ulrich/develop-java-app-with-compose-watch&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Compose Watch in few words
&lt;/h3&gt;

&lt;p&gt;This tool has been released in the Docker Compose v2.22.0 by my favorite ❤️ team at Docker. Quickly when you use Compose during your developments, it can useful to synchronize your local changes with your deployed App running in his container. Before the Watch feature you were forced to use a workaround like Bind mount for getting a hot reloaded capability and it was OK. But in many cases in many technologies, it's not a good idea to work like this because for example in Java (in particular Java WAR App based model) technology, we don't want to deploy our App after a simple modification. You understood, a Java WAR App needs many resources which can slow down the inner loop development experience.&lt;/p&gt;

&lt;p&gt;Docker Compose Watch offers an another approach for developers by using of new properties allowing to synchronize with a fine-grained level each piece of your App. By the way, a couple of new commands (develop/watch/action) will help you to define what/when/how I have to synchronize one piece of code (file, directory, module...).&lt;/p&gt;

&lt;p&gt;You will find more information in the following source &lt;a href="https://www.docker.com/blog/announcing-docker-compose-watch-ga-release/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Craft a simple Java WAR App
&lt;/h3&gt;

&lt;p&gt;Add a simple Spring Boot app with the convenience dependencies.&lt;/p&gt;

&lt;p&gt;The best way, for generating a ready-to-run application in Java you can use the &lt;a href="https://start.spring.io/"&gt;Spring Boot Initializr&lt;/a&gt; tool.&lt;/p&gt;

&lt;p&gt;We will need to enroll the following dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spring-boot-starter-tomcat&lt;/li&gt;
&lt;li&gt;spring-boot-starter-web&lt;/li&gt;
&lt;li&gt;spring-boot-starter-thymeleaf&lt;/li&gt;
&lt;li&gt;spring-boot-starter-data-jpa&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These followings for this introduction will use Java 21 and Maven.&lt;/p&gt;

&lt;p&gt;There is nothing of special to be noted for this application which use the classic architecture from Java App. The data layer is fed by a pre-initialized Postgres Compose service.&lt;/p&gt;

&lt;p&gt;After running the Docker Compose services you will access to the &lt;code&gt;students.html&lt;/code&gt; page from the following URL : &lt;code&gt;http://localhost:8080/app-for-compose-watch-0.0.1-SNAPSHOT/students/all&lt;/code&gt; (figure 1)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckpt01gbi8g8ug7tag2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckpt01gbi8g8ug7tag2h.png" alt="Image description" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Fig. 1&lt;/center&gt;




&lt;h3&gt;
  
  
  Run the App with --watch flag
&lt;/h3&gt;

&lt;p&gt;In this introduction, we will see two ways to synchronize our application.&lt;/p&gt;

&lt;p&gt;For running the App with the Compose Watch enabled, we have to launch like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will build and run the App with the Compose Watch observer enabled (figure 2).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9ognmoov3u93yugecsm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9ognmoov3u93yugecsm.png" alt="Image description" width="623" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Fig. 2&lt;/center&gt;




&lt;p&gt;👋 This way to start the services is interesting because in this case, we ask to build, run and watch the expected service with the logs enabled in the console.&lt;/p&gt;

&lt;h4&gt;
  
  
   When I want to create or modify the Java code
&lt;/h4&gt;

&lt;p&gt;In Java WAR App based on Tomcat, the easiest way to test the inner-loop development is to instruct Compose Watch for rebuilding and deploying the App when a "witness" file has changed. The following Compose configuration allows this development round-trip :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;  app:
    image: app:latest
    build: ./
    develop:
      watch:
        - action: rebuild
          path: deploy.watch
    ports:
      - 8080:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The notable point to consider there is the path instruction used to trigger a rebuild action when the developer considers it. As I explained previously, we don't want to trigger a rebuild action for each Java file modifications, so we can consider that when we want to test our modifications, the developer have to update this spy file.&lt;/p&gt;

&lt;p&gt;For my part I use a IntelliJ Shell script which update the &lt;strong&gt;deploy.watch&lt;/strong&gt; file located in the root path with this kind of configuration (figure 3).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdey7g9cwvyblytvlqqtq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdey7g9cwvyblytvlqqtq.png" alt="Image description" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Fig. 3&lt;/center&gt;




&lt;p&gt;When the Deploy App command is triggered, we can see the Docker Compose Watch feature take the hand and making the expected actions (build, deploy and restart Tomcat). PTAL the following sample of logs after executing the command (figure 4).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fet73dif768yttcuwp8sp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fet73dif768yttcuwp8sp.png" alt="Image description" width="800" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Fig. 4&lt;/center&gt;




&lt;blockquote&gt;
&lt;p&gt;Practical and inexpensive!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd87a41fzg7qosd7w9vz4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd87a41fzg7qosd7w9vz4.gif" alt="Image description" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
   When I want to modify a HTML file
&lt;/h4&gt;

&lt;p&gt;The solution to synchronize not compiled sources like Thymeleaf files (for example) is to use the &lt;strong&gt;sync&lt;/strong&gt; action. When we use this feature the Docker Compose doesn't restart the App but only copy and past the files denoted by the couple path/targer. Let's take a look of the following configuration used to (hot) update the &lt;strong&gt;students.html&lt;/strong&gt; file located in &lt;code&gt;src/main/resources/templates/&lt;/code&gt; path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;  app:
    image: app:latest
    build: ./
    develop:
      watch:
        - action: rebuild
          path: deploy.watch
        - action: sync
          path: src/main/resources/templates/
          target: /usr/local/tomcat/webapps/app-for-compose-watch-0.0.1-SNAPSHOT/WEB-INF/classes/templates/
    ports:
      - 8080:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main difference with the previous declaration is the necessity to declare a target path.&lt;/p&gt;

&lt;p&gt;Please take a look to the following resource : &lt;a href="http://localhost:8080/app-for-compose-watch-0.0.1-SNAPSHOT/students/adults"&gt;/students/adults&lt;/a&gt; (figure 5).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuc7wop9qw2zg3xzua6ob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuc7wop9qw2zg3xzua6ob.png" alt="Image description" width="622" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Fig. 5&lt;/center&gt;




&lt;p&gt;If I modify the template by adding some information, the modification should instantly happened (figure 6).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjfndpr0jy116xyacp54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjfndpr0jy116xyacp54.png" alt="Image description" width="596" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Fig. 6&lt;/center&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qpxevx4ey7at5xormst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qpxevx4ey7at5xormst.png" alt="Image description" width="613" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Fig. 7&lt;/center&gt;




&lt;h3&gt;
  
  
   Conclusion
&lt;/h3&gt;

&lt;p&gt;Finally we can consider the Docker Compose Watch tool while developing a Java WAR App on Tomcat. Given that I think is a good alternative from the classical development stack like IntelliJ Plugin or others. All parts of the inner-loop developments can be make in the image itself and no need to install third party tooling.&lt;/p&gt;

&lt;p&gt;Have a good day.&lt;/p&gt;

&lt;p&gt;Image par &lt;a href="https://pixabay.com/fr/users/jackmac34-483877/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1882817"&gt;jacqueline macou&lt;/a&gt; de &lt;a href="https://pixabay.com/fr//?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1882817"&gt;Pixabay&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>java</category>
      <category>tomcat</category>
      <category>development</category>
    </item>
    <item>
      <title>Step-up authentication in Keycloak + Spring Boot</title>
      <dc:creator>Ulrich VACHON</dc:creator>
      <pubDate>Sat, 15 Apr 2023 15:07:23 +0000</pubDate>
      <link>https://dev.to/ulrich/step-up-authentication-in-keycloak-spring-boot-3g1m</link>
      <guid>https://dev.to/ulrich/step-up-authentication-in-keycloak-spring-boot-3g1m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Today I will show you how to set up a stepped-up authentication based on Keycloak and Spring Boot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically in a classical application, the security is often based on the login/password, API key, token, roles, URI pattern, etc... Sometimes one or all of these features are coupled together to implement a security policy worthy of the name. &lt;/p&gt;

&lt;p&gt;When you need to enhance the security for a particular action it could be difficult to decide which security feature employing to accomplish that. The OAuth2 specification proposes a simple solution named &lt;strong&gt;Step-up Authentication Challenge Protocol&lt;/strong&gt;, link of the RFC is &lt;a href="https://www.ietf.org/archive/id/draft-ietf-oauth-step-up-authn-challenge-12.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this paper I will show you how I implemented this feature in a Spring Boot app coupled at Keycloak.&lt;/p&gt;

&lt;p&gt;💡A Docker Compose stack is ready to run on my &lt;a href="https://github.com/ulrich" rel="noopener noreferrer"&gt;github.com/ulrich&lt;/a&gt; space available in the repository : &lt;a href="https://github.com/ulrich/stepup-authentication-with-keycloak-and-spring-boot" rel="noopener noreferrer"&gt;stepup-authentication-with-keycloak-and-spring-boot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wear your seat belt and follow me&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the backend
&lt;/h2&gt;

&lt;p&gt;Add a simple Spring boot app with the convenience dependencies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The best way to generate a Ready-to-run application in Java is to use the &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring boot initializr&lt;/a&gt; tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the way we need to enroll the following dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring Web&lt;/li&gt;
&lt;li&gt;Spring Security&lt;/li&gt;
&lt;li&gt;OAuth2 Client&lt;/li&gt;
&lt;li&gt;OAuth2 Server&lt;/li&gt;
&lt;li&gt;Lombok&lt;/li&gt;
&lt;li&gt;Testcontainers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These followings for this introduction will use Java 17 and Maven (of course).&lt;/p&gt;

&lt;h3&gt;
  
  
  The minimum implementation
&lt;/h3&gt;

&lt;p&gt;The reader should accept that this app doesn't fill the best practices expected for the Production environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plug the backend to Keycloak
&lt;/h2&gt;

&lt;p&gt;Obviously, we will use Docker and Compose to lead this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Docker Compose stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add the Dockerfile for the backend app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing to new for this Dockerfile, we just need to separate the build stage from the runtime stage.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Build Container&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;maven:3.8.5-openjdk-17-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app/&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;      apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; /src src&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;mvn clean package &lt;span class="nt"&gt;-DskipTests&lt;/span&gt;

&lt;span class="c"&gt;# Run Container&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; amazoncorretto:17&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/target/**.jar app.jar&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Add the Docker Compose services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like the previous Docker configuration the Compose file is a-by-the-book example used to run the services:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;keycloak&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;quay.io/keycloak/keycloak:21.0&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stepup-keycloak&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stepup-keycloak&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9080:8080&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;keycloak-net&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./keycloak/config:/opt/keycloak/data/import&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;KEYCLOAK_ADMIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
      &lt;span class="na"&gt;KEYCLOAK_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
      &lt;span class="na"&gt;KC_HOSTNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/keycloak/bin/kc.sh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;start-dev"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--import-realm"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stepup-backend&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stepup-backend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:8080&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;keycloak-net&lt;/span&gt;
    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;java"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Xms512m"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Xmx1g"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app.jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--debug"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;keycloak-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Resolve the Keycloak hostname in your local hosts file for Keycloak&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'&lt;/span&gt; stepup-keycloak
172.22.0.2
❯ &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/hosts
172.22.0.2  stepup-keycloak


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Resolve the Keycloak hostname in your local hosts file for the backend&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'&lt;/span&gt; stepup-backend
172.22.0.3
❯ &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/hosts
172.22.0.3  stepup-backend


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Create the Keycloak the stepped-up configuration realm
&lt;/h3&gt;

&lt;p&gt;The detailed configuration can be found in this the &lt;a href="https://github.com/ulrich/stepup-authentication-with-keycloak-and-spring-boot/blob/master/keycloak/config/stepup-realm.json" rel="noopener noreferrer"&gt;stepup-realm.json&lt;/a&gt;&lt;br&gt;
file, but we can highlight some points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client &lt;code&gt;user-client&lt;/code&gt; is mandated for delivering an &lt;code&gt;access token&lt;/code&gt; to the user,&lt;/li&gt;
&lt;li&gt;The frontend-url property has necessary to be set to &lt;a href="http://172.30.0.2:8080" rel="noopener noreferrer"&gt;http://172.30.0.2:8080&lt;/a&gt; for the issuer claim value.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Create the OAuth2 security layer
&lt;/h3&gt;

&lt;p&gt;Backend side we can use a simple but an effective configuration where the settings are spread both in&lt;br&gt;
the &lt;code&gt;application.yml&lt;/code&gt; and &lt;code&gt;SecurityConfiguration.java&lt;/code&gt; files. No more needed for the time.&lt;/p&gt;

&lt;p&gt;The following shows the main content of the files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;code&gt;application.yml&lt;/code&gt; file we need to declare to Spring Security where the OIDC configuration can be found.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;oauth2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resourceserver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;jwt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;client-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user-client&lt;/span&gt;
          &lt;span class="na"&gt;issuer-uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://stepup-keycloak:8080/realms/stepup&lt;/span&gt;
          &lt;span class="na"&gt;jwk-set-uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://stepup-keycloak:8080/realms/stepup/protocol/openid-connect/certs&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;code&gt;SecurityConfiguration.java&lt;/code&gt; file we need to describe a basic configuration indicates how to handle the
request.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;


&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableWebSecurity&lt;/span&gt;
&lt;span class="nd"&gt;@EnableMethodSecurity&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SecurityConfiguration&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SecurityFilterChain&lt;/span&gt; &lt;span class="nf"&gt;filterChain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrf&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;disable&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth2ResourceServer&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Test the security configuration
&lt;/h3&gt;

&lt;p&gt;At this point we can run the Compose stack:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can note that the client secret was generated during this documentation and located in the &lt;code&gt;stepup-realm.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Open a terminal and test the configuration with curl.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get a valid access token from Keycloak&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ &lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="s1"&gt;'http://stepup-keycloak:8080/realms/stepup/protocol/openid-connect/token'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/x-www-form-urlencoded'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'client_secret=6AurffbSrQ4yaGOl2TE7nvdveKwM2CB0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'client_id=user-client'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'grant_type=password'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'username=ulrich'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'password=ulrich'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .access_token&lt;span class="sb"&gt;`&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Test an authenticated access&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"http://stepup-backend:8080/user?email=foo@gmail.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;The expected result from the previous request should be:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

HTTP/1.1 200 
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age&lt;span class="o"&gt;=&lt;/span&gt;0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 10 Apr 2023 16:35:48 GMT

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;:&lt;span class="s2"&gt;"foo@gmail.com"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;                                         


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If we omit the &lt;code&gt;Authorization&lt;/code&gt; header we should have this kind of response:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

HTTP/1.1 401 
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Set-Cookie: &lt;span class="nv"&gt;JSESSIONID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21C630C8B2B38333DC81029DEBC2818E&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/&lt;span class="p"&gt;;&lt;/span&gt; HttpOnly
WWW-Authenticate: Bearer
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age&lt;span class="o"&gt;=&lt;/span&gt;0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Mon, 10 Apr 2023 19:54:26 GMT


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  The step-up implementation from app side
&lt;/h2&gt;

&lt;p&gt;It's time to add more securities in our app by using a step-up mechanism involves the add of a request interceptor. The&lt;br&gt;
request interceptor will should be triggered if any resource is marked by &lt;strong&gt;@StepupAuthentication&lt;/strong&gt; annotation.&lt;/p&gt;
&lt;h3&gt;
  
  
  The step-up components
&lt;/h3&gt;

&lt;p&gt;No caveat in the current implementation based on Spring, we just need to develop three components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;StepupAuthentication.java&lt;/code&gt; is the class that we will put on a stepped-up resource,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;StepupAuthenticationInterceptor.java&lt;/code&gt; is the implementation of the step-up mechanism. It is responsible for the ACR
validation by opening the jwt-token and check if the signed token contains the expected LOA value,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;StepupAuthenticationInterceptorConfig.java&lt;/code&gt; is the underlying code responsible to declare own interceptor.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The resource security enhancement
&lt;/h3&gt;

&lt;p&gt;All is ready to enhance the security and for the test we will put the &lt;code&gt;@StepupAuthentication&lt;/code&gt; on&lt;br&gt;
the &lt;code&gt;UserResource.delete&lt;/code&gt; resource.&lt;/p&gt;

&lt;p&gt;Just like that:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;


&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;produces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;UserResource&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="nd"&gt;@DeleteMapping&lt;/span&gt;
    &lt;span class="nd"&gt;@StepupAuthentication&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteUserByEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NO_CONTENT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  The step-up implementation from Keycloak side
&lt;/h2&gt;

&lt;p&gt;The Keycloak instance is ready to test in this tutorial, but we will brief take a look in the authentication flow where all work.&lt;/p&gt;
&lt;h3&gt;
  
  
  The overridden Browser flow
&lt;/h3&gt;

&lt;p&gt;The basic Browser flow doesn't allow to build a stepped-up flow, so I created the &lt;code&gt;browser-stepup-flow&lt;/code&gt; cloned from the &lt;code&gt;browser-flow&lt;/code&gt;. More details are available in the Keycloak configuration but just note the two flows, the first one for the default flow and the second for the step-up flow.&lt;/p&gt;

&lt;p&gt;The key here is that the user pass through with success the &lt;code&gt;Stepup authentication flow&lt;/code&gt; the ACR field will contain the&lt;br&gt;
expected LOA value (2 for my example).&lt;/p&gt;

&lt;p&gt;Remember, if the stepup authentication interceptor validates the ACR code, it will let the execution continue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0nps5ro5lyxjh8zvdrk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0nps5ro5lyxjh8zvdrk.png" alt="keycloak"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Test the step-up configuration
&lt;/h3&gt;

&lt;p&gt;Open a terminal and test the configuration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get a valid access token from Keycloak&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ &lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="s1"&gt;'http://stepup-keycloak:8080/realms/stepup/protocol/openid-connect/token'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/x-www-form-urlencoded'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'client_secret=6AurffbSrQ4yaGOl2TE7nvdveKwM2CB0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'client_id=user-client'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'grant_type=password'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'username=ulrich'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'password=ulrich'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .access_token&lt;span class="sb"&gt;`&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Step 1
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Call the delete user resource&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-XDELETE&lt;/span&gt; &lt;span class="s2"&gt;"http://stepup-backend:8080/user?email=foo@gmail.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Referer: http://localhost:8080"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;The expected result from the previous request should be:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

HTTP/1.1 412 
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age&lt;span class="o"&gt;=&lt;/span&gt;0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Content-Length: 253
Date: Fri, 14 Apr 2023 12:02:51 GMT

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;:&lt;span class="s2"&gt;"PRECONDITION_FAILED"&lt;/span&gt;,&lt;span class="s2"&gt;"authenticationUrl"&lt;/span&gt;:&lt;span class="s2"&gt;"http://stepup-keycloak:8080/realms/stepup/protocol/openid-connect/auth?client_id=user-client&amp;amp;redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&amp;amp;response_type=code&amp;amp;response_mode=query&amp;amp;scope=openid&amp;amp;acr_values=2"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Explanations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This response status available in the &lt;code&gt;status&lt;/code&gt; field means that the LOA (Level Of Access) value stored in ACR claim
field was not in the good level,&lt;/li&gt;
&lt;li&gt;The step-up mechanism built a validation URL for the end user available in the &lt;code&gt;authenticationUrl&lt;/code&gt; field.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 2
&lt;/h4&gt;

&lt;p&gt;To continue the authentication flow, the user needs to follow the given URL and provides to good password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbza7unvk1vxncdowaj5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbza7unvk1vxncdowaj5s.png" alt="stepup-login"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the password form is successfully passed, the Keycloak forwards the flow by using the given URI located in the &lt;code&gt;redirect_uri&lt;/code&gt; URL parameter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugb9306mqke55zqascka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugb9306mqke55zqascka.png" alt="authorization-code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3
&lt;/h4&gt;

&lt;p&gt;At this moment, the app needs to exchange the &lt;code&gt;authorization-code&lt;/code&gt; (located in the response header Location) for the &lt;code&gt;access-token&lt;/code&gt;. This is the final step where the app will receive the &lt;code&gt;access-token&lt;/code&gt; filled with the expected ACR claim value (2).&lt;/p&gt;

&lt;p&gt;Proceed to get the new access-token.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="s1"&gt;'http://stepup-keycloak:8080/realms/stepup/protocol/openid-connect/token'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/x-www-form-urlencoded'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'grant_type=authorization_code'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'client_id=user-client'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'client_secret=6AurffbSrQ4yaGOl2TE7nvdveKwM2CB0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'code=3125c7c3-b2a9-4c20-a2dd-e5bb14b16b6b.e4702561-58b5-44b2-b493-b30242eb1c71.d4b4ea5c-b02b-46d6-8859-5a0f0ed32287'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'redirect_uri=http://localhost:8080/'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .access_token&lt;span class="sb"&gt;`&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;The expected result from the previous request should be a new access-token with the ACR claim field updated:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4ckfip1toccrfhzr8ym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4ckfip1toccrfhzr8ym.png" alt="acr-updated"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4
&lt;/h4&gt;

&lt;p&gt;It's now possible to call the protected resources with the good level of accreditation.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-XDELETE&lt;/span&gt; &lt;span class="s2"&gt;"http://stepup-backend:8080/user?email=foo@gmail.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Referer: http://localhost:8080"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;The expected result from the previous request should be:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

HTTP/1.1 204 
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age&lt;span class="o"&gt;=&lt;/span&gt;0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Date: Fri, 14 Apr 2023 12:34:41 GMT


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The status code of the delete user action is 204.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Finally the set up of a stepped-up authentication solution is relatively easy with some knowledge of how to deal with ACR/LOA notions and obviously if you use a powerful tool like Spring. But you can without troubles employ another mechanism like Servlet Filter or whatever technologies even outside of Java. Moreover you can use a more powerful checker like OTP or else.&lt;/p&gt;

&lt;p&gt;Have a good day.&lt;/p&gt;

&lt;p&gt;Image par &lt;a href="https://pixabay.com/fr/users/jackmac34-483877/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1882817" rel="noopener noreferrer"&gt;jacqueline macou&lt;/a&gt; de &lt;a href="https://pixabay.com/fr//?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1882817" rel="noopener noreferrer"&gt;Pixabay&lt;/a&gt;&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>spring</category>
      <category>stepup</category>
      <category>security</category>
    </item>
    <item>
      <title>Simple health check for Keycloak</title>
      <dc:creator>Ulrich VACHON</dc:creator>
      <pubDate>Thu, 23 Jun 2022 14:26:58 +0000</pubDate>
      <link>https://dev.to/ulrich/simple-health-check-for-keycloak-259p</link>
      <guid>https://dev.to/ulrich/simple-health-check-for-keycloak-259p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Today we will see how to add a simple and not intrusive health check based on shell script for your Keycloak&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometime ago I did the (bad) experience to note the user sessions increase very faster without known reason on the main cluster.&lt;/p&gt;

&lt;p&gt;The result of this was a rise of the user sessions which keep busy the CPU because we reached the maximum of heap memory occupation. More of 50k user sessions have been created on a dedicated Keycloak client by a health check probe a bit chatty 😇&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lesson of the day, if you fine-tuned your token settings don't forget to login AND logout test users.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple probe
&lt;/h2&gt;

&lt;p&gt;The only prerequisite is to have &lt;code&gt;jq&lt;/code&gt; command available on the environment where the script runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;login_access&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type:application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"grant_type=password"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_id=admin-cli"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"username=alive"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"password=[REDACTED]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="s1"&gt;'https://keyclaok.company.com/auth/realms/[REALM]/protocol/openid-connect/token'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; .error &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$login_access&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$error&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"null"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Login successful for test user."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unable to login test user (&lt;/span&gt;&lt;span class="nv"&gt;$error&lt;/span&gt;&lt;span class="s2"&gt;)."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt;  &lt;span class="s1"&gt;'.access_token'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;login_access&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;refresh_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt;  &lt;span class="s1"&gt;'.refresh_token'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;login_access&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;logout_response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}'&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type:application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$access_token&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_id=[CLIENT_ID]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"refresh_token=&lt;/span&gt;&lt;span class="nv"&gt;$refresh_token&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="s1"&gt;'https://keycloak.company.com/auth/realms/[REALM]/protocol/openid-connect/logout'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$logout_response&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 204 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Logout successful for test user."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unable to logout test user (&lt;/span&gt;&lt;span class="nv"&gt;$logout_response&lt;/span&gt;&lt;span class="s2"&gt;)."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Le me try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/ulrich/aa04a793d54703998ecb015a0e2ff803"&gt;https://gist.github.com/ulrich/aa04a793d54703998ecb015a0e2ff803&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crédit photo : &lt;a href="https://pixabay.com/fr/users/jackmac34-483877/"&gt;https://pixabay.com/fr/users/jackmac34-483877/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Configure Keycloak by a CLI extension</title>
      <dc:creator>Ulrich VACHON</dc:creator>
      <pubDate>Mon, 30 May 2022 21:49:47 +0000</pubDate>
      <link>https://dev.to/ulrich/configure-keycloak-by-a-cli-extension-779</link>
      <guid>https://dev.to/ulrich/configure-keycloak-by-a-cli-extension-779</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Today I will show you how to configure a Keycloak property by command-line interface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The main advantage in the Keycloak world is the possibility to extend it. 🔧 Indeed, it exists several way to extend or manage this software and one of these is to use a CLI script.&lt;/p&gt;

&lt;p&gt;In this article we will change the value at the startup time of the global property named &lt;code&gt;staticMaxAge&lt;/code&gt; responsible of the caching for all assets of all themes. To be noted that we cannot configure this value by the UI but only by the standalone(-ha).xml file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search the node path of the property
&lt;/h2&gt;

&lt;p&gt;Whatever the selected Keycloak profile (standalone or cluster mode), we will find the expected node here :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server &amp;gt; profile &amp;gt; subsystem:keycloak-server &amp;gt; theme &amp;gt; staticMaxAge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the CLI script
&lt;/h2&gt;

&lt;p&gt;As soon as we have the node we have to translate it to JBoss script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;embed-server --server-config=standalone.xml

/subsystem=keycloak-server/theme=defaults/:write-attribute(name=staticMaxAge,value=3600)

quit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The first one line indicates to the engine the target profile,&lt;/li&gt;
&lt;li&gt;The second one line writes the expected value in the property (staticMaxAge).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📝 One little tip, you can use a environment variable to fill the property :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;embed-server --server-config=standalone.xml

/subsystem=keycloak-server/theme=defaults/:write-attribute(name=staticMaxAge,value="${env.KEYCLOAK_ADMIN_STATIC_MAX_AGE:3600})

quit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Push the CLI script in the right folder
&lt;/h2&gt;

&lt;p&gt;If you want to configure your instance at the startup time (the common case for the production environment) you have to push the script in the following path : &lt;code&gt;/opt/jboss/startup-scripts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is an example of my Docker 🐋 configuration :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN mkdir /opt/jboss/startup-scripts

ADD --chown=jboss:root cli/update_assets_cache.cli /opt/jboss/startup-scripts/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let me try it
&lt;/h2&gt;

&lt;p&gt;If you want a complete example with a Docker configuration, you can clone the repository &lt;a href="https://github.com/ulrich/keycloak-configuration-with-cli"&gt;https://github.com/ulrich/keycloak-configuration-with-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crédit photo : Vanille&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>cli</category>
    </item>
    <item>
      <title>Add Keycloak in legacy Spring app</title>
      <dc:creator>Ulrich VACHON</dc:creator>
      <pubDate>Mon, 23 May 2022 14:25:02 +0000</pubDate>
      <link>https://dev.to/ulrich/add-keycloak-in-legacy-spring-app-3ep8</link>
      <guid>https://dev.to/ulrich/add-keycloak-in-legacy-spring-app-3ep8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Today I will show you how I added Keycloak in existing Spring app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Often when we want to study some technology, it's easy to ignit the stack by running the corresponding &lt;strong&gt;hello-world&lt;/strong&gt; 🌎 sample from sources, documentation, blog post, hands-on...&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But as you say in the real life (project), the things are never straightforward. OK it said.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Thereby this article we'll discover an application which already being protected by an existing security layer but which needs to use Keycloak for the next generation of its controllers. So the challenge for us there is to do work together the existing security layer with the newer based on Keycloak.&lt;/p&gt;

&lt;h2&gt;
  
  
  The existing code
&lt;/h2&gt;

&lt;p&gt;For the demonstration we use a very simple security pattern based on the &lt;code&gt;Authorization&lt;/code&gt; header presence. You can imagine that this security being based on a custom JWT layer or anything else...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
@RequestMapping(value = "/api/v1/user")
public class UserControllerImpl {

    @GetMapping
    public ResponseEntity&amp;lt;User&amp;gt; getUser(@RequestHeader("Authorization") String authorization, String id) {
        var tokenOpt = Optional.ofNullable(authorization);

        if (tokenOpt.isEmpty()) {
            return ResponseEntity
                    .status(BAD_REQUEST)
                    .build();
        }
        var token = tokenOpt.get();

        if (!"authorization".equals(token)) {
            return ResponseEntity
                    .status(UNAUTHORIZED)
                    .build();
        }
        return ResponseEntity
                .ok(new User("1", "Ulrich"));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected I will call my controller with CURL command and the result shall be very simple to understand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ curl -H "Authorization: foo" --verbose http://localhost:9999/api/v1/user          
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
&amp;gt; GET /api/v1/user HTTP/1.1
&amp;gt; Host: localhost:9999
&amp;gt; User-Agent: curl/7.68.0
&amp;gt; Accept: */*
&amp;gt; Authorization: foo
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 401 
&amp;lt; Content-Length: 0
&amp;lt; Date: Sun, 22 May 2022 14:03:49 GMT
&amp;lt; 
* Connection #0 to host localhost left intact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add Keycloak security layer
&lt;/h2&gt;

&lt;p&gt;This part won't be detailed because it's only some configurations to add in your Maven project and Spring Security layer, but broadly we have to follow the steps :&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Spring Security feature
&lt;/h3&gt;

&lt;p&gt;This is the start of migration and for that we have to add some dependencies in our project as Spring Security configuration to bu use by the existing resources (/api/v1). &lt;/p&gt;

&lt;p&gt;Keep in mind that we don't want to substitute the existing security layer by Spring Security and Keycloak but we want to dedicate a Spring Security configuration to the existing resources. Nothing else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@ConditionalOnProperty(value = "app.keycloak.enabled", havingValue = "false", matchIfMissing = true)
public class LegacySecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(LegacySecurityConfig.class);

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        logger.info("Configuring the legacy security layer");

        httpSecurity.csrf().disable();
        httpSecurity.authorizeRequests().anyRequest().permitAll();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will test this configuration by a CURL command to check if the existing resource can be reached again and also search the entry log related to the initialization of this configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2022-05-22 16:31:18.102  INFO 34438 --- [main] c.r.s.config.LegacySecurityConfig : Configuring the legacy security layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's works👌&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Keycloak feature
&lt;/h3&gt;

&lt;p&gt;In this part of the migration I supposed that you have an Keycloak instance already set with a realm named &lt;code&gt;test&lt;/code&gt;. If this is not the case you can start an instance based on my project located in the GitHub project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Declare the KeycloakConfigResolver component
&lt;/h4&gt;

&lt;p&gt;The goal of this component is to create a &lt;code&gt;deployment&lt;/code&gt; in the Keycloak terminology. The deployment bean stores the configuration claimed by the combo Spring Security and Keycloak library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
@Component("keycloakConfigResolver")
public class SimpleKeycloakConfigResolver implements KeycloakConfigResolver {

    private final static Logger logger = LoggerFactory.getLogger(SimpleKeycloakConfigResolver.class);

    @Value("${app.keycloak.server_url}")
    private String keycloakServerUrl;

    @Value("${app.keycloak.config_ssl}")
    private String keycloakConfigSSL;

    @Value("${app.keycloak.config_realm}")
    private String keycloakConfigRealm;

    @Value("${app.keycloak.config_resource}")
    private String keycloakConfigResource;

    @Override
    public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
        logger.info("Configuring the Keycloak deployment");

        var adapterConfig = new AdapterConfig();

        adapterConfig.setBearerOnly(true);
        adapterConfig.setRealm(keycloakConfigRealm);
        adapterConfig.setSslRequired(keycloakConfigSSL);
        adapterConfig.setResource(keycloakConfigResource);
        adapterConfig.setAuthServerUrl(keycloakServerUrl);

        return KeycloakDeploymentBuilder
                .build(adapterConfig);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡As you can image we can have as much deployments as we want and by definition having a multitenant app capability.&lt;/p&gt;

&lt;h4&gt;
  
  
  Declare the KeycloakSecurityConfig component
&lt;/h4&gt;

&lt;p&gt;This component is the entry point of the Keycloak configuration and allows developer to customize the behavior of sub components able to manage several things as the requests, the CORS or CSRF configurations etc...&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;configure&lt;/code&gt; method is the place where we declare the Http security configuration and specially which one URL have to be handled by Keycloak.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@KeycloakConfiguration
@DependsOn("keycloakConfigResolver")
@ConditionalOnProperty(value = "app.keycloak.enabled", havingValue = "true")
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(LegacySecurityConfig.class);

    public static final String VALIDATING_BY_KEYCLOAK = "/api/v2/**";

    @Autowired
    public void authenticationProvider(AuthenticationManagerBuilder authenticationManagerBuilder) {
        authenticationManagerBuilder.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        logger.info("Configuring the Keycloak security layer");

        super.configure(httpSecurity);

        httpSecurity.cors();
        httpSecurity.csrf().disable();
        httpSecurity.authorizeRequests().antMatchers(VALIDATING_BY_KEYCLOAK).authenticated();
        httpSecurity.authorizeRequests().anyRequest().permitAll();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we take the time to test the global mechanism we see that configuration runs well and that the &lt;code&gt;/api/v2/user&lt;/code&gt; endpoit as well protected, awesome!&lt;/p&gt;

&lt;p&gt;🟢Here we use a valid access token for test :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ curl -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItNFZiQmhtN1pZaXlhckdSMjk0el8xRDIwYzA5Y2Vrc0V4ZHJmUW9ESnhNIn0.eyJleHAiOjE2NTMyNDAzMjcsImlhdCI6MTY1MzI0MDAyNywianRpIjoiMjJlN2E4NTgtMDJhYS00NzE4LTlkZGItMDUwMDkwMmEyN2NiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL3Rlc3QiLCJzdWIiOiI5OTZmMDU2My04OGExLTQwYjEtYTc2NS0xNmI1NDYyMjBjZWYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLCJzZXNzaW9uX3N0YXRlIjoiNGExZDcyODEtZjk5OC00NGQ0LTliYmQtZDRkNmM4ZjhhNDJlIiwiYWNyIjoiMSIsInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6IjRhMWQ3MjgxLWY5OTgtNDRkNC05YmJkLWQ0ZDZjOGY4YTQyZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoidWxyaWNoIn0.h26O4nyVIpYPIzN5ocgvtBCRHcjEGT9kCxkv5T0yniLeTYU2XE0GZp_dmmb383sPz1KNJPOZRCYqMOgRFBl-7l9yWbiW85CsJ9EBsKDsp6z1lgtg_hkH0VDi_yVgDcdtQQ5fr7RppeoOhvmlM39qYf4_H2dr4WvhnJnjqLRBDsxcEaFWB97W8CUG66Ng2qEaHpmsqwFP7tPOdkwjktY9iBHJzG85SqKYO6YHbMM6YvukT14EwH2lFI4l-LpSfq1kXCMjZ9M__YEBbYswHs9RXfubI_Myr1kwCxFkXiqG6JOlwrxP8-UIRqTu2BCmbyq5YeliDhzO0NZP0eMTsaAedw' --verbose http://localhost:9999/api/v2/user
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
&amp;gt; GET /api/v2/user HTTP/1.1
&amp;gt; Host: localhost:9999
&amp;gt; User-Agent: curl/7.68.0
&amp;gt; Accept: */*
&amp;gt; Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICItNFZiQmhtN1pZaXlhckdSMjk0el8xRDIwYzA5Y2Vrc0V4ZHJmUW9ESnhNIn0.eyJleHAiOjE2NTMyNDAzMjcsImlhdCI6MTY1MzI0MDAyNywianRpIjoiMjJlN2E4NTgtMDJhYS00NzE4LTlkZGItMDUwMDkwMmEyN2NiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL3Rlc3QiLCJzdWIiOiI5OTZmMDU2My04OGExLTQwYjEtYTc2NS0xNmI1NDYyMjBjZWYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLCJzZXNzaW9uX3N0YXRlIjoiNGExZDcyODEtZjk5OC00NGQ0LTliYmQtZDRkNmM4ZjhhNDJlIiwiYWNyIjoiMSIsInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6IjRhMWQ3MjgxLWY5OTgtNDRkNC05YmJkLWQ0ZDZjOGY4YTQyZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoidWxyaWNoIn0.h26O4nyVIpYPIzN5ocgvtBCRHcjEGT9kCxkv5T0yniLeTYU2XE0GZp_dmmb383sPz1KNJPOZRCYqMOgRFBl-7l9yWbiW85CsJ9EBsKDsp6z1lgtg_hkH0VDi_yVgDcdtQQ5fr7RppeoOhvmlM39qYf4_H2dr4WvhnJnjqLRBDsxcEaFWB97W8CUG66Ng2qEaHpmsqwFP7tPOdkwjktY9iBHJzG85SqKYO6YHbMM6YvukT14EwH2lFI4l-LpSfq1kXCMjZ9M__YEBbYswHs9RXfubI_Myr1kwCxFkXiqG6JOlwrxP8-UIRqTu2BCmbyq5YeliDhzO0NZP0eMTsaAedw
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 200 
&amp;lt; Vary: Origin
&amp;lt; Vary: Access-Control-Request-Method
&amp;lt; Vary: Access-Control-Request-Headers
&amp;lt; Set-Cookie: JSESSIONID=89CE06C8B841F99D3F161C4E3D1FE8A5; Path=/; HttpOnly
&amp;lt; X-Content-Type-Options: nosniff
&amp;lt; X-XSS-Protection: 1; mode=block
&amp;lt; Cache-Control: no-cache, no-store, max-age=0, must-revalidate
&amp;lt; Pragma: no-cache
&amp;lt; Expires: 0
&amp;lt; X-Frame-Options: DENY
&amp;lt; Content-Type: application/json
&amp;lt; Transfer-Encoding: chunked
&amp;lt; Date: Sun, 22 May 2022 17:21:07 GMT
&amp;lt; 
* Connection #0 to host localhost left intact
{"id":"1","name":"Ulrich"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔴Here we use an invalid access token for test :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ curl -H 'Authorization: MY_BEARER_TOKEN_IS_WRONG' --verbose http://localhost:9999/api/v2/user
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
&amp;gt; GET /api/v2/user HTTP/1.1
&amp;gt; Host: localhost:9999
&amp;gt; User-Agent: curl/7.68.0
&amp;gt; Accept: */*
&amp;gt; Authorization: MY_BEARER_TOKEN_IS_WRONG
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 401 
&amp;lt; Vary: Origin
&amp;lt; Vary: Access-Control-Request-Method
&amp;lt; Vary: Access-Control-Request-Headers
&amp;lt; WWW-Authenticate: Bearer realm="test"
&amp;lt; X-Content-Type-Options: nosniff
&amp;lt; X-XSS-Protection: 1; mode=block
&amp;lt; Cache-Control: no-cache, no-store, max-age=0, must-revalidate
&amp;lt; Pragma: no-cache
&amp;lt; Expires: 0
&amp;lt; X-Frame-Options: DENY
&amp;lt; WWW-Authenticate: Bearer realm="test"
&amp;lt; Content-Length: 0
&amp;lt; Date: Sun, 22 May 2022 17:24:50 GMT
&amp;lt; 
* Connection #0 to host localhost left intact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have to confirm that the previous &lt;code&gt;/api/v1/user&lt;/code&gt; resource works as before but unfortunately it's broken.&lt;/p&gt;

&lt;p&gt;🔴Here we can confirm something was broken :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ curl -H "Authorization: authorization" --verbose http://localhost:9999/api/v1/user
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
&amp;gt; GET /api/v1/user HTTP/1.1
&amp;gt; Host: localhost:9999
&amp;gt; User-Agent: curl/7.68.0
&amp;gt; Accept: */*
&amp;gt; Authorization: authorization
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 401 
&amp;lt; Vary: Origin
&amp;lt; Vary: Access-Control-Request-Method
&amp;lt; Vary: Access-Control-Request-Headers
&amp;lt; WWW-Authenticate: Bearer realm="test"
&amp;lt; X-Content-Type-Options: nosniff
&amp;lt; X-XSS-Protection: 1; mode=block
&amp;lt; Cache-Control: no-cache, no-store, max-age=0, must-revalidate
&amp;lt; Pragma: no-cache
&amp;lt; Expires: 0
&amp;lt; X-Frame-Options: DENY
&amp;lt; WWW-Authenticate: Bearer realm="test"
&amp;lt; Content-Length: 0
&amp;lt; Date: Sun, 22 May 2022 17:28:55 GMT
&amp;lt; 
* Connection #0 to host localhost left intact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Declare the KeycloakAuthenticationProcessingFilter component
&lt;/h4&gt;

&lt;p&gt;The reason behind the previous issue is related to the way of Spring Security interacts with the Keycloak library. Keycloak takes the hand for all incoming requests and we have to declare we don't want handle all requests but only the &lt;code&gt;/api/v2/user&lt;/code&gt; based requests.&lt;/p&gt;

&lt;p&gt;For that, we have only to override the &lt;code&gt;KeycloakAuthenticationProcessingFilter&lt;/code&gt; bean by adding a specific matcher which match only for the &lt;code&gt;/api/v2/*&lt;/code&gt; requests. Please take a look :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CustomKeycloakAuthenticationProcessingFilter extends KeycloakAuthenticationProcessingFilter {

    public CustomKeycloakAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager, new AntPathRequestMatcher(VALIDATING_BY_KEYCLOAK));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To achieve this overloading we have to improve the existing &lt;code&gt;KeycloakSecurityConfig&lt;/code&gt; bean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
@KeycloakConfiguration
@DependsOn("keycloakConfigResolver")
@ConditionalOnProperty(value = "app.keycloak.enabled", havingValue = "true")
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(LegacySecurityConfig.class);

    public static final String VALIDATING_BY_KEYCLOAK = "/api/v2/**";

    @Autowired
    public void authenticationProvider(AuthenticationManagerBuilder authenticationManagerBuilder) {
        authenticationManagerBuilder.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    @Override
    protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
        return new CustomKeycloakAuthenticationProcessingFilter(authenticationManagerBean());
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        logger.info("Configuring the Keycloak security layer");

        super.configure(httpSecurity);

        httpSecurity.cors();
        httpSecurity.csrf().disable();
        httpSecurity.authorizeRequests().antMatchers(VALIDATING_BY_KEYCLOAK).authenticated();
        httpSecurity.authorizeRequests().anyRequest().permitAll();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉Here we are testing the &lt;code&gt;/api/v1/user&lt;/code&gt; call with Keycloak enabled :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ curl -H "Authorization: authorization" --verbose http://localhost:9999/api/v1/user
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
&amp;gt; GET /api/v1/user HTTP/1.1
&amp;gt; Host: localhost:9999
&amp;gt; User-Agent: curl/7.68.0
&amp;gt; Accept: */*
&amp;gt; Authorization: authorization
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 200 
&amp;lt; Vary: Origin
&amp;lt; Vary: Access-Control-Request-Method
&amp;lt; Vary: Access-Control-Request-Headers
&amp;lt; X-Content-Type-Options: nosniff
&amp;lt; X-XSS-Protection: 1; mode=block
&amp;lt; Cache-Control: no-cache, no-store, max-age=0, must-revalidate
&amp;lt; Pragma: no-cache
&amp;lt; Expires: 0
&amp;lt; X-Frame-Options: DENY
&amp;lt; Content-Type: application/json
&amp;lt; Transfer-Encoding: chunked
&amp;lt; Date: Sun, 22 May 2022 17:57:56 GMT
&amp;lt; 
* Connection #0 to host localhost left intact
{"id":"1","name":"Ulrich"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let me try it
&lt;/h2&gt;

&lt;p&gt;If you want a complete Spring boot example with the Keycloak integration as expected in this article, you can clone the repository &lt;a href="https://github.com/ulrich/spring-legacy-and-keycloak"&gt;https://github.com/ulrich/spring-legacy-and-keycloak&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crédit photo : &lt;a href="https://pixabay.com/fr/users/jackmac34-483877/"&gt;https://pixabay.com/fr/users/jackmac34-483877/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>spring</category>
      <category>keycloak</category>
      <category>secutiry</category>
    </item>
    <item>
      <title>Automatically add CORS configuration to Spring controllers</title>
      <dc:creator>Ulrich VACHON</dc:creator>
      <pubDate>Fri, 20 May 2022 16:31:58 +0000</pubDate>
      <link>https://dev.to/ulrich/automatically-add-cors-configuration-to-spring-controllers-4n01</link>
      <guid>https://dev.to/ulrich/automatically-add-cors-configuration-to-spring-controllers-4n01</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;It could be useful to add with a less of code the expected CORS headers in your Spring boot configuration. Let's do this!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing"&gt;CORS&lt;/a&gt; (Cross-Origin Resource Sharing) is a mechanism that allows "to protect" your resources APIs from undesirated calls. By saying protect I mean restrict the resources to be requested by any domain outside the origin domain where the resources was served.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flag all resources with @CrossOrigin
&lt;/h2&gt;

&lt;p&gt;This is the only step required by the &lt;em&gt;configurer&lt;/em&gt; to claim the cross origin resource sharing. For example :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@CrossOrigin
@RestController
@RequestMapping(value = "/api/user")
public interface UserController { ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Declare the CORS Spring configurer
&lt;/h2&gt;

&lt;p&gt;This Spring bean is responsible of several things 🔎 The first one action is to scan your resources looking for the paths will should be protected. In this example we use the autowired bean &lt;code&gt;ApplicationContext&lt;/code&gt; as entry point in the Spring application context :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Autowired
public CorsConfig(ApplicationContext applicationContext) {
    corsControllers = Stream.of(applicationContext.getBeanNamesForAnnotation(CrossOrigin.class))
            .filter(Objects::nonNull)
            .map(bean -&amp;gt; applicationContext.findAnnotationOnBean(bean, RequestMapping.class))
            .filter(mapping -&amp;gt; Objects.nonNull(mapping) &amp;amp;&amp;amp; mapping.path().length &amp;gt; 0)
            .map(mapping -&amp;gt; mapping.path()[0])
            .peek(path -&amp;gt; log.info("Register CORS configuration for \"{}\".", path))
            .collect(toSet());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Please keep this code safe !&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second one action is to declare &lt;code&gt;WebMvcConfigurer&lt;/code&gt; bean will add the protected path in the &lt;a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/CorsRegistry.html"&gt;CORS Registry&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            for (String corsController : corsControllers) {
                registry.addMapping(corsController)
                        .allowedOriginPatterns(allowedCorsOrigin)
                        .allowedHeaders("Authorization", ...)
                        .allowedMethods("OPTIONS", "PUT", "POST", ...);
            }
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something interresting to note there is that we can externalize the declaration of allowed domains by Ant patterns in an array of String. The property &lt;strong&gt;allowedCorsOrigin&lt;/strong&gt; is a variable annoted by @Value (see the sample code).&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the Spring configuration with CURL
&lt;/h2&gt;

&lt;p&gt;Now we are able to test the configuration in a real Spring app.&lt;/p&gt;

&lt;p&gt;🟢Here we use a valid domain for test :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ curl -H "Origin: https://fr.reservoircode.net" --verbose http://localhost:9999/api/user
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
&amp;gt; GET /api/user HTTP/1.1
&amp;gt; Host: localhost:9999
&amp;gt; User-Agent: curl/7.68.0
&amp;gt; Accept: */*
&amp;gt; Origin: https://fr.reservoircode.net
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 200 
&amp;lt; Vary: Origin
&amp;lt; Vary: Access-Control-Request-Method
&amp;lt; Vary: Access-Control-Request-Headers
&amp;lt; Access-Control-Allow-Origin: https://fr.reservoircode.net
&amp;lt; Content-Type: application/json
&amp;lt; Transfer-Encoding: chunked
&amp;lt; Date: Fri, 20 May 2022 16:20:17 GMT
&amp;lt; 
* Connection #0 to host localhost left intact
{"id":"1","name":"Ulrich"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔴Here we use a invalid domain for test :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ curl -H "Origin: https://foo.com" --verbose http://localhost:9999/api/user       
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
&amp;gt; GET /api/user HTTP/1.1
&amp;gt; Host: localhost:9999
&amp;gt; User-Agent: curl/7.68.0
&amp;gt; Accept: */*
&amp;gt; Origin: https://foo.com
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 403 
&amp;lt; Vary: Origin
&amp;lt; Vary: Access-Control-Request-Method
&amp;lt; Vary: Access-Control-Request-Headers
&amp;lt; Transfer-Encoding: chunked
&amp;lt; Date: Fri, 20 May 2022 16:23:02 GMT
&amp;lt; 
* Connection #0 to host localhost left intact
Invalid CORS request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let me try it
&lt;/h2&gt;

&lt;p&gt;If you want a Spring boot example you can clone the repository &lt;a href="https://github.com/ulrich/spring-cors-autoconfig"&gt;https://github.com/ulrich/spring-cors-autoconfig&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crédit photo : &lt;a href="https://pixabay.com/fr/users/jackmac34-483877/"&gt;https://pixabay.com/fr/users/jackmac34-483877/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>spring</category>
      <category>cors</category>
    </item>
    <item>
      <title>Create and use a result captor in Mockito</title>
      <dc:creator>Ulrich VACHON</dc:creator>
      <pubDate>Fri, 13 May 2022 15:02:11 +0000</pubDate>
      <link>https://dev.to/ulrich/create-and-use-a-result-captor-in-mockito-1l9i</link>
      <guid>https://dev.to/ulrich/create-and-use-a-result-captor-in-mockito-1l9i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Today I will show you how to create a Mockito captor&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;a href="https://site.mockito.org/"&gt;Mockito&lt;/a&gt; it exists the possibilty to use &lt;a href="https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#21"&gt;ArgumentCaptor&lt;/a&gt; to allow developers to verify the arguments used during the call of mocked method, but not the result itself.&lt;br&gt;
Indeed, in the current release of Mockito it's not possible to capture it and my solution to do that is to build a ResultCaptor class which implements the Answer interface and generify it for more conveniance.&lt;/p&gt;

&lt;p&gt;Let's take a look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the ResultCaptor class
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static class ResultCaptor&amp;lt;T&amp;gt; implements Answer&amp;lt;T&amp;gt; {
    private T result = null;

    public T getResult() {
        return result;
    }

    @Override
    public T answer(InvocationOnMock invocationOnMock) throws Throwable {
        //noinspection unchecked
        result = (T) invocationOnMock.callRealMethod();
        return result;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this quite simple implementation we see that the ResultCaptor class implements the Answer interface which force to override the answer method. We use this "interceptor" to call the real method of spied bean and capture the result to store it in the current context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the ResultCaptor class
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ResultCaptor&amp;lt;ServiceResult&amp;gt; serviceResultCaptor = new ResultCaptor&amp;lt;&amp;gt;();
var message = new Message("id", "hello all!");
doAnswer(serviceResultCaptor).when(serviceSpy).sendMessage(message);
// do the call
var serviceResult = serviceResultCaptor.getResult();
assertThat(serviceResult).isNotNull();
assertThat(serviceResult.getWhatever()).isEqualTo("Whatever");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it !&lt;/p&gt;

&lt;h2&gt;
  
  
  Let me try it
&lt;/h2&gt;

&lt;p&gt;If you want a Spring boot example you can clone the repository &lt;a href="https://github.com/ulrich/mockito-result-captor-demo"&gt;github.com/ulrich/mockito-result-captor-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crédit photo : &lt;a href="https://pixabay.com/fr/users/jackmac34-483877/"&gt;https://pixabay.com/fr/users/jackmac34-483877/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mockito</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
