portaldacalheta.pt
  • Principal
  • Personnes Et Équipes
  • Science Des Données Et Bases De Données
  • Conception Ux
  • Procédé De Design
Back-End

Spring Security avec JWT pour l'API REST



Printemps est considéré comme un framework de confiance dans l'écosystème Java et est largement utilisé. Il n’est plus valable de faire référence à Spring en tant que cadre, car il s’agit davantage d’un terme générique qui couvre divers cadres. L'un de ces cadres est Sécurité du printemps , qui est un cadre d'authentification et d'autorisation puissant et personnalisable. Il est considéré comme la norme de facto pour la sécurisation des applications Spring.

Malgré sa popularité, je dois admettre qu'en ce qui concerne applications d'une seule page , la configuration n’est ni simple ni directe. Je soupçonne que la raison est que cela a commencé plus comme un Application MVC -infrastructure orientée, où le rendu de page Web se produit côté serveur et la communication est basée sur la session.



Si le back-end est basé sur Java et Spring, il est logique d'utiliser Spring Security pour l'authentification / l'autorisation et de le configurer pour une communication sans état. Bien qu'il y ait beaucoup d'articles expliquant comment cela est fait, pour moi, c'était toujours frustrant de le configurer pour la première fois, et j'ai dû lire et résumer des informations provenant de plusieurs sources. C’est pourquoi j’ai décidé d’écrire cet article, dans lequel j’essaierai de résumer et de couvrir tous les détails subtils et les faiblesses que vous pourriez rencontrer pendant le processus de configuration.



Définition de la terminologie

Avant de plonger dans les détails techniques, je souhaite définir explicitement la terminologie utilisée dans le contexte de Spring Security juste pour être sûr que nous parlons tous la même langue.



Voici les conditions auxquelles nous devons répondre:

  • Authentification fait référence au processus de vérification de l'identité d'un utilisateur, sur la base des informations d'identification fournies. Un exemple courant est la saisie d'un nom d'utilisateur et d'un mot de passe lorsque vous vous connectez à un site Web. Vous pouvez le considérer comme une réponse à la question Qui êtes vous? .
  • Autorisation fait référence au processus consistant à déterminer si un utilisateur a l'autorisation appropriée pour effectuer une action particulière ou lire des données particulières, en supposant que l'utilisateur est authentifié avec succès. Vous pouvez le considérer comme une réponse à la question Un utilisateur peut-il faire / lire ceci? .
  • Principe fait référence à l'utilisateur actuellement authentifié.
  • Autorité accordée fait référence à l'autorisation de l'utilisateur authentifié.
  • Rôle fait référence à un groupe d'autorisations de l'utilisateur authentifié.

Créer une application Spring de base

Avant de passer à la configuration du framework Spring Security, créons une application Web Spring de base. Pour cela, nous pouvons utiliser un Spring Initializr et générer un projet modèle. Pour une application Web simple, seule une dépendance de framework Web Spring suffit:



org.springframework.boot spring-boot-starter-web

Une fois que nous avons créé le projet, nous pouvons y ajouter un simple contrôleur REST comme suit:

@RestController @RequestMapping('hello') public class HelloRestController { @GetMapping('user') public String helloUser() { return 'Hello User'; } @GetMapping('admin') public String helloAdmin() { return 'Hello Admin'; } }

Après cela, si nous construisons et exécutons le projet, nous pouvons accéder aux URL suivantes dans le navigateur Web:



  • http://localhost:8080/hello/user renverra la chaîne Hello User.
  • http://localhost:8080/hello/admin renverra la chaîne Hello Admin.

Maintenant, nous pouvons ajouter le framework Spring Security à notre projet, et nous pouvons le faire en ajoutant la dépendance suivante à notre pom.xml fichier:

org.springframework.boot spring-boot-starter-security

L'ajout d'autres dépendances du framework Spring n'a normalement pas d'effet immédiat sur une application tant que nous ne fournissons pas la configuration correspondante, mais Spring Security est différent en ce qu'il a un effet immédiat, ce qui déroute généralement les nouveaux utilisateurs. Après l'avoir ajouté, si nous reconstruisons et exécutons le projet, puis essayons d'accéder à l'une des URL susmentionnées au lieu d'afficher le résultat, nous serons redirigés vers http://localhost:8080/login. Il s'agit du comportement par défaut car le framework Spring Security requiert une authentification immédiate pour toutes les URL.



Pour réussir l'authentification, nous pouvons utiliser le nom d'utilisateur par défaut user et trouvez un mot de passe généré automatiquement dans notre console:

Using generated security password: 1fc15145-dfee-4bec-a009-e32ca21c77ce

N'oubliez pas que le mot de passe change chaque fois que nous réexécutons l'application. Si nous voulons changer ce comportement et rendre le mot de passe statique, nous pouvons ajouter la configuration suivante à notre application.properties fichier:



spring.security.user.password=Test12345_

Maintenant, si nous entrons les informations d'identification dans le formulaire de connexion, nous serons redirigés vers notre URL et nous verrons le résultat correct. Veuillez noter que le processus d'authentification prêt à l'emploi est basé sur la session, et si nous voulons nous déconnecter, nous pouvons accéder à l'URL suivante: http://localhost:8080/logout

traitement (langage de programmation)

Ce comportement prêt à l'emploi peut être utile pour les applications Web MVC classiques où nous avons une authentification basée sur la session, mais dans le cas des applications d'une seule page, il n'est généralement pas utile car dans la plupart des cas d'utilisation, nous avons rendu et authentification sans état basée sur JWT. Dans ce cas, nous devrons fortement personnaliser le framework Spring Security, ce que nous ferons dans la suite de l'article.



A titre d'exemple, nous allons implémenter un classique application web librairie et créer un back-end qui fournira des API CRUD pour créer des auteurs et des livres, ainsi que des API pour la gestion des utilisateurs et l'authentification.

Présentation de l'architecture de sécurité Spring

Avant de commencer à personnaliser la configuration, voyons d'abord comment l'authentification Spring Security fonctionne en arrière-plan.

Le diagramme suivant présente le flux et montre comment les demandes d'authentification sont traitées:

Architecture de sécurité Spring

Architecture de sécurité Spring

Maintenant, décomposons ce diagramme en composants et discutons chacun d'eux séparément.

Chaîne de filtres de sécurité à ressort

Lorsque vous ajoutez le framework Spring Security à votre application, il enregistre automatiquement une chaîne de filtres qui intercepte toutes les demandes entrantes. Cette chaîne se compose de différents filtres, et chacun d'eux gère un cas d'utilisation particulier.

Par exemple:

  • Vérifiez si l'URL demandée est accessible publiquement, en fonction de la configuration.
  • En cas d'authentification basée sur la session, vérifiez si l'utilisateur est déjà authentifié dans la session en cours.
  • Vérifiez si l'utilisateur est autorisé à effectuer l'action demandée, et ainsi de suite.

Un détail important que je veux mentionner est que les filtres Spring Security sont enregistrés avec l'ordre le plus bas et sont les premiers filtres invoqués. Pour certains cas d'utilisation, si vous souhaitez placer votre filtre personnalisé devant eux, vous devrez ajouter un remplissage à leur commande. Cela peut être fait avec la configuration suivante:

spring.security.filter.order=10

Une fois que nous ajoutons cette configuration à notre application.properties fichier, nous aurons de l'espace pour 10 filtres personnalisés devant les filtres Spring Security.

AuthenticationManager

Vous pouvez penser à AuthenticationManager en tant que coordinateur où vous pouvez enregistrer plusieurs fournisseurs, et en fonction du type de demande, il fournira une demande d'authentification au fournisseur approprié.

AuthenticationProvider

AuthenticationProvider traite des types spécifiques d'authentification. Son interface n'expose que deux fonctions:

  • authenticate effectue l'authentification avec la demande.
  • supports vérifie si ce fournisseur prend en charge le type d'authentification indiqué.

Une implémentation importante de l'interface que nous utilisons dans notre exemple de projet est DaoAuthenticationProvider, qui récupère les détails de l'utilisateur à partir d'un UserDetailsService.

UserDetailsService

UserDetailsService est décrite comme une interface principale qui charge des données spécifiques à l'utilisateur dans la documentation Spring.

Dans la plupart des cas d'utilisation, les fournisseurs d'authentification extraient les informations d'identité des utilisateurs en fonction des informations d'identification d'une base de données, puis effectuent la validation. Parce que ce cas d'utilisation est si courant, les développeurs Spring ont décidé de l'extraire en tant qu'interface distincte, ce qui expose la fonction unique:

  • loadUserByUsername accepte le nom d'utilisateur comme paramètre et renvoie l'objet d'identité de l'utilisateur.

Authentification à l'aide de JWT avec Spring Security

Après avoir discuté des éléments internes du framework Spring Security, configurons-le pour l'authentification sans état avec un Jeton JWT .

Pour personnaliser Spring Security, nous avons besoin d'une classe de configuration annotée avec @EnableWebSecurity annotation dans notre classpath. De plus, pour simplifier le processus de personnalisation, le framework expose un WebSecurityConfigurerAdapter classe. Nous allons étendre cet adaptateur et remplacer ses deux fonctions afin de:

  1. Configurer le gestionnaire d'authentification avec le bon fournisseur
  2. Configurer la sécurité Web (URL publiques, URL privées, autorisation, etc.)
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // TODO configure authentication manager } @Override protected void configure(HttpSecurity http) throws Exception { // TODO configure web security } }

Dans notre exemple d'application, nous stockons les identités des utilisateurs dans une base de données MongoDB, dans le répertoire users collection. Ces identités sont mappées par le User entité, et leurs opérations CRUD sont définies par UserRepo Référentiel Spring Data.

Désormais, lorsque nous acceptons la demande d'authentification, nous devons récupérer l'identité correcte de la base de données à l'aide des informations d'identification fournies, puis la vérifier. Pour cela, nous avons besoin de l'implémentation de UserDetailsService interface, qui est définie comme suit:

public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }

Ici, nous pouvons voir qu'il est nécessaire de retourner l'objet qui implémente le UserDetails interface, et notre User l'entité l'implémente (pour plus de détails sur la mise en œuvre, veuillez consulter le référentiel de l'exemple de projet). Compte tenu du fait qu'il expose uniquement le prototype à fonction unique, nous pouvons le traiter comme une interface fonctionnelle et fournir une implémentation comme une expression lambda.

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final UserRepo userRepo; public SecurityConfig(UserRepo userRepo) { this.userRepo = userRepo; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(username -> userRepo .findByUsername(username) .orElseThrow( () -> new UsernameNotFoundException( format('User: %s, not found', username) ) )); } // Details omitted for brevity }

Ici, le auth.userDetailsService l'appel de fonction lancera DaoAuthenticationProvider en utilisant notre implémentation de UserDetailsService interface et enregistrez-le dans le gestionnaire d'authentification.

Avec le fournisseur d'authentification, nous devons configurer un gestionnaire d'authentification avec le schéma de codage de mot de passe correct qui sera utilisé pour la vérification des informations d'identification. Pour cela, nous devons exposer l'implémentation préférée de PasswordEncoder interface comme un bean.

Dans notre exemple de projet, nous utiliserons le bcrypt algorithme de hachage de mot de passe.

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final UserRepo userRepo; public SecurityConfig(UserRepo userRepo) { this.userRepo = userRepo; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(username -> userRepo .findByUsername(username) .orElseThrow( () -> new UsernameNotFoundException( format('User: %s, not found', username) ) )); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // Details omitted for brevity }

Après avoir configuré le gestionnaire d'authentification, nous devons maintenant configurer la sécurité Web. Nous implémentons une API REST et avons besoin d'une authentification sans état avec un jeton JWT; par conséquent, nous devons définir les options suivantes:

  • Activer CORS et désactiver CSRF .
  • Définissez la gestion de session sur sans état.
  • Définissez le gestionnaire d'exceptions des demandes non autorisées.
  • Définissez les autorisations sur les points de terminaison.
  • Ajoutez un filtre de jeton JWT.

Cette configuration est implémentée comme suit:

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final UserRepo userRepo; private final JwtTokenFilter jwtTokenFilter; public SecurityConfig(UserRepo userRepo, JwtTokenFilter jwtTokenFilter) { this.userRepo = userRepo; this.jwtTokenFilter = jwtTokenFilter; } // Details omitted for brevity @Override protected void configure(HttpSecurity http) throws Exception { // Enable CORS and disable CSRF http = http.cors().and().csrf().disable(); // Set session management to stateless http = http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and(); // Set unauthorized requests exception handler http = http .exceptionHandling() .authenticationEntryPoint( (request, response, ex) -> { response.sendError( HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage() ); } ) .and(); // Set permissions on endpoints http.authorizeRequests() // Our public endpoints .antMatchers('/api/public/**').permitAll() .antMatchers(HttpMethod.GET, '/api/author/**').permitAll() .antMatchers(HttpMethod.POST, '/api/author/search').permitAll() .antMatchers(HttpMethod.GET, '/api/book/**').permitAll() .antMatchers(HttpMethod.POST, '/api/book/search').permitAll() // Our private endpoints .anyRequest().authenticated(); // Add JWT token filter http.addFilterBefore( jwtTokenFilter, UsernamePasswordAuthenticationFilter.class ); } // Used by spring security if CORS is enabled. @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin('*'); config.addAllowedHeader('*'); config.addAllowedMethod('*'); source.registerCorsConfiguration('/**', config); return new CorsFilter(source); } }

Veuillez noter que nous avons ajouté le JwtTokenFilter avant le Spring Security interne UsernamePasswordAuthenticationFilter. Nous faisons cela parce que nous avons besoin d'accéder à l'identité de l'utilisateur à ce stade pour effectuer l'authentification / l'autorisation, et son extraction se produit à l'intérieur du filtre de jeton JWT basé sur le jeton JWT fourni. Ceci est mis en œuvre comme suit:

@Component public class JwtTokenFilter extends OncePerRequestFilter { private final JwtTokenUtil jwtTokenUtil; private final UserRepo userRepo; public JwtTokenFilter(JwtTokenUtil jwtTokenUtil, UserRepo userRepo) { this.jwtTokenUtil = jwtTokenUtil; this.userRepo = userRepo; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // Get authorization header and validate final String header = request.getHeader(HttpHeaders.AUTHORIZATION); if (isEmpty(header) || !header.startsWith('Bearer ')) { chain.doFilter(request, response); return; } // Get jwt token and validate final String token = header.split(' ')[1].trim(); if (!jwtTokenUtil.validate(token)) { chain.doFilter(request, response); return; } // Get user identity and set it on the spring security context UserDetails userDetails = userRepo .findByUsername(jwtTokenUtil.getUsername(token)) .orElse(null); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails == null ? List.of() : userDetails.getAuthorities() ); authentication.setDetails( new WebAuthenticationDetailsSource().buildDetails(request) ); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response); } }

Avant d'implémenter notre fonction API de connexion, nous devons nous occuper d'une étape supplémentaire: nous devons accéder au gestionnaire d'authentification. Par défaut, il n'est pas accessible publiquement et nous devons l'exposer explicitement en tant que bean dans notre classe de configuration.

Cela peut être fait comme suit:

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { // Details omitted for brevity @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }

Et maintenant, nous sommes prêts à implémenter notre fonction API de connexion:

@Api(tags = 'Authentication') @RestController @RequestMapping(path = 'api/public') public class AuthApi { private final AuthenticationManager authenticationManager; private final JwtTokenUtil jwtTokenUtil; private final UserViewMapper userViewMapper; public AuthApi(AuthenticationManager authenticationManager, JwtTokenUtil jwtTokenUtil, UserViewMapper userViewMapper) { this.authenticationManager = authenticationManager; this.jwtTokenUtil = jwtTokenUtil; this.userViewMapper = userViewMapper; } @PostMapping('login') public ResponseEntity login(@RequestBody @Valid AuthRequest request) { try { Authentication authenticate = authenticationManager .authenticate( new UsernamePasswordAuthenticationToken( request.getUsername(), request.getPassword() ) ); User user = (User) authenticate.getPrincipal(); return ResponseEntity.ok() .header( HttpHeaders.AUTHORIZATION, jwtTokenUtil.generateAccessToken(user) ) .body(userViewMapper.toUserView(user)); } catch (BadCredentialsException ex) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } }

Ici, nous vérifions les informations d'identification fournies à l'aide du gestionnaire d'authentification, et en cas de succès, nous générons le jeton JWT et le renvoyons en tant qu'en-tête de réponse avec les informations d'identité de l'utilisateur dans le corps de la réponse.

Autorisation avec Spring Security

Dans la section précédente, nous avons mis en place un processus d'authentification et configuré des URL publiques / privées. Cela peut suffire pour des applications simples, mais pour la plupart des cas d'utilisation réels, nous avons toujours besoin de politiques d'accès basées sur les rôles pour nos utilisateurs. Dans ce chapitre, nous aborderons ce problème et configurerons un schéma d'autorisation basé sur les rôles à l'aide du framework Spring Security.

Dans notre exemple d'application, nous avons défini les trois rôles suivants:

  • USER_ADMIN nous permet de gérer les utilisateurs de l'application.
  • AUTHOR_ADMIN nous permet de gérer les auteurs.
  • BOOK_ADMIN nous permet de gérer des livres.

Maintenant, nous devons les appliquer aux URL correspondantes:

  • api/public est accessible au public.
  • api/admin/user peut accéder aux utilisateurs avec le USER_ADMIN rôle.
  • api/author peut accéder aux utilisateurs avec le AUTHOR_ADMIN rôle.
  • api/book peut accéder aux utilisateurs avec le BOOK_ADMIN rôle.

Le framework Spring Security nous offre deux options pour configurer le schéma d'autorisation:

  • Configuration basée sur l'URL
  • Configuration basée sur les annotations

Voyons d'abord comment fonctionne la configuration basée sur les URL. Il peut être appliqué à la configuration de sécurité Web comme suit:

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { // Details omitted for brevity @Override protected void configure(HttpSecurity http) throws Exception { // Enable CORS and disable CSRF http = http.cors().and().csrf().disable(); // Set session management to stateless http = http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and(); // Set unauthorized requests exception handler http = http .exceptionHandling() .authenticationEntryPoint( (request, response, ex) -> { response.sendError( HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage() ); } ) .and(); // Set permissions on endpoints http.authorizeRequests() // Our public endpoints .antMatchers('/api/public/**').permitAll() .antMatchers(HttpMethod.GET, '/api/author/**').permitAll() .antMatchers(HttpMethod.POST, '/api/author/search').permitAll() .antMatchers(HttpMethod.GET, '/api/book/**').permitAll() .antMatchers(HttpMethod.POST, '/api/book/search').permitAll() // Our private endpoints .antMatchers('/api/admin/user/**').hasRole(Role.USER_ADMIN) .antMatchers('/api/author/**').hasRole(Role.AUTHOR_ADMIN) .antMatchers('/api/book/**').hasRole(Role.BOOK_ADMIN) .anyRequest().authenticated(); // Add JWT token filter http.addFilterBefore( jwtTokenFilter, UsernamePasswordAuthenticationFilter.class ); } // Details omitted for brevity }

Comme vous pouvez le voir, cette approche est simple et directe, mais elle présente un inconvénient. Le schéma d'autorisation de notre application peut être complexe, et si nous définissons toutes les règles en un seul endroit, il deviendra très volumineux, complexe et difficile à lire. Pour cette raison, je préfère généralement utiliser une configuration basée sur des annotations.

Le framework Spring Security définit les annotations suivantes pour la sécurité Web:

à quoi sert le langage c
  • @PreAuthorize les soutiens Langage d'expression Spring et est utilisé pour fournir un contrôle d'accès basé sur des expressions avant exécuter la méthode.
  • @PostAuthorize les soutiens Langage d'expression Spring et est utilisé pour fournir un contrôle d'accès basé sur des expressions après exécuter la méthode (offre la possibilité d'accéder au résultat de la méthode).
  • @PreFilter les soutiens Langage d'expression Spring et est utilisé pour filtrer la collection ou les tableaux avant exécuter la méthode en fonction des règles de sécurité personnalisées que nous définissons.
  • @PostFilter les soutiens Langage d'expression Spring et est utilisé pour filtrer la collection ou les tableaux retournés après l'exécution de la méthode en fonction des règles de sécurité personnalisées que nous définissons (offre la possibilité d'accéder au résultat de la méthode).
  • @Secured ne prend pas en charge Langage d'expression Spring et est utilisé pour spécifier une liste de rôles sur une méthode.
  • @RolesAllowed ne prend pas en charge Langage d'expression Spring et est le JSR 250 Annotation équivalente de @Secured annotation.

Ces annotations sont désactivées par défaut et peuvent être activées dans notre application comme suit:

@EnableWebSecurity @EnableGlobalMethodSecurity( securedEnabled = true, jsr250Enabled = true, prePostEnabled = true ) public class SecurityConfig extends WebSecurityConfigurerAdapter { // Details omitted for brevity }


securedEnabled = true active @Secured annotation.
jsr250Enabled = true active @RolesAllowed annotation.
prePostEnabled = true active @PreAuthorize, @PostAuthorize, @PreFilter, @PostFilter annotations.

Après les avoir activés, nous pouvons appliquer des politiques d'accès basées sur les rôles sur nos points de terminaison d'API comme ceci:

@Api(tags = 'UserAdmin') @RestController @RequestMapping(path = 'api/admin/user') @RolesAllowed(Role.USER_ADMIN) public class UserAdminApi { // Details omitted for brevity } @Api(tags = 'Author') @RestController @RequestMapping(path = 'api/author') public class AuthorApi { // Details omitted for brevity @RolesAllowed(Role.AUTHOR_ADMIN) @PostMapping public void create() { } @RolesAllowed(Role.AUTHOR_ADMIN) @PutMapping('{id}') public void edit() { } @RolesAllowed(Role.AUTHOR_ADMIN) @DeleteMapping('{id}') public void delete() { } @GetMapping('{id}') public void get() { } @GetMapping('{id}/book') public void getBooks() { } @PostMapping('search') public void search() { } } @Api(tags = 'Book') @RestController @RequestMapping(path = 'api/book') public class BookApi { // Details omitted for brevity @RolesAllowed(Role.BOOK_ADMIN) @PostMapping public BookView create() { } @RolesAllowed(Role.BOOK_ADMIN) @PutMapping('{id}') public void edit() { } @RolesAllowed(Role.BOOK_ADMIN) @DeleteMapping('{id}') public void delete() { } @GetMapping('{id}') public void get() { } @GetMapping('{id}/author') public void getAuthors() { } @PostMapping('search') public void search() { } }

Veuillez noter que des annotations de sécurité peuvent être fournies à la fois au niveau de la classe et au niveau de la méthode.

Les exemples illustrés sont simples et ne représentent pas des scénarios du monde réel, mais Spring Security fournit un riche ensemble d'annotations et vous pouvez gérer un schéma d'autorisation complexe si vous choisissez de les utiliser.

fichier principal c++

Préfixe par défaut du nom du rôle

Dans cette sous-section distincte, je tiens à souligner un autre détail subtil qui déroute beaucoup de nouveaux utilisateurs.

Le framework Spring Security différencie deux termes:

  • Authority représente une autorisation individuelle.
  • Role représente un groupe d'autorisations.

Les deux peuvent être représentés avec une seule interface appelée GrantedAuthority et plus tard vérifié avec Spring Expression Language dans les annotations de Spring Security comme suit:

  • Authority: @PreAuthorize ('hasAuthority (‘ EDIT_BOOK ’)»)
  • Role: @PreAuthorize ('hasRole (‘ BOOK_ADMIN ’)»)

Pour rendre la différence entre ces deux termes plus explicite, le framework Spring Security ajoute un ROLE_ préfixe au nom du rôle par défaut. Ainsi, au lieu de rechercher un rôle nommé BOOK_ADMIN, il recherchera ROLE_BOOK_ADMIN.

Personnellement, je trouve ce comportement déroutant et préfère le désactiver dans mes applications. Il peut être désactivé dans la configuration de Spring Security comme suit:

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { // Details omitted for brevity @Bean GrantedAuthorityDefaults grantedAuthorityDefaults() { return new GrantedAuthorityDefaults(''); // Remove the ROLE_ prefix } }

Test avec Spring Security

Pour tester nos points de terminaison avec des tests unitaires ou d'intégration lors de l'utilisation du framework Spring Security, nous devons ajouter spring-security-test dépendance avec le spring-boot-starter-test. Notre pom.xml Le fichier de construction ressemblera à ceci:

org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine org.springframework.security spring-security-test test

Cette dépendance nous donne accès à certaines annotations qui peuvent être utilisées pour ajouter un contexte de sécurité à nos fonctions de test.

Ces annotations sont:

  • @WithMockUser peut être ajouté à une méthode de test pour émuler l'exécution avec un utilisateur fictif.
  • @WithUserDetails peut être ajouté à une méthode de test pour émuler en cours d'exécution avec UserDetails renvoyé par le UserDetailsService.
  • @WithAnonymousUser peut être ajouté à une méthode de test pour émuler l'exécution avec un utilisateur anonyme. Ceci est utile lorsqu'un utilisateur souhaite exécuter une majorité de tests en tant qu'utilisateur spécifique et remplacer quelques méthodes pour rester anonyme.
  • @WithSecurityContext détermine ce que SecurityContext à utiliser, et les trois annotations décrites ci-dessus sont basées dessus. Si nous avons un cas d'utilisation spécifique, nous pouvons créer notre propre annotation qui utilise @WithSecurityContext pour créer n'importe quel SecurityContext nous voulons. Sa discussion sort du cadre de notre article, et veuillez vous référer à la documentation de Spring Security pour plus de détails.

La manière la plus simple d'exécuter les tests avec un utilisateur spécifique est d'utiliser le @WithMockUser annotation. Nous pouvons créer un utilisateur fictif avec lui et exécuter le test comme suit:

@Test @WithMockUser(username=' [email protected] ', roles={'USER_ADMIN'}) public void test() { // Details omitted for brevity }

Cette approche présente cependant quelques inconvénients. Tout d'abord, l'utilisateur fictif n'existe pas et si vous exécutez le test d'intégration, qui interroge ultérieurement les informations utilisateur dans la base de données, le test échouera. Deuxièmement, l'utilisateur fictif est l'instance de org.springframework.security.core.userdetails.User , qui est l'implémentation interne du framework Spring de UserDetails interface, et si nous avons notre propre implémentation, cela peut provoquer des conflits plus tard, lors de l'exécution du test.

Si les inconvénients précédents sont des bloqueurs pour notre application, alors le @WithUserDetails l'annotation est la voie à suivre. Il est utilisé lorsque nous avons personnalisé UserDetails et UserDetailsService implémentations. Il suppose que l'utilisateur existe, nous devons donc soit créer la ligne réelle dans la base de données, soit fournir le UserDetailsService mock instance avant d'exécuter les tests.

Voici comment nous pouvons utiliser cette annotation:

@Test @WithUserDetails(' [email protected] ') public void test() { // Details omitted for brevity }

Il s'agit d'une annotation préférée dans les tests d'intégration de notre exemple de projet, car nous avons des implémentations personnalisées des interfaces susmentionnées.

Utilisation de @WithAnonymousUser permet de s'exécuter en tant qu'utilisateur anonyme. Ceci est particulièrement pratique lorsque vous souhaitez exécuter la plupart des tests avec un utilisateur spécifique mais quelques tests en tant qu'utilisateur anonyme. Par exemple, ce qui suit s'exécutera test1 et test2 cas de test avec un utilisateur fictif et test3 avec un utilisateur anonyme:

@SpringBootTest @AutoConfigureMockMvc @WithMockUser public class WithUserClassLevelAuthenticationTests { @Test public void test1() { // Details omitted for brevity } @Test public void test2() { // Details omitted for brevity } @Test @WithAnonymousUser public void test3() throws Exception { // Details omitted for brevity } }

Emballer

En fin de compte, je voudrais mentionner que le framework Spring Security ne gagnera probablement aucun concours de beauté et qu'il a certainement une courbe d'apprentissage abrupte. J'ai rencontré de nombreuses situations où il a été remplacé par une solution maison en raison de sa complexité de configuration initiale. Mais une fois que les développeurs comprennent ses composants internes et parviennent à mettre en place la configuration initiale, il devient relativement simple à utiliser.

Dans cet article, j'ai essayé de montrer tous les détails subtils de la configuration, et j'espère que vous trouverez les exemples utiles. Pour des exemples de code complets, veuillez vous référer au référentiel Git de mon exemple de projet Spring Security .

Comprendre les bases

Qu'est-ce que Spring Security?

Spring Security est un cadre d'authentification et d'autorisation puissant et hautement personnalisable. C'est la norme de facto pour la sécurisation des applications Spring.

Comment utiliser Spring Security avec l'API REST?

Prêt à l'emploi, Spring Security est livré avec une authentification basée sur la session, ce qui est utile pour les applications Web MVC classiques, mais nous pouvons le configurer pour prendre en charge l'authentification sans état basée sur JWT pour les API REST.

Quelle est la sécurité de Spring Security?

Spring Security est assez sécurisé. Il s'intègre facilement aux applications basées sur Spring, prend en charge de nombreux types d'authentification prêts à l'emploi et est capable de programmer la sécurité déclarative.

Pourquoi Spring Security est-il utilisé?

Parce qu'il s'intègre parfaitement avec d'autres écosystèmes Spring, et de nombreux développeurs préfèrent réutiliser les solutions existantes plutôt que de réinventer la roue.

Qu'est-ce que JWT?

JSON Web Token (JWT) est une norme pour le codage des informations qui peuvent être transmises en toute sécurité en tant qu'objet JSON.

Comment JWT fonctionne-t-il avec Spring Security?

Nous exposons une API POST publique pour l'authentification, et après avoir transmis les informations d'identification correctes, elle générera un JWT. Si un utilisateur tente d'accéder à l'API protégée, il autorisera l'accès uniquement si une requête a un JWT valide. La validation se produira dans le filtre enregistré dans la chaîne de filtres Spring Security.

Dix fonctionnalités Kotlin pour booster le développement Android

Back-End

Dix fonctionnalités Kotlin pour booster le développement Android
Composants angulaires 101 - un aperçu

Composants angulaires 101 - un aperçu

Interface Web

Articles Populaires
Comment mener des entretiens avec des utilisateurs à distance [Infographie]
Comment mener des entretiens avec des utilisateurs à distance [Infographie]
Chercheur UX
Chercheur UX
Une liste complète des conférences de gestion de projet 2020
Une liste complète des conférences de gestion de projet 2020
Comment créer des animations de chargement personnalisées pour réduire les taux de rebond
Comment créer des animations de chargement personnalisées pour réduire les taux de rebond
Utilisation de prétotypes pour soutenir une analyse de rentabilisation
Utilisation de prétotypes pour soutenir une analyse de rentabilisation
 
AI Investment Primer: Jeter les bases (Partie I)
AI Investment Primer: Jeter les bases (Partie I)
ApeeScape annonce les boursiers d'Asie et d'Océanie
ApeeScape annonce les boursiers d'Asie et d'Océanie
eCommerce UX pour l'expérience mobile
eCommerce UX pour l'expérience mobile
À la recherche de l'élite quelques-uns - Trouver et embaucher les meilleurs développeurs de logiciels de l'industrie
À la recherche de l'élite quelques-uns - Trouver et embaucher les meilleurs développeurs de logiciels de l'industrie
Angular vs React: quel est le meilleur pour le développement Web?
Angular vs React: quel est le meilleur pour le développement Web?
Articles Populaires
  • Les langages de programmation orientés objet ne sont plus utilisés par les développeurs de logiciels.
  • usages futurs de la réalité virtuelle
  • différence entre ai et vi
  • quel genre de langue est c
  • combien d'entreprises utilisent sharepoint
  • google cloud platform node js
Catégories
  • Personnes Et Équipes
  • Science Des Données Et Bases De Données
  • Conception Ux
  • Procédé De Design
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt