Spring Cloud – Securing Services
Last Updated :
06 Mar, 2024
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 {
@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
@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
@RequestMapping ( "/api" )
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@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();
}
@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() {
}
}
|
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;
TestRestTemplate testRestTemplate = new TestRestTemplate();
ResponseEntity<String> response = testRestTemplate.getForEntity(testUrl + "/api/employees" , String. class );
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
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" );
form.add( "password" , "admin" );
ResponseEntity<String> response = testRestTemplate.postForEntity(testUrl + "/login" , form, String. class );
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
Assert.assertNotNull(response.getBody());
|
By following the above steps, we can secure various services of our Spring Cloud application.
Share your thoughts in the comments
Please Login to comment...