Sécurité Spring RESTful Service utilisant Basic Authentication
1. Objectif de l'exemple
Ce document est basé sur :
Spring Boot 2.x (Or >= 1.5.9)
Eclipse 4.7 Oxygen
Dans cette publication, je vais vouss montrer comment créer une application RESTful Web Service et la sécuriser à l'aide de Basic Authentication. Cela signifie que votre application fournira des ressources (Resource) des données, mais l'utilisateurs qui voudrait utiliser cette ressource de données devrait être authentifié (authenticate) avec les méthodes de l'authentification basique (Basic Authentication).
Basic Authentication (Authentification basique)
Afin d'accéder à une ressource (Resource) de données sécurisée par Basic Authentication, un utilisateur doit envoyer une request dont cette request là conprend l'information de username/password affichée sur Header.
Vous pouvez utiliser un navigateur pour accéderr à une ressource de données sécurisée par Basic Authentication, dans ce cas, une boite dialogue (dialog) qui sera affichée vous permet de saisir username/password, ces informations ont jointes dans request pour envoyer au REST Server.
2. Créer le projet Spring Boot
Sur Eclipse, créer un projet Spring Boot.
Saisissez :
- Name: SbRestBasicAuthentication
- Group: org.o7planning
- Package: org.o7planning.sbrestbasicauth
Dans les étapes suivantes, vous devez sélectionner les technologies que vous allez utiliser.
SbRestBasicAuthenticationApplication.java
package org.o7planning.sbrestbasicauth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SbRestBasicAuthenticationApplication {
public static void main(String[] args) {
SpringApplication.run(SbRestBasicAuthenticationApplication.class, args);
}
}
3. Configurer le fichier pom.xml
Dans cet exemple, nous avons besoin d'une bibliothèque de convertissement (convert) XML en objet Java et vice versa. Une autre bibliothèque pour convertif JSON en Java et vice versa.
JSON <==> Java
spring-boot-starter-web a construit dans la bibliothèque jackson-databind, qui aide à convertir JSON en objet Java et vice versa.
XML <==> Java
Spring Boot utilise JAXB (Disponible dans JDK) comme une bibliothèque par défaut pour convertif XML et Java. Pourtant vos classes Java doivent être annotées (annotated) par @XmlRootElement,... Donc mon conseil est que vous devriez utiliser jackson-dataformat-xml comme une bibliothèque de convertissement XML et Java. Pour jackson-dataformat-xml vous devez la déclarer dans le fichier pom.xml:
** pom.xml **
...
<dependencies>
...
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
...
</dependencies>
...
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.o7planning</groupId>
<artifactId>SbRestBasicAuthentication</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SbRestBasicAuthentication</name>
<description>Spring Boot +Rest + Basic Authentication</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4. Security & AuthenticationEntryPoint
Les configurations de sécurité seront écrites dans la classe WebSecurityConfig. Dans cet article, je ne me concentre pas sur "Comment obtenir des noms d'utilisateurs dans la base de données ", donc nous créons 2 UserName fixés et stockés dans la mémoire. L'utilisateur qui accède à la ressource de données du REST Service va se connecter à un de deux username.
WebSecurityConfig.java
package org.o7planning.sbrestbasicauth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationEntryPoint authEntryPoint;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// All requests send to the Web Server request must be authenticated
http.authorizeRequests().anyRequest().authenticated();
// Use AuthenticationEntryPoint to authenticate user/password
http.httpBasic().authenticationEntryPoint(authEntryPoint);
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
String password = "123";
String encrytedPassword = this.passwordEncoder().encode(password);
System.out.println("Encoded password of 123=" + encrytedPassword);
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> //
mngConfig = auth.inMemoryAuthentication();
// Defines 2 users, stored in memory.
// ** Spring BOOT >= 2.x (Spring Security 5.x)
// Spring auto add ROLE_
UserDetails u1 = User.withUsername("tom").password(encrytedPassword).roles("USER").build();
UserDetails u2 = User.withUsername("jerry").password(encrytedPassword).roles("USER").build();
mngConfig.withUser(u1);
mngConfig.withUser(u2);
// If Spring BOOT < 2.x (Spring Security 4.x)):
// Spring auto add ROLE_
// mngConfig.withUser("tom").password("123").roles("USER");
// mngConfig.withUser("jerry").password("123").roles("USER");
}
}
La classe AuthenticationEntryPointImpl étendue (extends) de la classe BasicAuthenticationEntryPoint, elle sert à vérifier si username/password affcihés au request est validé ou non.
AuthenticationEntryPointImpl.java
package org.o7planning.sbrestbasicauth.auth;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class AuthenticationEntryPointImpl extends BasicAuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 - " + authEx.getMessage());
}
@Override
public void afterPropertiesSet() throws Exception {
// RealmName appears in the login window (Firefox).
setRealmName("o7planning");
super.afterPropertiesSet();
}
}
5. Model, DAO, Controller
La classe Employee représente un employé.
Employee.java
package org.o7planning.sbrestbasicauth.model;
public class Employee {
private String empNo;
private String empName;
private String position;
public Employee() {
}
public Employee(String empNo, String empName, String position) {
this.empNo = empNo;
this.empName = empName;
this.position = position;
}
public String getEmpNo() {
return empNo;
}
public void setEmpNo(String empNo) {
this.empNo = empNo;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
}
La classe EmployeeDAO est annotée (annotate) par @Repository pour informer à Spring qu'il s'agit un Spring BEAN. Cette classe comprend les méthodes aidant à enquêter une liste des employés (employee), à créer des employés, à mettre à jour les informations de l'employé ainsi qu' à effacer des employés.
EmployeeDAO.java
package org.o7planning.sbrestbasicauth.dao;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.o7planning.sbrestbasicauth.model.Employee;
import org.springframework.stereotype.Repository;
@Repository
public class EmployeeDAO {
private static final Map<String, Employee> empMap = new HashMap<String, Employee>();
static {
initEmps();
}
private static void initEmps() {
Employee emp1 = new Employee("E01", "Smith", "Clerk");
Employee emp2 = new Employee("E02", "Allen", "Salesman");
Employee emp3 = new Employee("E03", "Jones", "Manager");
empMap.put(emp1.getEmpNo(), emp1);
empMap.put(emp2.getEmpNo(), emp2);
empMap.put(emp3.getEmpNo(), emp3);
}
public Employee getEmployee(String empNo) {
return empMap.get(empNo);
}
public Employee addEmployee(Employee emp) {
empMap.put(emp.getEmpNo(), emp);
return emp;
}
public Employee updateEmployee(Employee emp) {
empMap.put(emp.getEmpNo(), emp);
return emp;
}
public void deleteEmployee(String empNo) {
empMap.remove(empNo);
}
public List<Employee> getAllEmployees() {
Collection<Employee> c = empMap.values();
List<Employee> list = new ArrayList<Employee>();
list.addAll(c);
return list;
}
}
La classe MainRESTController est annotée (annotate) par @RestController pour informer Spring qu'il s'agit unSpring Restful Controller,
MainRESTController.java
package org.o7planning.sbcrudrestful.controller;
import java.util.List;
import org.o7planning.sbcrudrestful.dao.EmployeeDAO;
import org.o7planning.sbcrudrestful.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MainRESTController {
@Autowired
private EmployeeDAO employeeDAO;
@RequestMapping("/")
@ResponseBody
public String welcome() {
return "Welcome to RestTemplate Example.";
}
// URL:
// http://localhost:8080/SomeContextPath/employees
// http://localhost:8080/SomeContextPath/employees.xml
// http://localhost:8080/SomeContextPath/employees.json
@RequestMapping(value = "/employees", //
method = RequestMethod.GET, //
produces = { MediaType.APPLICATION_JSON_VALUE, //
MediaType.APPLICATION_XML_VALUE })
@ResponseBody
public List<Employee> getEmployees() {
List<Employee> list = employeeDAO.getAllEmployees();
return list;
}
// URL:
// http://localhost:8080/SomeContextPath/employee/{empNo}
// http://localhost:8080/SomeContextPath/employee/{empNo}.xml
// http://localhost:8080/SomeContextPath/employee/{empNo}.json
@RequestMapping(value = "/employee/{empNo}", //
method = RequestMethod.GET, //
produces = { MediaType.APPLICATION_JSON_VALUE, //
MediaType.APPLICATION_XML_VALUE })
@ResponseBody
public Employee getEmployee(@PathVariable("empNo") String empNo) {
return employeeDAO.getEmployee(empNo);
}
// URL:
// http://localhost:8080/SomeContextPath/employee
// http://localhost:8080/SomeContextPath/employee.xml
// http://localhost:8080/SomeContextPath/employee.json
@RequestMapping(value = "/employee", //
method = RequestMethod.POST, //
produces = { MediaType.APPLICATION_JSON_VALUE, //
MediaType.APPLICATION_XML_VALUE })
@ResponseBody
public Employee addEmployee(@RequestBody Employee emp) {
System.out.println("(Service Side) Creating employee: " + emp.getEmpNo());
return employeeDAO.addEmployee(emp);
}
// URL:
// http://localhost:8080/SomeContextPath/employee
// http://localhost:8080/SomeContextPath/employee.xml
// http://localhost:8080/SomeContextPath/employee.json
@RequestMapping(value = "/employee", //
method = RequestMethod.PUT, //
produces = { MediaType.APPLICATION_JSON_VALUE, //
MediaType.APPLICATION_XML_VALUE })
@ResponseBody
public Employee updateEmployee(@RequestBody Employee emp) {
System.out.println("(Service Side) Editing employee: " + emp.getEmpNo());
return employeeDAO.updateEmployee(emp);
}
// URL:
// http://localhost:8080/SomeContextPath/employee/{empNo}
@RequestMapping(value = "/employee/{empNo}", //
method = RequestMethod.DELETE, //
produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
@ResponseBody
public void deleteEmployee(@PathVariable("empNo") String empNo) {
System.out.println("(Service Side) Deleting employee: " + empNo);
employeeDAO.deleteEmployee(empNo);
}
}
Expliquer:
- produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }
- produces = { "application/json" , "application/xml" }
L'attribut produces sert à spécifier un URL qui va seulement créer (renvoie à l'utilisateur) des données avec quel format. Par exemple "application/json", "application/xml".
Tutoriels Java Web Service
- Qu'est-ce que RESTful Web Service?
- Tutoriel Java RESTful Web Service pour débutant
- Exemple CRUD simple avec Java RESTful Web Service
- Créer Java RESTful Client avec Jersey Client
- RESTClient Un débogueur pour RESTful Web Service
- Exemple de CRUD simple avec Spring MVC RESTful Web Service
- Exemple CRUD Restful WebService avec Spring Boot
- Exemple Spring Boot Restful Client avec RestTemplate
- Sécurité Spring RESTful Service utilisant Basic Authentication
Show More
Tutoriels Spring Boot
- Installer Spring Tool Suite pour Eclipse
- Le Tutoriel de Spring pour débutant
- Le Tutoriel de Spring Boot pour débutant
- Propriétés communes de Spring Boot
- Le Tutoriel de Spring Boot et Thymeleaf
- Le Tutoriel de Spring Boot et FreeMarker
- Le Tutoriel de Spring Boot et Groovy
- Le Tutoriel de Spring Boot et Mustache
- Le Tutoriel de Spring Boot et JSP
- Le Tutoriel de Spring Boot, Apache Tiles, JSP
- Utiliser Logging dans Spring Boot
- Surveillance des applications avec Spring Boot Actuator
- Créer une application Web multilingue avec Spring Boot
- Utiliser plusieurs ViewResolvers dans Spring Boot
- Utiliser Twitter Bootstrap dans Spring Boot
- Le Tutoriel de Spring Boot Interceptor
- Le Tutoriel de Spring Boot, Spring JDBC et Spring Transaction
- Le Tutoriel de Spring JDBC
- Le Tutoriel de Spring Boot, JPA et Spring Transaction
- Le Tutoriel de Spring Boot et Spring Data JPA
- Le Tutoriel de Spring Boot, Hibernate et Spring Transaction
- Intégration de Spring Spring, JPA et H2 Database
- Le Tutoriel de Spring Boot et MongoDB
- Utiliser plusieurs DataSources avec Spring Boot et JPA
- Utiliser plusieurs DataSources avec Spring Boot et RoutingDataSource
- Créer une application de connexion avec Spring Boot, Spring Security, Spring JDBC
- Créer une application de connexion avec Spring Boot, Spring Security, JPA
- Créer une application d'enregistrement d'utilisateur avec Spring Boot, Spring Form Validation
- Exemple de OAuth2 Social Login dans Spring Boot
- Exécuter des tâches planifiées en arrière-plan dans Spring
- Exemple CRUD Restful WebService avec Spring Boot
- Exemple Spring Boot Restful Client avec RestTemplate
- Exemple CRUD avec Spring Boot, REST et AngularJS
- Sécurité Spring RESTful Service utilisant Basic Authentication
- Sécuriser Spring Boot RESTful Service en utilisant Auth0 JWT
- Exemple Upload file avec Spring Boot
- Le exemple de Download file avec Spring Boot
- Le exemple de Upload file avec Spring Boot et jQuery Ajax
- Le exemple de Upload file avec Spring Boot et AngularJS
- Créer une application Web Panier avec Spring Boot, Hibernate
- Le Tutoriel de Spring Email
- Créer une application Chat simple avec Spring Boot et Websocket
- Déployer le application Spring Boot sur Tomcat Server
- Déployer le application Spring Boot sur Oracle WebLogic Server
- Installer un certificat SSL gratuit Let's Encrypt pour Spring Boot
- Configurer Spring Boot pour rediriger HTTP vers HTTPS
Show More