Sécurité de printemps
Spring Security offre des moyens d’effectuer l’authentification et l’autorisation dans une application web. Nous pouvons utiliser Spring Security dans n’importe quelle application web basée sur un servlet.
- Technologie éprouvée, il est préférable d’utiliser cela plutôt que de réinventer la roue. La sécurité est un aspect où nous devons prendre des précautions supplémentaires, sinon notre application sera vulnérable aux attaquants.
- Empêche certaines des attaques courantes telles que CSRF, les attaques de fixation de session.
- Facile à intégrer dans n’importe quelle application web. Nous n’avons pas besoin de modifier les configurations de l’application web, Spring injecte automatiquement des filtres de sécurité dans l’application web.
- Offre un support pour l’authentification par différentes méthodes – en mémoire, DAO, JDBC, LDAP et bien d’autres encore.
- Permet d’ignorer des motifs d’URL spécifiques, utile pour servir des fichiers HTML statiques, des fichiers image.
- Prise en charge des groupes et des rôles.
Exemple de sécurité de printemps
Nous allons créer une application web et l’intégrer avec Spring Security. Créez une application web en utilisant l’option « Dynamic Web Project » dans Eclipse, afin que notre application web de base soit prête. Assurez-vous de la convertir en projet Maven car nous utilisons Maven pour la construction et le déploiement. Si vous n’êtes pas familier avec ces étapes, veuillez consulter le tutoriel sur les applications web Java. Une fois notre application sécurisée, la structure finale du projet ressemblera à l’image ci-dessous. Nous examinerons trois méthodes d’authentification Spring Security.
- en mémoire
- DAO
- JDBC
Pour JDBC, j’utilise la base de données MySQL et j’ai exécuté le script suivant pour créer les tables des détails des utilisateurs.
CREATE TABLE `Employees` (
`username` varchar(20) NOT NULL DEFAULT '',
`password` varchar(20) NOT NULL DEFAULT '',
`enabled` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Roles` (
`username` varchar(20) NOT NULL DEFAULT '',
`role` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`username`,`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `Employees` (`username`, `password`, `enabled`)
VALUES
('pankaj', 'pankaj123', 1);
INSERT INTO `Roles` (`username`, `role`)
VALUES
('pankaj', 'Admin'),
('pankaj', 'CEO');
commit;
Nous devrons également configurer JDBC DataSource en tant que JNDI dans notre conteneur de servlet. Pour en savoir plus à ce sujet, veuillez lire l’exemple de Tomcat JNDI DataSource.
Dépendances Maven de Spring Security
Voici notre fichier pom.xml final.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>WebappSpringSecurity</groupId>
<artifactId>WebappSpringSecurity</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- Spring Security Artifacts - START -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<!-- Spring Security Artifacts - END -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
Nous avons les dépendances suivantes liées au framework Spring.
- spring-jdbc: Ceci est utilisé pour les opérations JDBC par la méthode d’authentification JDBC. Il nécessite une configuration DataSource en tant que JNDI. Pour un exemple complet de son utilisation, veuillez consulter Exemple Spring DataSource JNDI
- spring-security-taglibs: Bibliothèque de balises Spring Security, je l’ai utilisée pour afficher les rôles des utilisateurs dans la page JSP. La plupart du temps, vous n’en aurez pas besoin cependant.
- spring-security-config: Il est utilisé pour configurer les fournisseurs d’authentification, que ce soit JDBC, DAO, LDAP, etc.
- spring-security-web: Ce composant intègre la sécurité Spring à l’API Servlet. Nous en avons besoin pour intégrer notre configuration de sécurité dans l’application Web.
Notez également que nous utiliserons la fonctionnalité Servlet API 3.0 pour ajouter des auditeurs et des filtres de manière programmative, c’est pourquoi la version de l’API servlet dans les dépendances doit être 3.0 ou supérieure.
Exemple de pages de vue Spring Security
Nous avons des pages JSP et HTML dans notre application. Nous voulons appliquer l’authentification sur toutes les pages sauf les pages HTML. health.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Health Check</title>
</head>
<body>
<h3>Service is up and running!!</h3>
</body>
</html>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="https://www.springframework.org/security/tags" prefix="sec" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Home Page</title>
</head>
<body>
<h3>Home Page</h3>
<p>
Hello <b><c:out value="${pageContext.request.remoteUser}"/></b><br>
Roles: <b><sec:authentication property="principal.authorities" /></b>
</p>
<form action="logout" method="post">
<input type="submit" value="Logout" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</body>
</html>
I have included index.jsp
as welcome-file
in the application deployment descriptor. Spring Security takes care of CSRF attack, so when we are submitting form for logout, we are sending the CSRF token back to server to delete it. The CSRF object set by Spring Security component is _csrf and we are using it’s property name and token value to pass along in the logout request. Let’s look at the Spring Security configurations now.
Exemple d’implémentation DAO de UserDetailsService pour Spring Security
Comme nous allons également utiliser une authentification basée sur DAO, nous devons implémenter l’interface UserDetailsService
et fournir l’implémentation de la méthode loadUserByUsername()
. Idéalement, nous devrions utiliser une ressource quelconque pour valider l’utilisateur, mais pour simplifier, je fais juste une validation de base. AppUserDetailsServiceDAO.java
package com.journaldev.webapp.spring.dao;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class AppUserDetailsServiceDAO implements UserDetailsService {
protected final Log logger = LogFactory.getLog(getClass());
@Override
public UserDetails loadUserByUsername(final String username)
throws UsernameNotFoundException {
logger.info("loadUserByUsername username="+username);
if(!username.equals("pankaj")){
throw new UsernameNotFoundException(username + " not found");
}
// création de détails utilisateur factices, devrait effectuer des opérations JDBC
return new UserDetails() {
private static final long serialVersionUID = 2059202961588104658L;
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return "pankaj123";
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
List auths = new java.util.ArrayList();
auths.add(new SimpleGrantedAuthority("admin"));
return auths;
}
};
}
}
Remarquez que je crée une classe interne anonyme de UserDetails
et la retourne. Vous pouvez créer une classe d’implémentation pour cela, puis l’instancier et la retourner. En général, c’est la méthode à suivre dans les applications réelles.
Exemple d’implémentation de WebSecurityConfigurer pour Spring Security
Nous pouvons implémenter l’interface WebSecurityConfigurer
ou nous pouvons étendre la classe d’implémentation de base WebSecurityConfigurerAdapter
et remplacer les méthodes. SecurityConfig.java
package com.journaldev.webapp.spring.security;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import com.journaldev.webapp.spring.dao.AppUserDetailsServiceDAO;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
// Authentification en mémoire
// auth.inMemoryAuthentication().withUser("pankaj").password("pankaj123").roles("USER");
// en utilisant un DAO UserDetailsService personnalisé
// auth.userDetailsService(new AppUserDetailsServiceDAO());
// en utilisant JDBC
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx
.lookup("java:/comp/env/jdbc/MyLocalDB");
final String findUserQuery = "select username,password,enabled "
+ "from Employees " + "where username = ?";
final String findRoles = "select username,role " + "from Roles "
+ "where username = ?";
auth.jdbcAuthentication().dataSource(ds)
.usersByUsernameQuery(findUserQuery)
.authoritiesByUsernameQuery(findRoles);
}
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
// Spring Security devrait complètement ignorer les URL se terminant par .html
.antMatchers("/*.html");
}
}
Remarquez que nous ignorons tous les fichiers HTML en remplaçant la méthode configure(WebSecurity web)
. Le code montre comment brancher l’authentification JDBC. Nous devons le configurer en fournissant la source de données. Comme nous utilisons des tables personnalisées, nous devons également fournir les requêtes de sélection pour obtenir les détails de l’utilisateur et ses rôles. La configuration de l’authentification en mémoire et basée sur un DAO est facile, elles sont commentées dans le code ci-dessus. Vous pouvez les décommenter pour les utiliser, assurez-vous d’avoir une seule configuration à la fois. Les annotations @Configuration
et @EnableWebSecurity
sont nécessaires, afin que le framework spring sache que cette classe sera utilisée pour la configuration de la sécurité spring. La configuration de la sécurité Spring utilise le patron de conception Builder et en fonction de la méthode d’authentification, certaines des méthodes ne seront pas disponibles ultérieurement. Par exemple, auth.userDetailsService()
retourne l’instance de UserDetailsService
et ensuite nous ne pouvons avoir aucune autre option, comme nous ne pouvons pas définir la source de données après cela.
Intégrer Spring Security Web avec l’API Servlet
La dernière étape consiste à intégrer notre classe de configuration Spring Security à l’API Servlet. Cela peut être fait facilement en étendant la classe AbstractSecurityWebApplicationInitializer
et en passant la classe de configuration de sécurité dans le constructeur de la super classe. SecurityWebApplicationInitializer.java
package com.journaldev.webapp.spring.security;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
Lorsque notre contexte démarre, il utilise ServletContext pour ajouter le listener ContextLoaderListener et enregistrer notre classe de configuration en tant que Filtre Servlet. Notez que cela fonctionnera uniquement dans les conteneurs de servlets conformes à Servlet-3. Donc, si vous utilisez Apache Tomcat, assurez-vous que sa version est 7.0 ou supérieure. Notre projet est prêt, il suffit de le déployer dans votre conteneur de servlets préféré. J’utilise Apache Tomcat-7 pour exécuter cette application. Les images ci-dessous montrent la réponse dans divers scénarios.
Accéder à une page HTML sans sécurité
Échec d’authentification pour des mauvaises informations d’identification
Page d’accueil avec authentification JDBC de Spring Security
Page d’accueil avec l’authentification DAO de Spring Security
Page d’accueil avec l’authentification en mémoire de Spring Security
Page de déconnexion
Si vous souhaitez utiliser un conteneur Servlet qui ne prend pas en charge les spécifications Servlet 3, vous devrez alors enregistrer
DispatcherServlet
via le descripteur de déploiement. Consultez la JavaDoc de WebApplicationInitializer
pour plus de détails. C’est tout pour le tutoriel exemple de Spring Security et son intégration dans une application Web basée sur Servlet. Veuillez télécharger le projet d’exemple via le lien ci-dessous et explorez-le pour en savoir plus.
Source:
https://www.digitalocean.com/community/tutorials/spring-security-example-tutorial