[JAVA] Try to work with Keycloak using Spring Security SAML (Spring 5)


Continuing from last year, this year I will write an Advent Calendar centered on the members of NRI Open Standia. Last year, we focused on Keycloak, but this year we will write in the frame of open source, not limited to Keycloak (although Keycloak will appear on the first day). Well then, thank you.

What I wanted to achieve this time

What are you guys making applications for these days? How do you make the certification? I think there are various options these days. The project requirements I was involved in this time were as follows (I think it is a common configuration in recent enterprise projects).

--Application development by Spring Framework 5 is assumed --Authentication information and attribute information are stored in the existing authentication server --The existing authentication server supports SAML, and SAML can be used for authentication linkage to the application.

So, inevitably, Spring Security SAML was selected as a candidate and verified. I would like to write about what I learned in this process. image.png

Security project configuration in Spring in the first place

Currently, Spring operates on 22 main projects, 2 community projects and 3 Attic projects. This time, the Spring Security SAML to be handled is being developed as a subproject in Spring Security, which is one of the main projects.

Spring Security SAML operation check

So, I'm going to introduce Spring Security SAML. As of this writing, 1.0.3 has been released as GA.

: information_source: GitHub and [Maven Repository](https://mvnrepository.com/ artifact / org.springframework.security.extensions / spring-security-saml2-core / 1.0.4.RELEASE), [Spring site post](https://spring.io/blog/2018/03/16/spring- security-saml-1-0-4-released) has announced the release of 1.0.4, but Spring Security SAML Official Site Since 1.0.3 is still like GA, we will proceed with verification with 1.0.3 this time.

You can install the library by adding the dependencies published above and building it, but this time we will run the sample application listed in the official guide and verify the operation.

Here is the official guide to Spring Security SAML.

There is no doubt that you should read the whole thing, but first of all, the minimum required part to operate the sample application is [4. Quick start guide](https://docs.spring.io/spring-security] -saml / docs / 1.0.x / reference / htmlsingle / index.html # chapter-quick-start). Below are the steps I took when I verified it. Please read as appropriate according to your environment. Also, idp.ssocircle.com, which is an IdP introduced in the official guide, seems to have a limited number of times you can log in with a free tier. Therefore, it is recommended to prepare and verify Keycloak or an IdP that will be built immediately (the following is also the verification procedure with Keycloak). image.png

Then, follow 4. Quick start guide I will continue to do.

  1. Quick start guide 4.1. Pre-requisites The prerequisite Java and Maven environments are as follows.

Java 1.6+ SDK Apache Maven

This time, we are verifying with the following version.

$ java -version
java version "1.8.0_181"
$ mvn -version
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T03:33:14+09:00)

4.2. Installation steps 4.2.1. Downloading sample application First, download the 1.0.3 GA version binary including the sample application from the following and extract it.

4.2.2. Configuration of IDP metadata The sample application is designed to dynamically acquire metadata from the IdP when the SP is started. Since Keycloak is used for IdP this time, specify the metadata acquisition URL provided by the IdP you use. This time, specify as follows to set up Keycloak on localhost using port 8180 with the realm name openstandia.


<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
    <!-- URL containing the metadata -->
        <value type="java.lang.String">http://localhost:8180/auth/realms/openstandia/protocol/saml/descriptor</value>
    <!-- Timeout for metadata loading in ms -->
        <value type="int">15000</value>
    <property name="parserPool" ref="parserPool"/>

4.2.3. Generation of SP metadata Modify as follows to specify the entityId to be used by yourself.


<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
    <property name="entityId" value="urn:test:hiroshiaida:yokohama"/>
    <property name="extendedMetadata">
        <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
            <property name="idpDiscoveryEnabled" value="true"/>

4.2.4. Compilation Package with the mvn command, but before that, modify some pom.xml. Despite pom.xml included in Spring Security SAML 1.0.3, there is a part where the description is 1.0.0, so correct it.


$ diff -u ../../spring-security-saml-1.0.3.RELEASE/sample/pom.xml pom.xml
--- ../../spring-security-saml-1.0.3.RELEASE/sample/pom.xml     2017-12-20 17:34:40.000000000 +0900
+++ pom.xml     2018-12-01 03:30:02.783266900 +0900
@@ -5,7 +5,7 @@
-  <version>1.0.0.RELEASE-SNAPSHOT</version>
+  <version>1.0.3.RELEASE-SNAPSHOT</version>
   <name>Spring Security SAML v2 sample webapp</name>
   <description>Spring Security SAML v2 sample webapp</description>
@@ -119,7 +119,7 @@
-      <version>1.0.0.RELEASE</version>
+      <version>1.0.3.RELEASE</version>

After making the above modifications, package with the following mvn command.

$ mvn package

4.2.5. Deployment Start the sample application on Tomcat with the following command.

$ mvn tomcat7:run

It will start successfully, but you will probably get an Exception like the one below.


- Error retrieving metadata from http://localhost:8180/auth/realms/openstandia/protocol/saml/descriptor
java.net.ConnectException: Connection refused: connect
        at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
        at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)

This error is as expected because Keycloak hasn't started yet, so proceed.

4.2.6. Uploading of SP metadata to the IDP

Download current SP metadata: Next, upload the SP metadata to the IdP (Keycloak). Spring Security SAML SP metadata can be obtained by directly accessing the URL below.

If spring_saml_metadata.xml` can be downloaded, the acquisition is successful.

Upload SP metadata to the IDP: Upload the metadata obtained above to Keycloak. For Keycloak, you can easily add SAML settings by following the steps below (Keycloak is described assuming that it is in Japanese).

  1. Log in as an administrator
  2. Click "Client" in the settings, then click "Create" on the right edge of the screen.
  3. "Select a file" next to "Import" and specify spring_saml_metadata.xml and upload it.
  4. Click "Save"

(Screens for implementation of 2. and 3. above) image.png

Basic settings related to SAML connection can be done above, but it is necessary to change Keycloak settings according to cooperation requirements such as user attribute mapping. The Keycloak settings and key settings used in this verification are shown in the capture at the end of this article. Please refer to it as appropriate.

4.3. Testing single sign-on and single logout Since the settings on the IdP side have been completed, restart the SP side and check the operation of single sign-on and single log-out. If the screen transition is successful as shown below, it is operating normally.

Go to http: // localhost: 8080 / spring-security-saml2-sample /. The Spring Security SAML sample application top screen is displayed. image.png After selecting the check box of http: // localhost: 8180 / auth / realms / openstandia, press "Start single sign -on". The Keycloak login screen is displayed. image.png After entering the ID / password, press the login button. After authentication, the Spring Security SAML sample application login screen will be displayed automatically. image.png Click "Metadata Administration". The login screen for the Spring Security SAML sample application Administrator is displayed. image.png After entering the ID / password, press the login button. After authentication, the Spring Security SAML sample application Administrator login screen will be displayed. image.png

Spring Security SAML Spring 5 support status

Up to this point, we have confirmed the operation of the Spring Security SAML sample application. I mentioned it briefly in the first "What I wanted to achieve", but this time, the original requirement was to run Spring Security SAML on Spring Framework 5. In fact, Spring Security SAML is based on Spring Framework 3 by the method described in the above procedure. You can see this by looking at the mvn command or the packaged war.


$ mvn dependency:tree
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ spring-security-saml2-sample ---
[INFO] org.springframework.security.extensions:spring-security-saml2-sample:war:1.0.3.RELEASE-SNAPSHOT
[INFO] +- javax.servlet:jstl:jar:1.2:compile
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.3:compile
[INFO] |  +- org.slf4j:slf4j-api:jar:1.6.3:compile
[INFO] |  \- log4j:log4j:jar:1.2.16:compile
[INFO] +- org.springframework.security.extensions:spring-security-saml2-core:jar:1.0.3.RELEASE:compile
[INFO] |  +- org.opensaml:opensaml:jar:2.6.1:compile
[INFO] |  |  +- org.opensaml:openws:jar:1.5.1:compile
[INFO] |  |  |  +- org.opensaml:xmltooling:jar:1.4.1:compile
[INFO] |  |  |  |  +- org.bouncycastle:bcprov-jdk15:jar:1.46:compile
[INFO] |  |  |  |  \- ca.juliusdavies:not-yet-commons-ssl:jar:0.3.9:compile
[INFO] |  |  |  +- commons-httpclient:commons-httpclient:jar:3.1:compile
[INFO] |  |  |  \- org.apache.santuario:xmlsec:jar:1.5.6:compile
[INFO] |  |  +- commons-codec:commons-codec:jar:1.7:compile
[INFO] |  |  +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] |  |  +- commons-lang:commons-lang:jar:2.6:compile
[INFO] |  |  +- org.apache.velocity:velocity:jar:1.7:compile
[INFO] |  |  +- org.owasp.esapi:esapi:jar:2.0.1:compile
[INFO] |  |  +- joda-time:joda-time:jar:2.2:compile
[INFO] |  |  +- xerces:xercesImpl:jar:2.10.0:runtime
[INFO] |  |  +- xalan:serializer:jar:2.7.1:runtime
[INFO] |  |  +- xml-resolver:xml-resolver:jar:1.2:runtime
[INFO] |  |  \- xalan:xalan:jar:2.7.1:runtime
[INFO] |  +- org.springframework.security:spring-security-core:jar:3.1.2.RELEASE:compile
[INFO] |  +- org.springframework.security:spring-security-web:jar:3.1.2.RELEASE:compile
[INFO] |  \- xml-apis:xml-apis:jar:1.4.01:runtime
[INFO] +- org.springframework.security:spring-security-config:jar:3.1.2.RELEASE:compile
[INFO] |  \- aopalliance:aopalliance:jar:1.0:compile
[INFO] +- org.springframework:spring-core:jar:3.1.2.RELEASE:compile
[INFO] |  +- org.springframework:spring-asm:jar:3.1.2.RELEASE:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] +- org.springframework:spring-beans:jar:3.1.2.RELEASE:compile
[INFO] +- org.springframework:spring-context:jar:3.1.2.RELEASE:compile
[INFO] |  \- org.springframework:spring-expression:jar:3.1.2.RELEASE:compile
[INFO] +- org.springframework:spring-aop:jar:3.1.2.RELEASE:compile
[INFO] +- org.springframework:spring-web:jar:3.1.2.RELEASE:compile
[INFO] +- org.springframework:spring-webmvc:jar:3.1.2.RELEASE:compile
[INFO] |  \- org.springframework:spring-context-support:jar:3.1.2.RELEASE:compile
[INFO] +- javax.servlet:jsp-api:jar:2.0:provided
[INFO] |  \- javax.servlet:servlet-api:jar:2.4:provided
[INFO] \- junit:junit:jar:4.4:test
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------

Spring Security SAML seems to be compatible with Spring Framework 5 in the release notes and reference guide,

On behalf of the community, I’m pleased to announce the release of Spring Security SAML 1.0.3.RELEASE which makes some minor changes to work with Spring Framework 5.0.0+ while keeping backward compatibility.

The current version of SAML Extension has been tested to work with Spring 3.1.2, Spring Security 3.1.2 and OpenSAML 2.6.1. Later versions of these libraries are likely to be compatible without need for modifications.

Since there are some uneasy descriptions such as "likely to be compatible", this time I tried running the Spring Security SAML sample application with Spring Framework 5 using the following approach.

Changed all Spring Framework related jars to 5 series

This time, I rewrote the version of the dependent jar specified in pom.xml of the Spring Security SAML sample application to the version of Spring Framework 5.

--With the EOL announcement of Spring IO Platform, it is recommended to use spring-boot-starter-parent and spring-boot-dependencies (Spring IO Platform end-of-life announcement / blog / 2018/04/09 / spring-io-platform-end-of-life-announcement)), but if it doesn't work this time, I wanted to specify jar dependencies one by one and try and error, so the above I took an approach like this. --Because of the relationship with the Spring Framework version that was originally intended to be incorporated, this time the Spring IO Platform [Cairo SR1](https://spring.io/blog/2018/05/10/spring-io-platform-cairo- The version is specified according to sr1). In Cairo SR1, Spring Security related version is 5.0.5, Spring Framework related version is 5.0. It is .6. --If you do not specify spring-security-core and spring-security-web, 3 systems will come in, so explicitly specify 5.0.5.

The modified parts of pom.xml are as follows.


$ diff -u ../../spring-security-saml-1.0.3.RELEASE_Keycloak/sample/pom.xml pom.xml
--- ../../spring-security-saml-1.0.3.RELEASE_Keycloak/sample/pom.xml    2018-12-01 03:30:02.783266900 +0900
+++ pom.xml     2018-12-01 04:34:36.179213100 +0900
@@ -113,7 +113,7 @@
-      <version>1.6.3</version>
+      <version>1.7.25</version>
@@ -125,43 +125,55 @@
-      <version>3.1.2.RELEASE</version>
+      <version>5.0.5.RELEASE</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-core</artifactId>
+      <version>5.0.5.RELEASE</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-web</artifactId>
+      <version>5.0.5.RELEASE</version>
-      <version>3.1.2.RELEASE</version>
+      <version>5.0.6.RELEASE</version>
-      <version>3.1.2.RELEASE</version>
+      <version>5.0.6.RELEASE</version>
-      <version>3.1.2.RELEASE</version>
+      <version>5.0.6.RELEASE</version>
-      <version>3.1.2.RELEASE</version>
+      <version>5.0.6.RELEASE</version>
-      <version>3.1.2.RELEASE</version>
+      <version>5.0.6.RELEASE</version>
-      <version>3.1.2.RELEASE</version>
+      <version>5.0.6.RELEASE</version>
@@ -173,7 +185,7 @@
-      <version>4.4</version>
+      <version>4.12</version>

Check the operation of the sample application above

Check the operation in the above environment. It didn't work at first, but in conclusion, it worked just by modifying the settings and code in the sample application. Since it worked with only the following changes, there was no particular library conflict, and we have determined that the Spring Security SAML 1.0.3 sample application simply did not support Spring Framework 5.

CSRF measures part 1

In the SAML POST binding method, the SAML Response is POSTed to the SP, but the above CSRF measures are also effective for the SAML endpoint, so if the sample application is operated as it is, the request will result in a 403 error. (Although it is proof that CSRF measures are effective). This time, referring to the URL below, we have separated the SAML endpoint and the normal endpoint.

The other, easier is to define Spring SAML endpoints in a separate http configuration which will not have the csrf protection enabled.

\sample\src\main\webapp\WEB-INF\securityContext.xml modification(Excerpt)

     <!-- Secured pages with SAML as entry point -->
-    <security:http entry-point-ref="samlEntryPoint" use-expressions="false">
+    <security:http pattern="/saml/**" entry-point-ref="samlEntryPoint" use-expressions="false">
+        <security:csrf disabled="true"/>
+        <security:intercept-url pattern="/saml/**" access="IS_AUTHENTICATED_FULLY"/>
+        <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
+        <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
+    </security:http>
+    <!-- Secured pages with Not SAML as entry point -->
+    <security:http pattern="/**" entry-point-ref="samlEntryPoint" use-expressions="false">
+        <security:csrf disabled="false"/>
         <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
         <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
         <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>

CSRF measures part 2

Spring Security has been changed so that CSRF measures are enabled by default when it goes up from 3 to 4.

As Spring Security 4.0+ CSRF Protection is now enabled by default.

Therefore, applications that do not take CSRF measures are inevitably required to take measures. Since POST is used to log in to the screen for Administrator in the sample application of Spring Security SAML 1.0.3, it is necessary to take measures to embed the CSRF token as shown below.

\sample\src\main\webapp\WEB-INF\security\adminLogin.Modification of jsp(Excerpt)

+                                <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />

Change default value of form login

In Spring Security, the parameter name at the time of form login is changed at the timing of going up from 3 to 4.

If the is being used within an application, then some of the default attributes have changed.

Since j_username and j_password are used in the sample application, they also need to be changed.

\sample\src\main\webapp\WEB-INF\security\adminLogin.Modification of jsp(Excerpt)

                                         <td><label for="username">User:</label></td>
-                                        <td><input type='text' name='j_username' id="username" class="text" value='admin'></td>
+                                        <td><input type='text' name='username' id="username" class="text" value='admin'></td>
                                         <td><label for="password">Password:</label></td>
-                                        <td><input type='password' name='j_password' id="password" class="text" value="admin"/></td>
+                                        <td><input type='password' name='password' id="password" class="text" value="admin"/></td>
                                         <td><input name="submit" class="button" type="submit" value="Login"/></td>

Password encoder support

The handling of the password encoder has also changed due to the change in Spring Security 4 → 5.

In the sample application, password encoding is not performed and the raw password is described in the configuration file, so change it to use BCryptPasswordEncoder.

\sample\src\main\webapp\WEB-INF\securityContext.xml modification(Excerpt)

+    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
     <security:authentication-manager alias="authenticationManager">
         <!-- Register authentication manager for SAML provider -->
         <security:authentication-provider ref="samlAuthenticationProvider"/>
         <!-- Register authentication manager for administration UI -->
+            <security:password-encoder ref="passwordEncoder"/>
             <security:user-service id="adminInterfaceService">
-                <security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
+                <security:user name="admin" password="$2a$10$ozKJfV0iGFKuK4J8Rm.3nuCtpe9dxK0fb/05WfoDejxQXxUcAjNzm" authorities="ROLE_ADMIN"/>

Rename default attributes

This is the effect of the Spring MVC upgrade. There are places where commandName is used on various screens, but it is necessary to replace all with modelAttribute.

\sample\src\main\webapp\WEB-INF\security\metadataGenerator.Modification of jsp(Excerpt)

                         <div class="post-body">
                             <p><a href="<c:url value="/saml/web/metadata"/>">&lt;&lt Back</a></p>
-                            <form:form commandName="metadata" action="create">
+                            <form:form modelAttribute="metadata" action="create">
                                 <td><label for="store">Store for the current session:</label></td>

: information_source: metadataView.jsp and providerView.jsp also need to be modified.

Incorporating into an application

The operation check of the sample application is completed by the above. After this, the following measures are required when embedding in the actual application.

  1. Incorporate into an existing application build environment
  2. Incorporate an existing application into web.xml
  3. Passing attribute information to an existing application

Actually, from here, various design patterns can be considered depending on the structure and framework of the existing application, standardization guidelines, and so on. The guide provides a way to extend the SAMLUserDetailsService, and I think there is also a way to create parts according to the project standardization guidelines.


The sample application is undeveloped, but we have confirmed that Spring Security SAML 1.0.3 works with Spring Framework 5. SAML is an old protocol that is still often used even though it is said to be old, and I think it is inevitable that Spring will be centered on the 5th system in the future. We hope that this article will be helpful to those who will use it in the future.

: information_source: Currently, Spring Security SAML is being developed at:

Keycloak settings used during this verification

image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png

Reference link

-Qiita --Try using SAML with Keycloak (WordPress) -[Notes on challenging SAML authentication using Spring Security SAML](https://hotchpotchj37.wordpress.com/2018/07/23/spring-security-saml%E3%82%92%E4%BD%BF% E3% 81% A3% E3% 81% A6saml% E8% AA% 8D% E8% A8% BC% E3% 81% AB% E3% 83% 81% E3% 83% A3% E3% 83% AC% E3% 83% B3% E3% 82% B8% E3% 81% 97% E3% 81% 9F% E3% 83% A1% E3% 83% A2 /)

Recommended Posts

Try to work with Keycloak using Spring Security SAML (Spring 5)
Part 1: Try using OAuth 2.0 Login supported by Spring Security 5 with Spring Boot
Try using Spring Boot with VS Code
Try to build a reverse proxy type configuration with Keycloak (Security Proxy edition)
Try LDAP authentication with Spring Security (Spring Boot) + OpenLDAP
Try to implement login function with Spring Boot
Try to automate migration with Spring Boot Flyway
[Introduction to Spring Boot] Authentication function with Spring Security
Try using Spring JDBC
Try using DI container with Laravel and Spring Boot
Try using GloVe with Deeplearning4j
Try using view_component with rails
Using Mapper with Java (Spring)
Implemented authentication function with Spring Security ②
Implemented authentication function with Spring Security ③
Spring Boot Tutorial Using Spring Security Authentication
I tried connecting to MySQL using JDBC Template with Spring MVC
Try using Redis with Java (jar)
Try to imitate marshmallows with MiniMagick
[Java] Try to implement using generics
Authentication / authorization with Spring Security & Thymeleaf
How to read Body of Request multiple times with Spring Boot + Spring Security
Workaround for Command Line Runner to work with JUnit in Spring Boot
JSESSIONID could not be assigned to the URL when using Spring Security
Set Spring Security authentication result to JSON
DB authentication with Spring Security & hashing with BCrypt
Try Spring Security AES256 string encryption / decryption
Try to implement login function with Spring-Boot
Asynchronous processing with Spring Boot using @Async
Use Spring Security JSP tags with FreeMarker
Try using Kong + Konga with Docker Compose.
How Spring Security works with Hello World
Try using the Wii remote with Java
Achieve BASIC authentication with Spring Boot + Spring Security
Create login / logout function with Spring Security according to Spring official guide [For beginners]
How to create an Excel form using a template file with Spring MVC
[Reverse lookup] Spring Security (updated from time to time)
Hash passwords with Spring Boot + Spring Security (with salt, with stretching)
How to use MyBatis2 (iBatis) with Spring Boot 1.4 (Spring 4)
How to use built-in h2db with spring boot
You use context to use MDC with Spring WebFlux
Try using GPS receiver kit with RaspberryPi3 (Ruby)
(Android) Try to display static text using DataBinding
Try to display prime numbers using boolean type
Steps to play mp3 with haml using audio_tag
Add your own authentication items with Spring Security
Try to implement TCP / IP + NIO with JAVA
[Java] Article to add validation with Spring Boot 2.3.1.
I wanted to gradle spring boot with multi-project
Apply Twitter Bootstrap 4 to Spring Boot 2 using Webjars
Try using S3Proxy with Microsoft Azure Blob Storage
Try using another Servlet container Jetty with Docker
Try to summarize the common layout with rails
Create Spring Cloud Config Server with security with Spring Boot 2.0