<?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: Qaisar Abbas</title>
    <description>The latest articles on DEV Community by Qaisar Abbas (@qaisarabbas).</description>
    <link>https://dev.to/qaisarabbas</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%2F787732%2F528919ef-06e5-4b8a-bc13-9f4528915f1d.jpeg</url>
      <title>DEV Community: Qaisar Abbas</title>
      <link>https://dev.to/qaisarabbas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/qaisarabbas"/>
    <language>en</language>
    <item>
      <title>Secure Spring Boot Application With Keycloak</title>
      <dc:creator>Qaisar Abbas</dc:creator>
      <pubDate>Fri, 07 Jan 2022 12:13:14 +0000</pubDate>
      <link>https://dev.to/qaisarabbas/secure-spring-boot-application-with-keycloak-27c2</link>
      <guid>https://dev.to/qaisarabbas/secure-spring-boot-application-with-keycloak-27c2</guid>
      <description>&lt;p&gt;After the announcement of spring security team, spring is no longer supporting its own authorization server. Spring security OAuth2 officially deprecated all of its classes. Therefore it is recommended to use existing authorization server such as Keycloak or Okta.&lt;/p&gt;

&lt;p&gt;In this tutorial we will be using Keycloak as authorization server with spring boot.&lt;/p&gt;

&lt;p&gt;What is Keycloak?&lt;br&gt;
Keycloak is an open source software product to allow single sign-on with Identity and Access Management aimed at modern applications and services&lt;/p&gt;

&lt;p&gt;Downloading and Installing Keycloak&lt;br&gt;
Before getting started, you will need to installed and setup a Keycloak server. Visit this url &lt;a href="https://www.keycloak.org/"&gt;https://www.keycloak.org/&lt;/a&gt; to download Keycloak  version 15.0.2.&lt;/p&gt;

&lt;p&gt;After this go to your local directory    keycloak-&amp;gt;keycloak-15.0.2-&amp;gt;bin&lt;/p&gt;

&lt;p&gt;Run standalone.bat file.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="http://localhost:8080/auth/"&gt;http://localhost:8080/auth/&lt;/a&gt; and create admin user. Set admin /admin for username and password.&lt;/p&gt;

&lt;p&gt;Now you will be able to view the admin console.&lt;/p&gt;

&lt;p&gt;In this section, we setup the Keycloak configuration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Realm
A realm is a security domain that manages a collection of users, roles, credentials and groups. The protected resources on server can divided into different realm where each realm manages its own authentication and authorization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now select, Add realm from Master dropdown menu.&lt;/p&gt;

&lt;p&gt;Set the name of realm to test-realm and click create.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Client
Clients are entities that request to Keycloak for authentication. Mostly clients are application and services that uses Keycloak to secure themselves and provide single sign on solution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Clients request access token or identity information to use services that are secured by Keycloak.&lt;/p&gt;

&lt;p&gt;From the left sidebar select client and click create client.&lt;/p&gt;

&lt;p&gt;Now set app-client in client-id  and client-protocal-&amp;gt; openid-connect. Set root url to application url &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now click on save button. Then you will see app-client setting page. Here we will modify some properties for authentication. First we change access type to confidential then  the Authorization Enabled to ON , Service Account Enabled to ON and click on Save button.&lt;/p&gt;

&lt;p&gt;From credential tab you can view the client-secret which would be required later.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create client roles
Client Roles are only accessible from a specific client and the role of client cannot be accessible from a different client.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here we will create two roles admin and student.&lt;/p&gt;

&lt;p&gt;From the client tab, click on roles tab and click on Add Role.&lt;/p&gt;

&lt;p&gt;Now Add Role.&lt;/p&gt;

&lt;p&gt;Set Role Name to admin&lt;/p&gt;

&lt;p&gt;Now create other role and set Role Name to student. Then press the save button to save the role&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Realm Roles
Realm roles are global roles shared by all clients. Realm role only belongs to specific realm. Role of one realm can’t be accessible by other realm.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From the sidebar menu click on Roles. List of available roles listed here.&lt;/p&gt;

&lt;p&gt;Now Add Realm Role -&amp;gt; test-admin and save role.&lt;/p&gt;

&lt;p&gt;Enable composite roles. Here we will assign client and client roles to realm role.&lt;/p&gt;

&lt;p&gt;Select the app-client from client roles dropdown.&lt;/p&gt;

&lt;p&gt;From available Roles click admin and press Add-Selected.&lt;/p&gt;

&lt;p&gt;Now add second Realm Role -&amp;gt; test-student and save role&lt;/p&gt;

&lt;p&gt;Enable composite roles.&lt;/p&gt;

&lt;p&gt;Select the app-client from client roles dropdown for test-student role.&lt;/p&gt;

&lt;p&gt;From available Roles click student and press Add-Selected.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Users
Users are entities that are able to log into your system. User can have attributes username ,password, email etc. Users access resources through specific clients.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Select Users from sidebar and Add User -&amp;gt; student-1. Press save.&lt;/p&gt;

&lt;p&gt;Click the credential tab to set a password for user.&lt;/p&gt;

&lt;p&gt;Set password -&amp;gt; student123&lt;/p&gt;

&lt;p&gt;Switch off the temporary password and press save.&lt;/p&gt;

&lt;p&gt;From the tabs, select Role Mapping. Here we will assign realm roles to users.&lt;/p&gt;

&lt;p&gt;In Available roles of Realm roles, select test-student and press Add selected&lt;/p&gt;

&lt;p&gt;Now create second user for admin .&lt;/p&gt;

&lt;p&gt;Set Username to admin-1&lt;/p&gt;

&lt;p&gt;After saving the admin user, set credential for user admin-1&lt;/p&gt;

&lt;p&gt;Set password -&amp;gt; admin123 and switch off temporary password&lt;/p&gt;

&lt;p&gt;From Role Mapping tab, assign test-admin realm to user.&lt;/p&gt;

&lt;p&gt;Below diagram gives the complete understanding about model. Every Realm can have one or more client and each client can have multiple users.&lt;/p&gt;

&lt;p&gt;Keycloak-configuration-model&lt;br&gt;
Generate Tokens for Users&lt;br&gt;
Go to Realm settings and click on OpenID Endpoint Configuration to view OpenID Endpoint details.&lt;/p&gt;

&lt;p&gt;It will show you the application related endpoint.&lt;/p&gt;

&lt;p&gt;To generate a token select token-endpoint&lt;/p&gt;

&lt;p&gt;Copy the above highlighted url and create a POST request in POSTMAN to get access token.&lt;/p&gt;

&lt;p&gt;Add the following detail in x-www-form-urlencoded&lt;/p&gt;

&lt;p&gt;client_id -&amp;gt; app-client&lt;/p&gt;

&lt;p&gt;username -&amp;gt; admin-1&lt;/p&gt;

&lt;p&gt;password -&amp;gt; admin123&lt;/p&gt;

&lt;p&gt;grant_type -&amp;gt; password&lt;/p&gt;

&lt;p&gt;Click on Clients from the sidebar and get client_secret.&lt;/p&gt;

&lt;p&gt;Curl Command&lt;/p&gt;

&lt;p&gt;curl –location –request POST ‘&lt;a href="http://localhost:8080/auth/realms/test-realm/protocol/openid-connect/token%E2%80%99"&gt;http://localhost:8080/auth/realms/test-realm/protocol/openid-connect/token’&lt;/a&gt; \&lt;br&gt;
–header ‘Content-Type: application/x-www-form-urlencoded’ \&lt;br&gt;
–data-urlencode ‘client_id=app-client’ \&lt;br&gt;
–data-urlencode ‘client_secret=a4c29854-acad-423a-90a7-cec27032443a’ \&lt;br&gt;
–data-urlencode ‘username=admin-1’ \&lt;br&gt;
–data-urlencode ‘password=admin123’ \&lt;br&gt;
–data-urlencode ‘grant_type=password’&lt;/p&gt;

&lt;p&gt;Create a spring boot project.&lt;br&gt;
Prerequisite&lt;/p&gt;

&lt;p&gt;Java 11&lt;br&gt;
Spring Boot 2.5.6 stable version&lt;br&gt;
Add Spring Web and Spring Security dependency&lt;/p&gt;

&lt;p&gt;Now open your pom.xml in your IDE.&lt;/p&gt;

&lt;p&gt;Add the following dependency under dependencies section.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
    org.keycloak&lt;br&gt;
    keycloak-spring-boot-starter&lt;br&gt;
    15.0.2&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Then add the following dependency in dependency management.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
    org.keycloak.bom&lt;br&gt;
    keycloak-adapter-bom&lt;br&gt;
    15.0.2&lt;br&gt;
    pom&lt;br&gt;
    import&lt;br&gt;
&lt;br&gt;
After adding the dependencies your pom file should look this.&lt;/p&gt;

&lt;p&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br&gt;

         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&amp;gt;&lt;br&gt;
    4.0.0&lt;br&gt;
    &lt;br&gt;
        org.springframework.boot&lt;br&gt;
        spring-boot-starter-parent&lt;br&gt;
        2.5.7&lt;br&gt;
         &amp;lt;!-- lookup parent from repository --&amp;gt;&lt;br&gt;
    &lt;br&gt;
    com.example&lt;br&gt;
    keyCloakSpringBoot&lt;br&gt;
    0.0.1-SNAPSHOT&lt;br&gt;
    keyCloakSpringBoot&lt;br&gt;
    keyCloakSpringBoot&lt;br&gt;
    &lt;br&gt;
        11&lt;br&gt;
    &lt;br&gt;
    &lt;br&gt;
        &lt;br&gt;
            org.springframework.boot&lt;br&gt;
            spring-boot-starter&lt;br&gt;
        &lt;br&gt;
        &lt;br&gt;
            org.keycloak&lt;br&gt;
            keycloak-spring-boot-starter&lt;br&gt;
            15.0.2&lt;br&gt;
        &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-test&amp;lt;/artifactId&amp;gt;
        &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-security&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.keycloak.bom&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;keycloak-adapter-bom&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;15.0.2&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
            &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/dependencyManagement&amp;gt;
&amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
Add the following properties in your spring boot project application.properties file&lt;/p&gt;

&lt;p&gt;server.port=8181&lt;/p&gt;

&lt;p&gt;keycloak.realm=test-realm&lt;br&gt;
keycloak.auth-server-url = &lt;a href="http://localhost:8080/auth"&gt;http://localhost:8080/auth&lt;/a&gt;&lt;br&gt;
keycloak.ssl-required= external&lt;br&gt;
keycloak.resource=app-client&lt;br&gt;
keycloak.credentials.secret=a4c29854-acad-423a-90a7-cec27032443a&lt;br&gt;
keycloak.use-resource-role-mappings = true&lt;br&gt;
keycloak.bearer-only= true&lt;/p&gt;

&lt;p&gt;Create a package dto. Add the following class&lt;/p&gt;

&lt;p&gt;Student.java&lt;br&gt;
package com.example.keycloakspringboot.dto;&lt;/p&gt;

&lt;p&gt;public class Student {&lt;br&gt;
    private String name;&lt;br&gt;
    private String age;&lt;br&gt;
    private String semester;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Student(String name, String age, String semester) {
    this.name = name;
    this.age = age;
    this.semester = semester;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getAge() {
    return age;
}

public void setAge(String age) {
    this.age = age;
}

public String getSemester() {
    return semester;
}

public void setSemester(String semester) {
    this.semester = semester;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
Create a package named controller.&lt;/p&gt;

&lt;p&gt;UserController.java&lt;/p&gt;

&lt;p&gt;package com.example.keycloakspringboot.controller;&lt;/p&gt;

&lt;p&gt;import com.example.keycloakspringboot.dto.Student;&lt;br&gt;
import org.springframework.http.ResponseEntity;&lt;br&gt;
import org.springframework.web.bind.annotation.GetMapping;&lt;br&gt;
import org.springframework.web.bind.annotation.RequestMapping;&lt;br&gt;
import org.springframework.web.bind.annotation.RestController;&lt;br&gt;
import java.util.ArrayList;&lt;br&gt;
import java.util.List;&lt;/p&gt;

&lt;p&gt;@RestController&lt;br&gt;
@RequestMapping("/user")&lt;br&gt;
public class UserController {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@GetMapping(value = "/access-by-all")
public ResponseEntity&amp;lt;String&amp;gt; getAccess() {
    return ResponseEntity.ok("This endpoint is not authenticated ");
}

@GetMapping(value = "/student")
public ResponseEntity&amp;lt;Student&amp;gt; getStudent() {

    Student student=new Student("Emily","16","1");

    return  ResponseEntity.ok(student);
}

@GetMapping(value = "/all-students")
public ResponseEntity&amp;lt;List&amp;lt;Student&amp;gt;&amp;gt; getAllStudents() {

    List&amp;lt;Student&amp;gt; studentList=new ArrayList&amp;lt;&amp;gt;();

    studentList.add(new Student("Emily","16","1"));
    studentList.add(new Student("John","18","2"));
    studentList.add(new Student("Sam","15","1"));

    return ResponseEntity.ok(studentList);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
SecurityConfig.java&lt;/p&gt;

&lt;p&gt;In Security Config class, we are extending KeycloakWebSecurityConfigurerAdapter as a convenient base class for creating WebSecurityConfigurer Instance&lt;/p&gt;

&lt;p&gt;By default, Spring Security Adapter looks for configuration file keycloak.json .So by adding keycloakConfigResolver bean it will look at the configuration provided by Spring Boot Adapter.&lt;/p&gt;

&lt;p&gt;RegisterSessionAuthenticationStrategy bean register user session after authentication.&lt;/p&gt;

&lt;p&gt;SimpleAuthorityMapper in configure global method make sure that role are not prefixed with ROLE_&lt;/p&gt;

&lt;p&gt;package com.example.keycloakspringboot.config;&lt;/p&gt;

&lt;p&gt;import org.keycloak.adapters.KeycloakConfigResolver;&lt;br&gt;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;&lt;br&gt;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;&lt;br&gt;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;&lt;br&gt;
import org.springframework.beans.factory.annotation.Autowired;&lt;br&gt;
import org.springframework.context.annotation.Bean;&lt;br&gt;
import org.springframework.context.annotation.Configuration;&lt;br&gt;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;&lt;br&gt;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;&lt;br&gt;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;&lt;br&gt;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;&lt;br&gt;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;&lt;br&gt;
import org.springframework.security.core.session.SessionRegistryImpl;&lt;br&gt;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;&lt;br&gt;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;&lt;/p&gt;

&lt;p&gt;@Configuration&lt;br&gt;
@EnableWebSecurity&lt;br&gt;
@EnableGlobalMethodSecurity(securedEnabled = true)&lt;br&gt;
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {&lt;br&gt;
    @Override&lt;br&gt;
    protected void configure(HttpSecurity http) throws Exception {&lt;br&gt;
        super.configure(http);&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    http
            .authorizeRequests()

            .antMatchers("/user/all-students").hasRole("admin")
            .antMatchers("/user/access-by-all").permitAll()
            .anyRequest().authenticated();

}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

    KeycloakAuthenticationProvider keycloakAuthenticationProvider = new          
                                 keycloakAuthenticationProvider();

    keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new 
                                 SimpleAuthorityMapper());

    auth.authenticationProvider(keycloakAuthenticationProvider);

}


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

@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
    return new KeycloakSpringBootConfigResolver();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
users with admin Role are only authorized to access this endpoint /user/all-students.&lt;/p&gt;

&lt;p&gt;Endpoint /user/access-by-all is accessible without any auth token.&lt;/p&gt;

&lt;p&gt;Run Spring Boot Project.&lt;/p&gt;

&lt;p&gt;Test Application&lt;/p&gt;

&lt;p&gt;In our application, we have not authenticated this endpoint &lt;a href="http://localhost:8181/user/access-by-all"&gt;http://localhost:8181/user/access-by-all&lt;/a&gt;. So it should work without token&lt;/p&gt;

&lt;p&gt;Next we will access the authenticated endpoint &lt;a href="http://localhost:8181/user/all-students"&gt;http://localhost:8181/user/all-students&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now get the authorization access token through admin-1 user and add it to your authorization header as shown in the fig below.&lt;/p&gt;

&lt;p&gt;Now get the access token through student-1 user.&lt;/p&gt;

&lt;p&gt;Add this token in Authorization bearer and access this url &lt;a href="http://localhost:8181/user/all-students"&gt;http://localhost:8181/user/all-students&lt;/a&gt;. It will send you a forbidden response. Because the user with admin role is only authorized to access this endpoint.&lt;/p&gt;

&lt;p&gt;Access this url &lt;a href="http://localhost:8181/user/student"&gt;http://localhost:8181/user/student&lt;/a&gt; from student-1  token.&lt;/p&gt;

&lt;p&gt;Access this url &lt;a href="http://localhost:8181/user/student"&gt;http://localhost:8181/user/student&lt;/a&gt; from student-1  token.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>webdev</category>
      <category>java</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
