<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Byte Journey]]></title><description><![CDATA[Byte Journey]]></description><link>https://blog.gintophilip.com</link><generator>RSS for Node</generator><lastBuildDate>Sun, 07 Jun 2026 11:20:40 GMT</lastBuildDate><atom:link href="https://blog.gintophilip.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[PART 4: Integrate the database with Spring Security.]]></title><description><![CDATA[Up to this point, we have used the default user provided by Spring Security to log in to the application. In the previous sections, we added some sample users to the application_users table. Moving forward, we will use this table for logging in to th...]]></description><link>https://blog.gintophilip.com/part-4-integrate-the-database-with-spring-security</link><guid isPermaLink="true">https://blog.gintophilip.com/part-4-integrate-the-database-with-spring-security</guid><category><![CDATA[Springboot]]></category><category><![CDATA[spring security]]></category><category><![CDATA[Java]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Ginto Philip]]></dc:creator><pubDate>Sat, 25 May 2024 15:09:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/YXdbaTRcQzA/upload/a9fd66bcfaedc2129dea444127c40794.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Up to this point, we have used the default user provided by Spring Security to log in to the application. In the previous sections, we added some sample users to the <code>application_users</code> table. Moving forward, we will use this table for logging in to the application. To achieve this, we need to configure Spring Security to recognize that the details of the application users are stored in the <code>application_users</code> table. This will enable Spring Security to retrieve and verify user credentials during authentication and authorization.</p>
<p>For that let's do the following steps:</p>
<ol>
<li><p>Add the password encoder bean</p>
</li>
<li><p>Update the plain text password to encrypted password</p>
</li>
<li><p>Configure user details service</p>
</li>
<li><p>Configure the authentication provider</p>
</li>
</ol>
<h3 id="heading-add-the-password-encoder-bean">Add the password encoder bean</h3>
<p>In the <code>SecurityConfig</code> class add a bean of type <code>PasswordEncoder</code></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth.web;

<span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;
<span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;
<span class="hljs-keyword">import</span> org.springframework.security.config.Customizer;
<span class="hljs-keyword">import</span> org.springframework.security.config.annotation.web.builders.HttpSecurity;
<span class="hljs-keyword">import</span> org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
<span class="hljs-keyword">import</span> org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
<span class="hljs-keyword">import</span> org.springframework.security.crypto.password.PasswordEncoder;
<span class="hljs-keyword">import</span> org.springframework.security.web.SecurityFilterChain;

<span class="hljs-meta">@Configuration</span>
<span class="hljs-meta">@EnableWebSecurity</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecurityConfig</span> </span>{

    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> SecurityFilterChain <span class="hljs-title">securityFilterChain</span><span class="hljs-params">(HttpSecurity securityConfig)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        <span class="hljs-keyword">return</span> securityConfig
                .authorizeHttpRequests(auth-&gt;
                        auth.requestMatchers(<span class="hljs-string">"/api/hello"</span>)
                                .permitAll()
                                .requestMatchers(<span class="hljs-string">"/api/admin"</span>).hasRole(<span class="hljs-string">"ADMIN"</span>)
                                .anyRequest().authenticated()
                ).formLogin(Customizer.withDefaults())
                .build();
    }
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> PasswordEncoder <span class="hljs-title">passwordEncoder</span><span class="hljs-params">()</span></span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BCryptPasswordEncoder();
    }
}
</code></pre>
<p>The <code>BCryptPasswordEncoder</code> is the preferred method for encoding passwords.</p>
<h3 id="heading-update-the-plain-text-password-to-encrypted-password">Update the plain text password to encrypted password.</h3>
<p>While creating the sample users, the passwords were stored as plain text. In this step, let's update the passwords to an encrypted form.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">You may wonder why user creation is handled in this manner and not through an API. The reason is, to minimize the overhead of creating APIs for every function. The primary goal of this article is to demonstrate the fundamentals of integrating authentication and authorization mechanisms.</div>
</div>

<ol>
<li><p>Drop the <code>user_roles</code> table</p>
</li>
<li><p>Drop the <code>roles</code> table</p>
</li>
<li><p>Drop the <code>application_user</code> table</p>
</li>
<li><p>Add the <code>PasswordEncoder</code> class dependency in <code>DataBaseUtilityRunner</code></p>
</li>
<li><p>Encode the password</p>
</li>
</ol>
<p><strong>Drop the</strong><code>user_roles</code><strong>table</strong></p>
<pre><code class="lang-bash">spring_access_db=<span class="hljs-comment"># drop table user_roles;</span>
</code></pre>
<p><strong>Drop the</strong><code>roles</code><strong>table</strong></p>
<pre><code class="lang-bash">spring_access_db=<span class="hljs-comment"># drop table roles;</span>
</code></pre>
<p><strong>Drop the</strong><code>application_user</code><strong>table</strong></p>
<pre><code class="lang-bash">spring_access_db=<span class="hljs-comment"># drop table application_user;</span>
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">The drop tables command were executed via psql CLI</div>
</div>

<p>And also, do the following in <code>DataBaseUtilityRunner</code> class.</p>
<p><em>update the lines</em></p>
<pre><code class="lang-java">user1.setPassword(<span class="hljs-string">"123456"</span>);
adminUser.setPassword(<span class="hljs-string">"12345"</span>);
</code></pre>
<p>as follows.</p>
<pre><code class="lang-java"> user1.setPassword(passwordEncoder.encode(<span class="hljs-string">"123456"</span>));
 adminUser.setPassword(passwordEncoder.encode(<span class="hljs-string">"12345"</span>));
</code></pre>
<p>The <code>DataBaseUtilityRunner</code> after modification is as follows,</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth;

<span class="hljs-keyword">import</span> com.gintophilip.springauth.entities.ApplicationUser;
<span class="hljs-keyword">import</span> com.gintophilip.springauth.entities.Roles;
<span class="hljs-keyword">import</span> com.gintophilip.springauth.repository.RoleRepository;
<span class="hljs-keyword">import</span> com.gintophilip.springauth.repository.UserRepository;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.boot.CommandLineRunner;
<span class="hljs-keyword">import</span> org.springframework.security.crypto.password.PasswordEncoder;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Component;

<span class="hljs-meta">@Component</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataBaseUtilityRunner</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">CommandLineRunner</span> </span>{
    <span class="hljs-meta">@Autowired</span>
    PasswordEncoder passwordEncoder;
    <span class="hljs-meta">@Autowired</span>
    UserRepository usersRepository;
    <span class="hljs-meta">@Autowired</span>
    RoleRepository rolesRepository;
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">(String... args)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        <span class="hljs-keyword">try</span> {
            Roles userRole = <span class="hljs-keyword">new</span> Roles();
            userRole.setRoleName(<span class="hljs-string">"USER"</span>);
            Roles adminRole = <span class="hljs-keyword">new</span> Roles();
            adminRole.setRoleName(<span class="hljs-string">"ADMIN"</span>);
            rolesRepository.save(userRole);
            rolesRepository.save(adminRole);
            ApplicationUser user1 = <span class="hljs-keyword">new</span> ApplicationUser();
            user1.setFirstName(<span class="hljs-string">"John"</span>);
            user1.setEmail(<span class="hljs-string">"john@test.com"</span>);
            user1.setPassword(passwordEncoder.encode(<span class="hljs-string">"123456"</span>));
            user1.setRole(userRole);

            ApplicationUser adminUser = <span class="hljs-keyword">new</span> ApplicationUser();
            adminUser.setFirstName(<span class="hljs-string">"sam"</span>);
            adminUser.setEmail(<span class="hljs-string">"sam@test.com"</span>);
            adminUser.setPassword(passwordEncoder.encode(<span class="hljs-string">"12345"</span>));

            adminUser.setRole(adminRole);
            usersRepository.save(user1);
            usersRepository.save(adminUser);
        }<span class="hljs-keyword">catch</span> (Exception exception){

        }

    }
}
</code></pre>
<p>Run the application and query the <code>application_user</code> table. You will see the passwords are encoded now instead of plain text.</p>
<pre><code class="lang-bash">spring_access_db=<span class="hljs-comment"># select * from application_user;</span>
 id  |     email     | first_name | last_name |            password                           
-----+---------------+------------+-----------+--------------------------------------------------------------
 102 | john@test.com | John       |           | <span class="hljs-variable">$2a</span><span class="hljs-variable">$10</span><span class="hljs-variable">$IBP9g8pOvNCvkEc7</span>/EG6TO3j6gh49QMuO6uuw9Dd/P9dPRi5mxbiAsnG
 103 | sam@test.com  | sam        |           | <span class="hljs-variable">$2a</span><span class="hljs-variable">$10</span><span class="hljs-variable">$WxM0l4qnjbYn1Vkgmrbte</span>.hgYqPyHLm/y.9IGvEoiRkrL8.h47QKu
(2 rows)
</code></pre>
<h3 id="heading-configure-user-details-service">Configure user details service</h3>
<p>For making spring security to use the database for authentication and authorization a user details service needs to be implemented. This is nothing but a class which implements the interface <code>UserDetailsService</code> .</p>
<p>This interface has a method named <code>loadUserByUsername</code> which accepts a string parameter and returns a <code>UserDetails</code> object.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> org.springframework.security.core.userdetails;
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserDetailsService</span> </span>{
    <span class="hljs-function">UserDetails <span class="hljs-title">loadUserByUsername</span><span class="hljs-params">(String username)</span> <span class="hljs-keyword">throws</span> UsernameNotFoundException</span>;
}
</code></pre>
<p>Create a class named <code>DatabaseUserDetailsService.java</code> which implements the <code>UserDetailsService</code> interface.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseUserDetailsService</span>  <span class="hljs-keyword">implements</span> <span class="hljs-title">UserDetailsService</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> UserDetails <span class="hljs-title">loadUserByUsername</span><span class="hljs-params">(String username)</span> <span class="hljs-keyword">throws</span> UsernameNotFoundException </span>{
       <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }
}
</code></pre>
<p>In the overridden method we do the following</p>
<ol>
<li><p>Fetch the user from database based on the parameter username.</p>
</li>
<li><p>Retrieve the roles associated with the user</p>
</li>
<li><p>Create an instance of class <code>User</code> with the user details and the associated roles</p>
</li>
<li><p>Return the <code>User</code> object</p>
</li>
</ol>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth.service;

<span class="hljs-keyword">import</span> com.gintophilip.springauth.entities.ApplicationUser;
<span class="hljs-keyword">import</span> com.gintophilip.springauth.repository.UserRepository;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.security.core.GrantedAuthority;
<span class="hljs-keyword">import</span> org.springframework.security.core.authority.SimpleGrantedAuthority;
<span class="hljs-keyword">import</span> org.springframework.security.core.userdetails.User;
<span class="hljs-keyword">import</span> org.springframework.security.core.userdetails.UserDetails;
<span class="hljs-keyword">import</span> org.springframework.security.core.userdetails.UserDetailsService;
<span class="hljs-keyword">import</span> org.springframework.security.core.userdetails.UsernameNotFoundException;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Service;

<span class="hljs-keyword">import</span> java.util.Collections;

<span class="hljs-meta">@Service</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseUserDetailsService</span>  <span class="hljs-keyword">implements</span> <span class="hljs-title">UserDetailsService</span> </span>{
    <span class="hljs-meta">@Autowired</span>
    UserRepository userRepository;
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> UserDetails <span class="hljs-title">loadUserByUsername</span><span class="hljs-params">(String username)</span> <span class="hljs-keyword">throws</span> UsernameNotFoundException </span>{
        ApplicationUser user = userRepository.findByEmail(username); <span class="hljs-comment">//fetch user from db</span>
        <span class="hljs-keyword">if</span>(user == <span class="hljs-keyword">null</span>){
            <span class="hljs-keyword">throw</span>  <span class="hljs-keyword">new</span> UsernameNotFoundException(<span class="hljs-string">"User Not found"</span>);
        }
        <span class="hljs-comment">//Retrieve user roles</span>
        GrantedAuthority authority = <span class="hljs-keyword">new</span> SimpleGrantedAuthority(<span class="hljs-string">"ROLE_"</span>+user.getRole().getRoleName());
        <span class="hljs-comment">//create User object</span>
        User applicationUser = <span class="hljs-keyword">new</span> User(user.getEmail(),user.getPassword(), Collections.singleton(authority));
        <span class="hljs-keyword">return</span> applicationUser;
    }
}
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">The <code>User</code> class implements the <code>UserDetails</code> interface. Spring utilizes the <code>UserDetails</code> to generate an Authentication object. This Authentication object indicates whether the user is authenticated.</div>
</div>

<p>Next we need to configure an authentication provider.</p>
<h3 id="heading-configure-the-authentication-provider">Configure the authentication provider</h3>
<p>For Spring Security to utilize the <code>DatabaseUserDetailsService</code> for retrieving user details, it must be linked with an authentication provider. For that we will create a bean of type <code>DaoAuthenticationProvider</code> in the <code>SecurityConfig</code> and bind it with <code>DatabaseUserDetailsService</code> within the <code>SecurityConfig</code>.</p>
<p>Bind the <code>DatabaseUserDetailsService</code></p>
<pre><code class="lang-java">    <span class="hljs-meta">@Autowired</span>
    DatabaseUserDetailsService databaseUserDetailsService;
</code></pre>
<p>Next, create an instance of <code>DaoAuthenticationProvider</code> which is an implementation of <code>AUthenticationProvider</code> given by Spring security. Then,</p>
<ol>
<li><p>Link the <code>databaseUserDetailsService</code></p>
</li>
<li><p>Link the <code>passwordEncoder</code></p>
</li>
</ol>
<pre><code class="lang-java"> <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> DaoAuthenticationProvider <span class="hljs-title">daoAuthenticationProvider</span><span class="hljs-params">()</span> </span>{
        DaoAuthenticationProvider authenticationProvider = <span class="hljs-keyword">new</span> DaoAuthenticationProvider();
        <span class="hljs-comment">//set the user details object        </span>
        authenticationProvider.setUserDetailsService(databaseUserDetailsService);
        <span class="hljs-comment">//set the password encoder        </span>
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        <span class="hljs-keyword">return</span> authenticationProvider;
    }
</code></pre>
<p>After this the <code>SecurityConfig</code> looks like below.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth.web;

<span class="hljs-keyword">import</span> com.gintophilip.springauth.service.DatabaseUserDetailsService;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;
<span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;
<span class="hljs-keyword">import</span> org.springframework.security.authentication.dao.DaoAuthenticationProvider;
<span class="hljs-keyword">import</span> org.springframework.security.config.Customizer;
<span class="hljs-keyword">import</span> org.springframework.security.config.annotation.web.builders.HttpSecurity;
<span class="hljs-keyword">import</span> org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
<span class="hljs-keyword">import</span> org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
<span class="hljs-keyword">import</span> org.springframework.security.crypto.password.PasswordEncoder;
<span class="hljs-keyword">import</span> org.springframework.security.web.SecurityFilterChain;

<span class="hljs-meta">@Configuration</span>
<span class="hljs-meta">@EnableWebSecurity</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecurityConfig</span> </span>{
    <span class="hljs-meta">@Autowired</span>
    DatabaseUserDetailsService databaseUserDetailsService;

    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> SecurityFilterChain <span class="hljs-title">securityFilterChain</span><span class="hljs-params">(HttpSecurity securityConfig)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        <span class="hljs-keyword">return</span> securityConfig
                .authorizeHttpRequests(auth-&gt;
                        auth.requestMatchers(<span class="hljs-string">"/api/hello"</span>)
                                .permitAll()
                                .requestMatchers(<span class="hljs-string">"/api/admin"</span>).hasRole(<span class="hljs-string">"ADMIN"</span>)
                                .anyRequest().authenticated()
                ).formLogin(Customizer.withDefaults())
                .build();
    }
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> PasswordEncoder <span class="hljs-title">passwordEncoder</span><span class="hljs-params">()</span></span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BCryptPasswordEncoder();
    }
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> DaoAuthenticationProvider <span class="hljs-title">daoAuthenticationProvider</span><span class="hljs-params">()</span> </span>{
        DaoAuthenticationProvider authenticationProvider = <span class="hljs-keyword">new</span> DaoAuthenticationProvider();
        <span class="hljs-comment">//set the user details object</span>
        authenticationProvider.setUserDetailsService(databaseUserDetailsService);
        <span class="hljs-comment">//set the password encoder</span>
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        <span class="hljs-keyword">return</span> authenticationProvider;
    }
}
</code></pre>
<p>Next, run the application and access the APIs. When the login page appears, use the user details from the <code>application_user</code> table.</p>
<ol>
<li><p><a target="_blank" href="http://localhost:8080/api/hello">http://localhost:8080/api/hello</a></p>
</li>
<li><p><a target="_blank" href="http://localhost:8080/api/protected">http://localhost:8080/api/protected</a></p>
</li>
<li><p><a target="_blank" href="http://localhost:8080/api/admin">http://localhost:8080/api/admin</a></p>
</li>
</ol>
<p>You will be successfully authenticated and able to view the response.</p>
<p>The <code>/api/admin</code> is only accessible to the user <strong><em>sam</em></strong>.</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[PART 3: Configuring security of the API end points]]></title><description><![CDATA[In this section, to configure the security of the API end points a custom security configuration needs to be created. To achieve this let's go through the following steps.

Create the security configuration class

Make all APIs to be accessed only by...]]></description><link>https://blog.gintophilip.com/part-3-configuring-security-of-the-api-end-points</link><guid isPermaLink="true">https://blog.gintophilip.com/part-3-configuring-security-of-the-api-end-points</guid><category><![CDATA[Springboot]]></category><category><![CDATA[spring security]]></category><category><![CDATA[Java]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Ginto Philip]]></dc:creator><pubDate>Sat, 25 May 2024 15:08:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/KxeFuXta4SE/upload/d42379adc47550e9f23b90e106754f38.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this section, to configure the security of the API end points a custom security configuration needs to be created. To achieve this let's go through the following steps.</p>
<ol>
<li><p>Create the security configuration class</p>
</li>
<li><p>Make all APIs to be accessed only by logged in users</p>
</li>
<li><p>Allow /api/hello to be accessed by anyone</p>
</li>
<li><p>Restrict access to /api/admin to user with ADMIN role only</p>
</li>
</ol>
<p>Access to the end points will be configured as follows.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>API</td><td>Who can access</td></tr>
</thead>
<tbody>
<tr>
<td>api/hello</td><td>anyone</td></tr>
<tr>
<td>api/protected</td><td>authenticated users</td></tr>
<tr>
<td>api/admin</td><td>admin user</td></tr>
</tbody>
</table>
</div><h3 id="heading-create-the-security-configuration-class">Create the security configuration class</h3>
<p>To implement a custom security configuration by overriding the default one we need to create a configuration class. This can be done with the help of <code>@Configuration</code> annotation.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth.web;

<span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;
<span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;
<span class="hljs-keyword">import</span> org.springframework.security.config.Customizer;
<span class="hljs-keyword">import</span> org.springframework.security.config.annotation.web.builders.HttpSecurity;
<span class="hljs-keyword">import</span> org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
<span class="hljs-keyword">import</span> org.springframework.security.web.SecurityFilterChain;

<span class="hljs-meta">@Configuration</span>
<span class="hljs-meta">@EnableWebSecurity</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecurityConfig</span> </span>{

    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> SecurityFilterChain <span class="hljs-title">securityFilterChain</span><span class="hljs-params">(HttpSecurity securityConfig)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        <span class="hljs-keyword">return</span> securityConfig
                .authorizeHttpRequests(auth-&gt;
                        auth.anyRequest().authenticated()
                ).formLogin(Customizer.withDefaults())
                .build();

    }
}
</code></pre>
<p>This will serve as our initial configuration. Here, we have mandated that every request must be authenticated. In the coming steps, we will configure the security settings as required.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">For logging in use the default user created by the Spring Security.</div>
</div>

<h3 id="heading-make-all-apis-to-be-accessed-only-by-logged-in-users">Make all APIs to be accessed only by logged in users</h3>
<p>There is nothing to do. Because the initial configuration we created satisfied the requirement. Hence we don't need to specify any special configuration for the API endpoint <code>/api/protected</code></p>
<h3 id="heading-allow-apihello-to-be-accessed-by-anyone">Allow <code>/api/hello</code> to be accessed by anyone</h3>
<pre><code class="lang-java">    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> SecurityFilterChain <span class="hljs-title">securityFilterChain</span><span class="hljs-params">(HttpSecurity securityConfig)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        <span class="hljs-keyword">return</span> securityConfig
                .authorizeHttpRequests(auth-&gt;
                        auth.requestMatchers(<span class="hljs-string">"/api/hello"</span>).permitAll().
                        anyRequest().authenticated()
                ).formLogin(Customizer.withDefaults())
                .build();
    }
</code></pre>
<p>Now run the application and attempt to access the APIs. The endpoint <code>/api/hello</code> is now accessible to everyone, while all other endpoints still require users to log in.</p>
<h3 id="heading-restrict-access-to-apiadmin-to-user-with-the-admin-role-only">Restrict access to <code>/api/admin</code> to user with the ADMIN role only.</h3>
<pre><code class="lang-java">    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> SecurityFilterChain <span class="hljs-title">securityFilterChain</span><span class="hljs-params">(HttpSecurity securityConfig)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        <span class="hljs-keyword">return</span> securityConfig
                .authorizeHttpRequests(auth-&gt;
                        auth.requestMatchers(<span class="hljs-string">"/api/hello"</span>)
                                .permitAll()
                                .requestMatchers(<span class="hljs-string">"/api/admin"</span>).hasRole(<span class="hljs-string">"ADMIN"</span>)
                                .anyRequest().authenticated()
                ).formLogin(Customizer.withDefaults())
                .build();
    }
</code></pre>
<p>At this point, the only API endpoint accessible to users is <code>/api/hello</code>. All other endpoints are restricted by a login screen.</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[PART 2: Enable Spring Security]]></title><description><![CDATA[In the previous section, we built a foundational application. In this section, we will enable Spring Security in the application. For that let's do the following steps:

Add the Spring Security dependency

Restart the application

Verify Spring Secur...]]></description><link>https://blog.gintophilip.com/part-2-enable-spring-security</link><guid isPermaLink="true">https://blog.gintophilip.com/part-2-enable-spring-security</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Spring framework]]></category><category><![CDATA[Java]]></category><category><![CDATA[backend]]></category><category><![CDATA[spring security]]></category><dc:creator><![CDATA[Ginto Philip]]></dc:creator><pubDate>Sat, 25 May 2024 15:07:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/_we0BQQewBo/upload/66ece73f92e9aa99893db0548a994835.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous section, we built a foundational application. In this section, we will enable Spring Security in the application. For that let's do the following steps:</p>
<ol>
<li><p>Add the Spring Security dependency</p>
</li>
<li><p>Restart the application</p>
</li>
<li><p>Verify Spring Security is enabled</p>
</li>
</ol>
<h3 id="heading-add-the-spring-security-dependency">Add the Spring Security dependency</h3>
<p>To Enable spring security the library <code>org.springframework.boot:spring-boot-starter-security</code> must be present in the Classpath. This can be achieved by adding the library as a dependency in the <code>build.gradle</code> file. Note the first item in the dependencies list</p>
<pre><code class="lang-plaintext">dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    runtimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
</code></pre>
<h3 id="heading-restart-the-application">Restart the application</h3>
<p>Just restart the application. Then go to the next step.</p>
<h3 id="heading-verify-spring-security-is-enabled">Verify Spring Security is enabled</h3>
<p>Open the browser and attempt to access the API endpoints. If a login page appears for each endpoint you try to access, it confirms that Spring Security is enabled and functioning as expected.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715611998026/4ecf026e-b942-46b0-84d4-beb4868963a7.png" alt class="image--center mx-auto" /></p>
<p>Yeah that's it.</p>
<p>At this point the application is running with the default implementation of Spring Security. You will not able to access the APIs without entering login credentials. In the default implementation, Spring Security provides a default user with username as <strong>“user”</strong> and a randomly generated password . This generated password can be obtained from the console logs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715612196065/a1aadde1-6e7d-4166-95f7-372a1f3c135a.png" alt="console log" class="image--center mx-auto" /></p>
<p>Note the line <strong><em>Using generated security password: dd05314d-2856-48b4-9c81-fcc480e0b4bf</em></strong></p>
<p>The default behavior of Spring Security, unless configured otherwise is as follows:</p>
<ul>
<li><p>All end points are protected by default when the library <code>org.springframework.boot:spring-boot-starter-security</code> is present in the Classpath</p>
</li>
<li><p>One cannot access the resources without authentication.</p>
</li>
<li><p>Provides a default user that can be overridden.</p>
</li>
</ul>
<p>In this default setup the APIs can be accessed by entering the username as <strong>user</strong> and password as the one which is printed in the console. Try accessing the APIs by entering the default credentials.</p>
<ol>
<li><p><a target="_blank" href="http://localhost:8080/api/hello">http://localhost:8080/api/hello</a></p>
</li>
<li><p><a target="_blank" href="http://localhost:8080/api/protected">http://localhost:8080/api/protected</a></p>
</li>
<li><p><a target="_blank" href="http://localhost:8080/api/admin">http://localhost:8080/api/admin</a></p>
</li>
</ol>
<hr />
]]></content:encoded></item><item><title><![CDATA[PART 1: Create the base application]]></title><description><![CDATA[Before proceeding to the implementation of authentication and authorization , let's create a simple application to serve as a foundation. The application will have the following API end points.

GET /api/hello

GET /api/protected

GET /api/admin


Al...]]></description><link>https://blog.gintophilip.com/part-1-create-the-base-application</link><guid isPermaLink="true">https://blog.gintophilip.com/part-1-create-the-base-application</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Java]]></category><category><![CDATA[backend]]></category><category><![CDATA[spring security]]></category><category><![CDATA[Spring framework]]></category><dc:creator><![CDATA[Ginto Philip]]></dc:creator><pubDate>Sat, 25 May 2024 15:05:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/s8OO2-t-HmQ/upload/a813cc8557b4a161a6d5fbb9a12277e7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Before proceeding to the implementation of authentication and authorization , let's create a simple application to serve as a foundation. The application will have the following API end points.</p>
<ol>
<li><p><code>GET /api/hello</code></p>
</li>
<li><p><code>GET /api/protected</code></p>
</li>
<li><p><code>GET /api/admin</code></p>
</li>
</ol>
<p>Also a database software is required. Here the setup uses <strong>PostgreSQL</strong> database. You are free to choose yours.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Don't forget to create a database named <code>spring_access_db</code> in your favorite database tool.</div>
</div>

<p>Let's go through the following steps to build the foundation.</p>
<ol>
<li><p>Create the project</p>
</li>
<li><p>Implement the API end points</p>
</li>
<li><p>Create user and role entities</p>
</li>
<li><p>Create user and role repositories</p>
</li>
<li><p>Configure database connectivity</p>
</li>
<li><p>Populate database with sample users</p>
</li>
<li><p>Run the application</p>
</li>
<li><p>Verify users and roles are created by querying the database</p>
</li>
<li><p>Try accessing the API end points</p>
</li>
</ol>
<h3 id="heading-create-the-project">Create the project</h3>
<p>To create the project go to <a target="_blank" href="https://start.spring.io/">https://start.spring.io/</a> and create the sample project with the following dependencies.</p>
<ul>
<li><p><strong>Spring Web</strong></p>
</li>
<li><p><strong>Spring Data JPA</strong></p>
</li>
<li><p><strong>PostgreSQL Driver</strong></p>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">If you are using <strong><em>IntelliJ IDEA Ultimate</em></strong> or <strong><em>Visual Studio Code</em></strong> you can create the project within the IDE.</div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Create project with IntelliJ IDEA Ultimate : <a target="_blank" href="https://www.jetbrains.com/help/idea/your-first-spring-application.html">Click here for doc</a></div>
</div>

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Create project with Visual Studio Code: <a target="_blank" href="https://code.visualstudio.com/docs/java/java-spring-boot">Click here for doc</a></div>
</div>

<p>Here I used the Spring initializer and opened the project in IntelliJ IDEA (Community Edition)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715625938788/e1c98dc6-eb29-4d54-b919-b3cd6ad1375d.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>Add the dependencies</p>
</li>
<li><p>Fill up the project metadata</p>
</li>
<li><p>Generate the Zip file and extract it.</p>
</li>
<li><p>Then open it in your IDE.</p>
</li>
</ol>
<h3 id="heading-implement-the-api-end-points">Implement the API end points</h3>
<p>Create a class named <strong>ApiController</strong> and implement the APIs there.</p>
<ul>
<li><p><strong>ApiController.java</strong></p>
<pre><code class="lang-java">  <span class="hljs-keyword">package</span> com.gintophilip.springauth.controller;

  <span class="hljs-keyword">import</span> org.springframework.http.ResponseEntity;
  <span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
  <span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;
  <span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

  <span class="hljs-meta">@RestController</span>
  <span class="hljs-meta">@RequestMapping("/api")</span>
  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiController</span> </span>{
      <span class="hljs-meta">@GetMapping("/hello")</span>
      <span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">hello</span><span class="hljs-params">()</span> </span>{
          <span class="hljs-keyword">return</span> ResponseEntity.ok(<span class="hljs-string">"Hello"</span>);
      }

      <span class="hljs-meta">@GetMapping("/protected")</span>
      <span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">protectedResource</span><span class="hljs-params">()</span> </span>{
          String message = <span class="hljs-string">""</span><span class="hljs-string">"
                  This is a protected resource &lt;br&gt;
                  You are seeing this because you are an authenticated user
                  "</span><span class="hljs-string">""</span>;
          <span class="hljs-keyword">return</span> ResponseEntity.ok(message);
      }

      <span class="hljs-meta">@GetMapping("/admin")</span>
      <span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">admin</span><span class="hljs-params">()</span> </span>{
          String message = <span class="hljs-string">"Hello Admin"</span>;
          <span class="hljs-keyword">return</span> ResponseEntity.ok(message);
      }
  }
</code></pre>
</li>
</ul>
<h3 id="heading-create-user-and-role-entities">Create user and role entities</h3>
<p>Create two classes for modeling the user and role entities.</p>
<p><strong>ApplicationUser.java</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth.entities;

<span class="hljs-keyword">import</span> jakarta.persistence.*;

<span class="hljs-meta">@Entity</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationUser</span> </span>{

    <span class="hljs-meta">@Id</span>
    <span class="hljs-meta">@GeneratedValue(strategy = GenerationType.AUTO)</span>
    <span class="hljs-keyword">private</span> Long id;
    <span class="hljs-keyword">private</span> String firstName;
    <span class="hljs-keyword">private</span> String lastName;
    <span class="hljs-meta">@Column(unique = true)</span>
    <span class="hljs-keyword">private</span> String email;
    <span class="hljs-keyword">private</span> String password;
    <span class="hljs-meta">@ManyToOne</span>
    <span class="hljs-meta">@JoinTable(name = "user_roles")</span>
    <span class="hljs-keyword">private</span> Roles role;

    <span class="hljs-function"><span class="hljs-keyword">public</span> Long <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> id;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(Long id)</span> </span>{
        <span class="hljs-keyword">this</span>.id = id;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getFirstName</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> firstName;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setFirstName</span><span class="hljs-params">(String firstName)</span> </span>{
        <span class="hljs-keyword">this</span>.firstName = firstName;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getLastName</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> lastName;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setLastName</span><span class="hljs-params">(String lastName)</span> </span>{
        <span class="hljs-keyword">this</span>.lastName = lastName;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getEmail</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> email;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setEmail</span><span class="hljs-params">(String email)</span> </span>{
        <span class="hljs-keyword">this</span>.email = email;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getPassword</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> password;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPassword</span><span class="hljs-params">(String password)</span> </span>{
        <span class="hljs-keyword">this</span>.password = password;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> Roles <span class="hljs-title">getRole</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> role;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setRole</span><span class="hljs-params">(Roles role)</span> </span>{
        <span class="hljs-keyword">this</span>.role = role;
    }
}
</code></pre>
<p><strong>Roles.java</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth.entities;

<span class="hljs-keyword">import</span> jakarta.persistence.*;

<span class="hljs-meta">@Entity</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Roles</span> </span>{
    <span class="hljs-meta">@Id</span>
    <span class="hljs-meta">@GeneratedValue(strategy = GenerationType.AUTO)</span>
    <span class="hljs-keyword">private</span> Long id;
    <span class="hljs-meta">@Column(unique = true)</span>
    <span class="hljs-keyword">private</span> String roleName;

    <span class="hljs-function"><span class="hljs-keyword">public</span> Long <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> id;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(Long id)</span> </span>{
        <span class="hljs-keyword">this</span>.id = id;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getRoleName</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> roleName;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setRoleName</span><span class="hljs-params">(String roleName)</span> </span>{
        <span class="hljs-keyword">this</span>.roleName = roleName;
    }
}
</code></pre>
<h3 id="heading-create-user-and-role-repositories">Create user and role repositories</h3>
<p><strong>UserRepository.java</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth.repository;

<span class="hljs-keyword">import</span> com.gintophilip.springauth.entities.ApplicationUser;
<span class="hljs-keyword">import</span> org.springframework.data.jpa.repository.JpaRepository;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserRepository</span>  <span class="hljs-keyword">extends</span> <span class="hljs-title">JpaRepository</span>&lt;<span class="hljs-title">ApplicationUser</span>,<span class="hljs-title">Long</span>&gt; </span>{

    <span class="hljs-function">ApplicationUser <span class="hljs-title">findByEmail</span><span class="hljs-params">(String email)</span></span>;
}
</code></pre>
<p><strong>RoleRepository.java</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth.repository;

<span class="hljs-keyword">import</span> com.gintophilip.springauth.entities.Roles;
<span class="hljs-keyword">import</span> org.springframework.data.jpa.repository.JpaRepository;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">RoleRepository</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">JpaRepository</span>&lt;<span class="hljs-title">Roles</span>,<span class="hljs-title">Long</span>&gt; </span>{

}
</code></pre>
<h3 id="heading-configure-database-connectivity">Configure database connectivity</h3>
<p>Let's configure the connection to the database named <code>spring_access_db</code> with the following settings:</p>
<p>username: <strong>postgres</strong></p>
<p>password: <strong>12345</strong></p>
<p>For configuring the database connectivity update the <strong>application.properties</strong> file as follows.</p>
<pre><code class="lang-plaintext">spring.datasource.url=jdbc:postgresql://localhost:5432/spring_access_db
spring.datasource.username=postgres
spring.datasource.password=12345
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto = update
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">If you are using a different database, please ensure that the connection URL is updated to correspond with your specific database. also the hibernate dialect</div>
</div>

<h3 id="heading-populate-database-with-sample-users">Populate database with sample users</h3>
<p>Let's Create a class which implements the <code>CommandLineRunner</code> interface. The method <code>run</code> will be executed once the application is ready.</p>
<p><strong>DatabaseUtilityRunner.java</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.springauth;

<span class="hljs-keyword">import</span> com.gintophilip.springauth.entities.ApplicationUser;
<span class="hljs-keyword">import</span> com.gintophilip.springauth.entities.Roles;
<span class="hljs-keyword">import</span> com.gintophilip.springauth.repository.RoleRepository;
<span class="hljs-keyword">import</span> com.gintophilip.springauth.repository.UserRepository;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.boot.CommandLineRunner;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Component;

<span class="hljs-meta">@Component</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataBaseUtilityRunner</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">CommandLineRunner</span> </span>{
    <span class="hljs-meta">@Autowired</span>
    UserRepository usersRepository;
    <span class="hljs-meta">@Autowired</span>
    RoleRepository rolesRepository;
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">(String... args)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        <span class="hljs-keyword">try</span> {
            Roles userRole = <span class="hljs-keyword">new</span> Roles();
            userRole.setRoleName(<span class="hljs-string">"USER"</span>);
            Roles adminRole = <span class="hljs-keyword">new</span> Roles();
            adminRole.setRoleName(<span class="hljs-string">"ADMIN"</span>);
            rolesRepository.save(userRole);
            rolesRepository.save(adminRole);
            ApplicationUser user1 = <span class="hljs-keyword">new</span> ApplicationUser();
            user1.setFirstName(<span class="hljs-string">"John"</span>);
            user1.setEmail(<span class="hljs-string">"john@test.com"</span>);
            user1.setPassword(<span class="hljs-string">"123456"</span>);
            user1.setRole(userRole);

            ApplicationUser adminUser = <span class="hljs-keyword">new</span> ApplicationUser();
            adminUser.setFirstName(<span class="hljs-string">"sam"</span>);
            adminUser.setEmail(<span class="hljs-string">"sam@test.com"</span>);
            adminUser.setPassword(<span class="hljs-string">"12345"</span>);

            adminUser.setRole(adminRole);
            usersRepository.save(user1);
            usersRepository.save(adminUser);
        }<span class="hljs-keyword">catch</span> (Exception exception){

        }

    }
}
</code></pre>
<h3 id="heading-verify-users-and-roles-are-created-by-querying-the-database">Verify users and roles are created by querying the database</h3>
<p>As a result of the previous step three tables will be created with the provided data in the database <code>spring_access_db</code>;</p>
<p>Here I use the psql CLI client to view and query the database.</p>
<p><strong>List of tables</strong></p>
<pre><code class="lang-bash">spring_access_db-<span class="hljs-comment"># \dt</span>
              List of relations
 Schema |       Name       | Type  |  Owner   
--------+------------------+-------+----------
 public | application_user | table | postgres
 public | roles            | table | postgres
 public | user_roles       | table | postgres
(3 rows)
</code></pre>
<p><strong>Table: application_user</strong></p>
<p><strong>Query</strong>: <code>select * from application_user;</code></p>
<pre><code class="lang-bash">spring_access_db=<span class="hljs-comment"># select * from application_user;</span>
 id |     email     | first_name | last_name | password 
----+---------------+------------+-----------+----------
  1 | john@test.com | John       |           | 123456
  2 | sam@test.com  | sam        |           | 12345
(2 rows)
</code></pre>
<p>In this example, the password is stored in clear text. In a real-world scenario, it is essential to store passwords in an encrypted form rather than as plain text. We'll do this later.</p>
<p><strong>Table: roles</strong></p>
<p><strong>Query:</strong><code>select * from roles;</code></p>
<pre><code class="lang-bash">spring_access_db=<span class="hljs-comment"># select * from roles;</span>
 id  | role_name 
-----+-----------
 802 | USER
 803 | ADMIN
(2 rows)
</code></pre>
<p><strong>Table: user_roles</strong></p>
<p><strong>Query:</strong><code>select * from user_roles;</code></p>
<pre><code class="lang-bash">spring_access_db=<span class="hljs-comment"># select * from user_roles;</span>
 role_id | id 
---------+----
     802 |  1
     803 |  2
(2 rows)
</code></pre>
<h3 id="heading-run-the-application">Run the application</h3>
<p>Now run the application. Once it is up and running, open the browser and try to access the API endpoints.</p>
<ol>
<li><p><a target="_blank" href="http://localhost:8080/api/hello">http://localhost:8080/api/hello</a></p>
</li>
<li><p><a target="_blank" href="http://localhost:8080/api/protected">http://localhost:8080/api/protected</a></p>
</li>
<li><p><a target="_blank" href="http://localhost:8080/api/admin">http://localhost:8080/api/admin</a></p>
</li>
</ol>
<p>You will be able to access all the APIs without any restrictions. This completes the setup of base application.</p>
<p>In the comings steps we'll see how to protect these end points by integrating authentication and authorization mechanisms using Spring Security.</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Introduction]]></title><description><![CDATA[Hello everyone, this document will guide you through the process of integrating authentication and authorization mechanisms into a Spring Boot web application using Spring Security. The following topics will be covered:.

PART 1: Create the base appl...]]></description><link>https://blog.gintophilip.com/spring-security-basics-introduction</link><guid isPermaLink="true">https://blog.gintophilip.com/spring-security-basics-introduction</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Java]]></category><category><![CDATA[Security]]></category><category><![CDATA[backend]]></category><category><![CDATA[Spring framework]]></category><category><![CDATA[Backend Development]]></category><dc:creator><![CDATA[Ginto Philip]]></dc:creator><pubDate>Sat, 25 May 2024 15:04:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/uEcSKKDB1pg/upload/82bb826da77039037eb893b0d1eb47f4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello everyone, this document will guide you through the process of integrating authentication and authorization mechanisms into a Spring Boot web application using Spring Security. The following topics will be covered:.</p>
<ol>
<li><p>PART 1: Create the base application.</p>
<ol>
<li><p>Create the project.</p>
</li>
<li><p>Implement the API end points.</p>
</li>
<li><p>Create user and role entities.</p>
</li>
<li><p>Create user and role repositories.</p>
</li>
<li><p>Configure database connectivity.</p>
</li>
<li><p>Populate database with sample users.</p>
</li>
<li><p>Verify users and roles are created by querying the database.</p>
</li>
<li><p>Run the application.</p>
</li>
</ol>
</li>
<li><p>PART 2: Enable Spring Security</p>
<ol>
<li><p>Add the Spring Security dependency</p>
</li>
<li><p>Restart the application</p>
</li>
<li><p>Verify Spring Security is enabled</p>
</li>
</ol>
</li>
<li><p>PART 3: Configuring security of the API end points</p>
<ol>
<li><p>Create the security configuration class</p>
</li>
<li><p>Make all APIs to be accessed only by logged in users</p>
</li>
<li><p>Allow /api/hello to be accessed by anyone</p>
</li>
<li><p>Restrict access to /api/admin to user with ADMIN role only</p>
</li>
</ol>
</li>
<li><p>PART 4: Integrate the database with Spring Security.</p>
<ol>
<li><p>Add the password encoder bean.</p>
</li>
<li><p>Update the plain text password to encrypted password.</p>
</li>
<li><p>Configure user details service.</p>
</li>
<li><p>Configure the authentication provider.</p>
</li>
</ol>
</li>
</ol>
<p>Before going in let's see what is,</p>
<ul>
<li><p>Authentication</p>
</li>
<li><p>Authorization</p>
</li>
<li><p>User details service</p>
</li>
<li><p>Authentication provider</p>
</li>
</ul>
<h3 id="heading-authentication">Authentication</h3>
<p>Authentication is the process of proving that someone is who they claim to be. The authentication can be done via certain ways such as Username and password, fingerprint. token etc...</p>
<p>To authenticate against an application, a user must have valid credentials. When these credentials are provided to the application for access, the application verifies them against the database where the credentials are stored. If the credentials match, the authentication is successful and the user can proceed to use the application.</p>
<h3 id="heading-authorization">Authorization.</h3>
<p>While authentication verifies the identity of a user in an application, authorization determines what actions the user is permitted to perform. This means it addresses the permissions assigned to a user.</p>
<p>In an application, there may be multiple users, each with different levels of operational rights. For example, an ADMIN user may have the right to delete a user, whereas a regular user will not have this capability.</p>
<p>A basic flow of user authentication and authorization</p>
<p><strong>Authentication</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1716636141801/01e13a37-13d4-4b1f-8fdf-fd022d40281c.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>User submits the credentials to the application.</p>
</li>
<li><p>The application verifies the credentials by checking in the database.</p>
</li>
<li><p>If the credentials are valid allow access to the application.</p>
</li>
</ol>
<p><strong>Authorization</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1716636320079/c4e9e1a8-21fa-4705-a831-6b2754883364.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>User requests a resource.</p>
</li>
<li><p>The authorization module checks if the user has rights to access the resource.</p>
</li>
<li><p>If user has rights the resource is given to the user.</p>
</li>
<li><p>Else the access to resource is denied.</p>
</li>
</ol>
<h3 id="heading-user-details-service"><strong>User details service</strong></h3>
<p>This service provides the necessary data, such as username, password, or other details required for authentication. In Spring Security, we configure a <code>UserDetailsService</code> object, which instructs Spring Security on where to load the required data for authentication. For example, when a username is provided as "test@test.com," Spring Security needs to look up the user data where the username is "test@test.com." This lookup is typically performed on a database. The database required for this lookup is configured using the user details service.</p>
<p>In essence, the user details service is a service that retrieves user data from the database based on a given key.</p>
<h3 id="heading-authentication-provider"><strong>Authentication provider</strong></h3>
<p>The authentication provider is responsible for authenticating users based on the provided credentials.</p>
<p>The authentication provider requests the user's details from the user details service using the received username. The user details service fetches and returns the user information if a user with the requested name is found. Then, it proceeds to compare the password.</p>
<p>The authentication provider and the user details service collaborate to authenticate a user effectively.</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Executing background tasks using @Async annotation in Spring Boot]]></title><description><![CDATA[When developing an application we will encounter scenarios where some operations require more time to finish.
For example, consider the scenario where an application provides an option for its users to download all their data. When a download request...]]></description><link>https://blog.gintophilip.com/async-annotation-in-spring-boot</link><guid isPermaLink="true">https://blog.gintophilip.com/async-annotation-in-spring-boot</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Java]]></category><category><![CDATA[backend]]></category><category><![CDATA[asynchronous programming]]></category><category><![CDATA[asynchronous]]></category><dc:creator><![CDATA[Ginto Philip]]></dc:creator><pubDate>Fri, 08 Sep 2023 20:46:47 GMT</pubDate><content:encoded><![CDATA[<p>When developing an application we will encounter scenarios where some operations require more time to finish.</p>
<p>For example, consider the scenario where an application provides an option for its users to download all their data. When a download request is received, the application will start to collect all the data of the user. Then make a compressed file of this data and send it to the user. This will take a minimum of 5 minutes to complete.</p>
<p>If this operation is executing in the main thread of the request, then we will be blocked until the task is finished. So, How can we solve this? This long-running task needs to be moved to another thread.</p>
<p>The Spring boot framework provides a feature to execute tasks in a background thread using the <a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html">@Async</a> annotation. Let's see how to execute background tasks in Spring Boot using the <a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html">@Async</a> annotation.</p>
<p>I've created a sample project named asyncDemo using the <a target="_blank" href="https://start.spring.io/">spring initializer</a> with the following structure</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694199682361/91488c8d-bd12-460e-bc06-3870f891a322.png" alt class="image--center mx-auto" /></p>
<p><strong>GitHub Link:</strong> <a target="_blank" href="https://github.com/gintoissac/spring-boot-async-demo">spring-boot-async-demo</a></p>
<h3 id="heading-the-rest-endpoints">The REST Endpoints</h3>
<ol>
<li><p><strong>/execute</strong>:- This will simulate a time-consuming task by introducing a 3-minute delay and writing a random number to a test database. Returns a taskId</p>
</li>
<li><p><strong>/fetch/{taskId}</strong>:- retrieves the generated random number</p>
</li>
</ol>
<h3 id="heading-steps-to-make-a-method-asynchronous"><strong>Steps to make a method asynchronous</strong></h3>
<ol>
<li><p>Create a thread pool executor configuration</p>
</li>
<li><p>Enable asynchronous method execution capability using <a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html">@EnableAsync</a> annotation</p>
</li>
<li><p>Annotate the required method with <a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html">@Async</a></p>
</li>
</ol>
<h3 id="heading-add-the-following-items-to-the-project">Add the following items to the project</h3>
<ol>
<li><p>Rest controller class</p>
</li>
<li><p>Entity class</p>
</li>
<li><p>Service class</p>
</li>
<li><p>Repository class</p>
</li>
</ol>
<p><strong>AsyncController.java (Rest API Controller)</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.asyncDemo.controller;

<span class="hljs-keyword">import</span> com.gintophilip.asyncDemo.entities.RandomUUID;
<span class="hljs-keyword">import</span> com.gintophilip.asyncDemo.service.RandomUUIDService;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.http.ResponseEntity;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.*;

<span class="hljs-keyword">import</span> java.util.Optional;
<span class="hljs-keyword">import</span> java.util.Random;

<span class="hljs-meta">@RestController</span>
<span class="hljs-meta">@RequestMapping("/api/")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AsyncController</span> </span>{
    <span class="hljs-meta">@Autowired</span>
    RandomUUIDService userService;
    <span class="hljs-meta">@PostMapping("/execute")</span>
    <span class="hljs-function">ResponseEntity&lt;Long&gt; <span class="hljs-title">timeConsumingTask</span><span class="hljs-params">()</span></span>{
        Long taskId = <span class="hljs-keyword">new</span> Random().nextLong(<span class="hljs-number">900</span>) + <span class="hljs-number">100</span>;
        userService.timeConsumingTask(taskId);
        <span class="hljs-keyword">return</span> ResponseEntity.ok(taskId);
    }
    <span class="hljs-meta">@GetMapping("/fetch/{taskId}")</span>
    ResponseEntity&lt;Optional&lt;RandomUUID&gt;&gt; fetchResult(<span class="hljs-meta">@PathVariable</span> Long taskId){
        <span class="hljs-keyword">return</span> ResponseEntity.ok(userService.fetchResult(taskId));
    }
}
</code></pre>
<p><strong>RandomUUIDService.java (Service)</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.asyncDemo.service;

<span class="hljs-keyword">import</span> com.gintophilip.asyncDemo.entities.RandomUUID;
<span class="hljs-keyword">import</span> com.gintophilip.asyncDemo.repositories.RandomUUIDRepository;
<span class="hljs-keyword">import</span> org.slf4j.Logger;
<span class="hljs-keyword">import</span> org.slf4j.LoggerFactory;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.scheduling.annotation.Async;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Service;

<span class="hljs-keyword">import</span> java.util.Optional;
<span class="hljs-keyword">import</span> java.util.UUID;

<span class="hljs-meta">@Service</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RandomUUIDService</span> </span>{
    <span class="hljs-meta">@Autowired</span>
    RandomUUIDRepository randomUUIDRepository;
    Logger logger = LoggerFactory.getLogger(RandomUUIDService.class);
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">timeConsumingTask</span><span class="hljs-params">(Long taskId)</span> </span>{
        <span class="hljs-keyword">try</span> {
            String id = String.valueOf(UUID.randomUUID());
            Thread.sleep(<span class="hljs-number">1000</span>);
            RandomUUID uuid = <span class="hljs-keyword">new</span> RandomUUID();
            uuid.setId(taskId);
            uuid.setUUID(id);
            randomUUIDRepository.save(uuid);
        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(e);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> Optional&lt;RandomUUID&gt; <span class="hljs-title">fetchResult</span><span class="hljs-params">(Long taskId)</span> </span>{
        <span class="hljs-keyword">return</span> randomUUIDRepository.findById(taskId);
    }
}
</code></pre>
<p><strong>RandomUUID.java (Entity)</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.asyncDemo.entities;

<span class="hljs-keyword">import</span> jakarta.persistence.Entity;
<span class="hljs-keyword">import</span> jakarta.persistence.Id;

<span class="hljs-meta">@Entity</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RandomUUID</span> </span>{
    <span class="hljs-meta">@Id</span>
    <span class="hljs-keyword">private</span> Long id;
    <span class="hljs-keyword">private</span> String UUID;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">RandomUUID</span><span class="hljs-params">()</span> </span>{
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> Long <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> id;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(Long id)</span> </span>{
        <span class="hljs-keyword">this</span>.id = id;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getUUID</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> UUID;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setUUID</span><span class="hljs-params">(String UUID)</span> </span>{
        <span class="hljs-keyword">this</span>.UUID = UUID;
    }
}
</code></pre>
<p><strong>RandomUUIDRepository.java (Repository)</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.asyncDemo.repositories;

<span class="hljs-keyword">import</span> com.gintophilip.asyncDemo.entities.RandomUUID;
<span class="hljs-keyword">import</span> org.springframework.data.jpa.repository.JpaRepository;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">RandomUUIDRepository</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">JpaRepository</span>&lt;<span class="hljs-title">RandomUUID</span>,<span class="hljs-title">Long</span>&gt; </span>{
}
</code></pre>
<p>Now run the application and call the <strong>/execute</strong> endpoint. You will see that to receive the response it took 3 minutes.</p>
<p>Let's solve this problem by using the <code>@Async</code> annotation</p>
<h3 id="heading-adding-asynchronous-method-execution-capability">Adding asynchronous method execution capability</h3>
<p>Add <a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html"><code>@EnableAsync</code></a> at <em>AsyncDemoApplication</em> class</p>
<pre><code class="lang-java"><span class="hljs-meta">@SpringBootApplication</span>
<span class="hljs-meta">@EnableAsync</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AsyncDemoApplication</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        SpringApplication.run(AsyncDemoApplication.class, args);
    }

}
</code></pre>
<h3 id="heading-create-the-thread-pool-executor-configuration"><strong>Create the thread pool executor configuration</strong></h3>
<p><strong>ThreadPoolConfig.java</strong></p>
<p>Let's give an initial thread pool size of 5 and the name for the thread as <em>async_thread</em></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.gintophilip.asyncDemo.configurations;

<span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;
<span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;
<span class="hljs-keyword">import</span> org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

<span class="hljs-meta">@Configuration</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThreadPoolConfig</span> </span>{
    <span class="hljs-meta">@Bean("asyncExecutor")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> ThreadPoolTaskExecutor <span class="hljs-title">executor</span><span class="hljs-params">()</span> </span>{
        ThreadPoolTaskExecutor executor = <span class="hljs-keyword">new</span> ThreadPoolTaskExecutor();
        executor.setCorePoolSize(<span class="hljs-number">5</span>);
        executor.setMaxPoolSize(<span class="hljs-number">5</span>);
        executor.setThreadNamePrefix(<span class="hljs-string">"async_thread"</span>);
        executor.initialize();
        <span class="hljs-keyword">return</span> executor;
    }
}
</code></pre>
<h3 id="heading-configure-the-method-to-run-as-an-asynchronous-task">Configure the method to run as an asynchronous task</h3>
<p>Annotate the method <strong><em>timeConsumingTask</em></strong> inside the <em>RandomUUIDService.java</em> class with <code>@Async("asyncExecutor")</code></p>
<p>We can specify which task executor to use by including the bean name if we have multiple task executor configurations. Here we use the "<strong><em>asyncExecutor</em></strong>" bean.</p>
<pre><code class="lang-java">    <span class="hljs-meta">@Async("asyncExecutor")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">timeConsumingTask</span><span class="hljs-params">(Long taskId)</span> </span>{
        logger.info(<span class="hljs-string">"Thread name :{}"</span>,Thread.currentThread().getName());
        <span class="hljs-keyword">try</span> {
            String id = String.valueOf(UUID.randomUUID());
            <span class="hljs-comment">//simulate task by sleeping for 3 minute</span>
            Thread.sleep(<span class="hljs-number">180000</span>);
            RandomUUID uuid = <span class="hljs-keyword">new</span> RandomUUID();
            uuid.setId(taskId);
            uuid.setUUID(id);
            randomUUIDRepository.save(uuid);
            logger.info(<span class="hljs-string">"task completed"</span>);
        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(e);
        }
    }
</code></pre>
<p>We also added a log message to show the thread name of the running async task</p>
<blockquote>
<p>The async method only supports <strong>Future</strong> and <strong>void</strong> as return type</p>
</blockquote>
<p>Now run the application and call the <strong><em>/execute</em></strong> endpoint. You will notice that it returned a task ID as a response.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694197184303/c23e4aff-be59-4f7c-9760-e9e469968722.png" alt class="image--center mx-auto" /></p>
<p>After returning the response the <strong><em>timeConsumingTask</em></strong> method will execute on the thread named <strong><em>async_thread</em></strong></p>
<p>Check the log. You will see the thread name we logged from the <strong><em>timeConsumingTask</em></strong> method</p>
<pre><code class="lang-java">[  async_thread1] c.g.asyncDemo.service.RandomUUIDService  : Thread name :async_thread1
[  async_thread1] c.g.asyncDemo.service.RandomUUIDService  : task completed
</code></pre>
<p>After the completion of the task, we can get the result by calling <strong><em>/fetch/{taskId}</em></strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694197463873/9c1237da-d21b-48df-a2ae-2e2450e423c1.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item></channel></rss>