Spring Security with REST API
Objectives
Today I would like to dive into security topic in Spring boot application. I base on my previous post (https://java-architect.blogspot.com/2020/05/spring-boot-rest-api-with-jpa.html) with the sources. I'm going to change and add protection to the API. There are two important aspect:- Authentication - process of identifying the user who calls the API
- Authorization - process of checking user's permission to call resources
The Authentication process can base on plain text password, digest method, JWT (Java WEB Token), OAuth, SAML or other method to identify user. Besides previously mentioned methods, very often applications are protected by certificates.
The simplest way to authenticate
So, due the topic, the simplest way to enable user authentication is to add the appropriate configuration.For simplification I defined users and roles in memory. Of course in production environment that data should be fetched form LDAP, Data Base or other identity server..
package com.main.artsci.configutarion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@SuppressWarnings("deprecation")
@Bean
public UserDetailsService userDetailsService() {
User.UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password("user").roles("USER").build());
manager.createUser(users.username("admin").password("admin").roles("USER", "ADMIN").build());
return manager;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userDetailsService());
}
@Override
protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/regions/complex/**").hasRole("ADMIN")
.anyRequest()
.authenticated()
.and().httpBasic();
}
}
In "userDetailsService()" method I defined all necessary users for this test case. In "configure()" method I enabled authentication for every request and all users can have access to all resources except resource defined by path "/regions/complex/" where access has only administrator.
In addition I created SecurityWebApplicationInitializer
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
Let's see the pom's changes. I added security libraries:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
Finally it is necessary to checks is requested resources are protected in a proper way.
Let's see the results after fetching resource available to everyone who pass true the authentication process.
curl --user user:user http://localhost:8080/regionsThe results are exactly we expect to achieve. Next what does happen if we try to fetch protected data?
[{"regionId":1,"name":"Europe"},{"regionId":2,"name":"Americas"},{"regionId":3,"name":"Asia"},{"regionId":4,"name":"Middle East and Africa"}]
curl --user user:user http://localhost:8080/regions/complex/1
{"timestamp":"2020-05-27T07:20:00.487+0000","status":403,"error":"Forbidden", "message":"Forbidden","path":"/regions/complex/1"}
The error occurs. There is no permission. Let's change user to admin.
curl --user admin:admin http://localhost:8080/regions/complex/1And everything is correct :)
{"regionId":1,"name":"Europe","countries":[{"countryId":"BE","name":"Belgium"},{"countryId":"CH","name":"Switzerland"},{"countryId":"DE","name":"Germany"},{"countryId":"DK","name":"Denmark"},{"countryId":"FR","name":"France"},{"countryId":"IT","name":"Italy"},{"countryId":"NL","name":"Netherlands"},{"countryId":"UK","name":"United Kingdom"}]}
Thank you so much for digging out useful aspects of REST API and finding how useful and practical are those approaches.
ReplyDeleteSQL Server Load Rest API