Open In App

Spring Cloud – Securing Services

Last Updated : 06 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Spring Cloud Security is an extremely versatile and strong framework for access control and authentication. To secure the Spring-based applications, this is a good mechanism. A Java application’s authentication and authorization are the main goals of the Spring Security framework. Our application can be authenticated across the board by using Spring Cloud Gateway and Spring Session to log users into a single service. This indicates that it will be simple for us to divide our application into appropriate domains and protect each one as needed.

Step-by-Step Implementation Spring Cloud – Securing Services

Below are the steps to implement Spring Cloud – Securing Services

Step 1: Integrate The UAA with Spring Cloud Gateway

The OAuth2 configuration included in the application.yml file of our gateway is the first item to take care of while configuring this functionality.

security:
oauth2:
client:
registration:
gateway:
provider: uaa
client-id: gateway
client-secret: secret
authorization-grant-type: authorization_code
redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}"
scope: openid,profile,email,resource.read
provider:
uaa:
authorization-uri: http://localhost:8090/uaa/oauth/authorize
token-uri: http://uaa:8099/uaa/oauth/token
user-info-uri: http://uaa:8099/uaa/userinfo
user-name-attribute: sub
jwk-set-uri: http://uaa:8099/uaa/token_keys

Step 2: Add the Maven Dependency

First, let’s make sure that every system module has the spring-boot-starter-security dependency:

<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Step 3: Create a SecurityConfig class

To replicate our discovery service, let’s construct a SecurityConfig class and this class will contain methods to configure authentication and authorization for the application.

Java




import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
  
@EnableWebFluxSecurity
@Configuration
public class SecurityConfig {
  
    /**
     * Configure a MapReactiveUserDetailsService with two users: "user" and "admin".
     
     * @param passwordEncoder the password encoder
     * @return a MapReactiveUserDetailsService with two users
     */
    @Bean
    public MapReactiveUserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails user = User.withUsername("user")
                .password(passwordEncoder.encode("password"))
                .roles("USER")
                .build();
        UserDetails adminUser = User.withUsername("admin")
                .password(passwordEncoder.encode("admin"))
                .roles("ADMIN")
                .build();
        return new MapReactiveUserDetailsService(user, adminUser);
    }
  
    /**
     * Configure a SecurityWebFilterChain with URL authorization.
     
     * @param http the ServerHttpSecurity object
     * @return a SecurityWebFilterChain object with URL authorization
     */
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange()
                .pathMatchers("/api/employees").permitAll()
                .pathMatchers("/api/employees/*").authenticated()
                .and().formLogin()
                .and().logout();
        return http.build();
    }
}


Step 4: Create an Employee class

To represent employee data, create an employee class.

Employee.java:

Java




public class Employee {
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
  
    public Long getId() {
        return id;
    }
  
    public void setId(Long id) {
        this.id = id;
    }
  
    public String getFirstName() {
        return firstName;
    }
  
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
  
    public String getLastName() {
        return lastName;
    }
  
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
  
    public String getEmail() {
        return email;
    }
  
    public void setEmail(String email) {
        this.email = email;
    }
}


Step 5: Configure Authentication and Authorization

We need to configure authentication and authorization in our application. We can use the SecurityConfig class to define our security configuration.

Java




import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
  
/**
 * Configuration class for security settings.
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    /**
     * Configures HTTP security.
     
     * @param http the HttpSecurity object to configure
     * @throws Exception if an error occurs during configuration
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/api/public/**").permitAll()
                .antMatchers(HttpMethod.POST, "/api/public/**").permitAll()
                .antMatchers(HttpMethod.GET, "/api/private/**").hasRole("USER")
                .antMatchers(HttpMethod.POST, "/api/private/**").hasRole("ADMIN")
                .and().httpBasic();
    }
  
    /**
     * Creates a PasswordEncoder bean for password encoding.
     
     * @return a BCryptPasswordEncoder bean
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}


Step 6: Create a Controller Class to Handle Employee Requests

To handle employee requests, create a controller class.

Java




import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
  
/**
 * RestController for handling employee-related requests.
 */
@RestController
@RequestMapping("/api")
public class EmployeeController {
  
    @Autowired
    private EmployeeService employeeService;
  
    /**
     * Handles GET requests to "/api/public" for public data.
     
     * @return the public data
     */
    @GetMapping("/public")
    public String getPublicData() {
        return employeeService.getPublicData();
    }
  
    /**
     * Handles GET requests to "/api/private/user" for user data.
     
     * @return the user data
     */
    @GetMapping("/private/user")
    public String getUserData() {
        return employeeService.getUserData();
    }
  
    /**
     * Handles POST requests to "/api/private/admin" for admin data.
     
     * @return the admin data
     */
    @PostMapping("/private/admin")
    public String getAdminData() {
        return employeeService.getAdminData();
    }
}


Step 7: Add properties to the application.properties file

Add the following properties to the rating service’s src/main/resources application.properties file:

spring.cloud.config.username=configUser
spring.cloud.config.password=configPassword
eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/

Step 8: Create a test class

In gateway project, let’s first construct a test class and a test method:

Java




public class GatewayApplicationTest 
{
  
    @Test
    public void testAccess() {
        // Add test logic to verify access to the gateway application
        // For example, use tools like RestTemplate or WebClient to make HTTP requests
        // and assert the expected behavior.
    }
}


Step 9: Access unprotected /api/employees Endpoint

Let’s add this code snippet to our test method so that we can configure our test and confirm that we can access our unprotected /api/employees resource:

Java




import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.web.client.TestRestTemplate;
  
// Create a new instance of TestRestTemplate to test HTTP requests
TestRestTemplate testRestTemplate = new TestRestTemplate();
  
// Define the base URL of the API being tested
String testUrl = "http://localhost:8080";
  
// Send a GET request to the "/api/employees" endpoint
ResponseEntity<String> response = testRestTemplate.getForEntity(testUrl + "/api/employees", String.class);
  
// Verify that the HTTP status code of the response is OK (200)
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
  
// Verify that the response body is not null
Assert.assertNotNull(response.getBody());


Step 10: Access the admin protected resource

Our ability to access the admin-protected resource and log in as the administrator will be verified by the following test:

Admin-Protected /api/employees Endpoint:

Java




import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
  
form.add("username", "admin"); // Add username
form.add("password", "admin"); // Add password
  
// Send a POST request to the "/login" endpoint with the form data
ResponseEntity<String> response = testRestTemplate.postForEntity(testUrl + "/login", form, String.class);
  
// Verify that the HTTP status code of the response is OK (200)
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
  
// Verify that the response body is not null
Assert.assertNotNull(response.getBody());


By following the above steps, we can secure various services of our Spring Cloud application.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads