Formulaire de connexion personnalisé. Configurer la sécurité Spring pour obtenir une réponse JSON

Arnaud Denoyelle :

J'ai une application simple qui est divisée en 2 parties:

  • Un backend qui expose les services REST avec Spring-boot / Spring-security
  • Un frontend qui ne contient que des fichiers statiques.

Les demandes sont reçues par un serveur nginx qui écoute sur le port 80.

  • Si l'URL de la demande commence par / api /, la demande est redirigée vers le backend.
  • Sinon, la demande est gérée par nginx qui sert les fichiers statiques.

J'ai créé un formulaire de connexion personnalisé (dans la partie frontend) et j'essaie de configurer le serveur Spring-boot.

Il y a beaucoup d'exemples où je peux voir comment définir une URL "succès de connexion" et une URL "erreur de connexion" mais je ne veux pas que Spring-security redirige l'utilisateur. Je veux que Spring-security réponde avec HTTP 200 si la connexion a réussi ou HTTP 40x si la connexion a échoué.

En d'autres termes: je veux que le backend ne réponde qu'avec JSON, jamais HTML.

Jusqu'à présent, lorsque je soumets le formulaire de connexion, la demande est redirigée et j'obtiens le formulaire de connexion Spring par défaut comme réponse.

J'ai essayé d'utiliser .formLogin().loginProcessingUrl("/login");au lieu de loginPage(""):

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
      .withUser("user").password("password").roles("ADMIN");
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
        .anyRequest().authenticated()
        .and()
      .formLogin()
        .loginProcessingUrl("/login");
Arnaud Denoyelle :

Grâce à M. Deinum et grâce à ce guide , j'ai pu trouver la solution.

Tout d'abord, j'ai eu un problème de configuration avec le formulaire de connexion lui-même. Comme le backend a un chemin de contexte défini sur /api, le formulaire personnalisé aurait dû soumettre les paramètres du formulaire à /api/loginmais je soumettais en fait les données à /api/login/(remarquez le supplément /à la fin).

En conséquence, j'essayais sans le savoir d'accéder à une ressource protégée! Par conséquent, la demande a été traitée par défaut, AuthenticationEntryPointdont le comportement par défaut consiste à rediriger l'utilisateur vers la page de connexion.

Comme solution, j'ai implémenté un AuthenticationEntryPoint personnalisé:

private AuthenticationEntryPoint authenticationEntryPoint() {
  return new AuthenticationEntryPoint() {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
      httpServletResponse.getWriter().append("Not authenticated");
      httpServletResponse.setStatus(401);
    }
  };
}

Puis utilisé dans la configuration:

http
  .exceptionHandling()
  .authenticationEntryPoint(authenticationEntryPoint())

et j'ai fait de même pour les autres gestionnaires:

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("user").password("password").roles("ADMIN");
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
          .anyRequest().authenticated()
        .and()
          .formLogin()
          .successHandler(successHandler())
          .failureHandler(failureHandler())
        .and()
          .exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler())
            .authenticationEntryPoint(authenticationEntryPoint())
        .and()
          .csrf().csrfTokenRepository(csrfTokenRepository()).and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
    ;
  }

  private AuthenticationSuccessHandler successHandler() {
    return new AuthenticationSuccessHandler() {
      @Override
      public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        httpServletResponse.getWriter().append("OK");
        httpServletResponse.setStatus(200);
      }
    };
  }

  private AuthenticationFailureHandler failureHandler() {
    return new AuthenticationFailureHandler() {
      @Override
      public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.getWriter().append("Authentication failure");
        httpServletResponse.setStatus(401);
      }
    };
  }

  private AccessDeniedHandler accessDeniedHandler() {
    return new AccessDeniedHandler() {
      @Override
      public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.getWriter().append("Access denied");
        httpServletResponse.setStatus(403);
      }
    };
  }

  private AuthenticationEntryPoint authenticationEntryPoint() {
    return new AuthenticationEntryPoint() {
      @Override
      public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.getWriter().append("Not authenticated");
        httpServletResponse.setStatus(401);
      }
    };
  }

  private Filter csrfHeaderFilter() {
    return new OncePerRequestFilter() {
      @Override
      protected void doFilterInternal(HttpServletRequest request,
                                      HttpServletResponse response, FilterChain filterChain)
          throws ServletException, IOException {
        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
            .getName());
        if (csrf != null) {
          Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
          String token = csrf.getToken();
          if (cookie == null || token != null
              && !token.equals(cookie.getValue())) {
            cookie = new Cookie("XSRF-TOKEN", token);
            cookie.setPath("/");
            response.addCookie(cookie);
          }
        }
        filterChain.doFilter(request, response);
      }
    };
  }

  private CsrfTokenRepository csrfTokenRepository() {
    HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
    repository.setHeaderName("X-XSRF-TOKEN");
    return repository;
  }
}

Cet article est collecté sur Internet, veuillez indiquer la source lors de la réimpression.

En cas d'infraction, veuillez [email protected] Supprimer.

modifier le
0

laisse moi dire quelques mots

0commentaires
connexionAprès avoir participé à la revue

Articles connexes

Formulaire de connexion personnalisé de sécurité Spring redirigeant vers la ressource non disponible

difficulté à connecter Angular 10 avec la sécurité Spring pour utiliser la page de connexion personnalisée

Comment configurer Spring-Boot avec sécurité pour le rôle d'utilisateur de schéma SQL personnalisé?

Configurer Spring Security pour renvoyer la réponse JSON après l'authentification

Erreur 403 pour la sécurité de printemps de la page de connexion personnalisée

Sécurité Spring BOOT: la page de connexion personnalisée ne s'authentifie jamais

Méthode de sécurité Spring sans formulaire de connexion

Comment utiliser le formulaire html pour soumettre un lien API pour obtenir une réponse JSON

Échec personnaliséHandler lance 401 dans la connexion de sécurité Spring

Comment configurer des réclamations personnalisées sur mon site Web React pour une page de connexion

Le facteur " n'a pas pu obtenir de réponse, une erreur s'est produite lors de la connexion à ."

Comment afficher les champs personnalisés dans la réponse JSON pour les types de publication personnalisés dans Wordpress?

Impossible d'obtenir le message d'erreur Spring Security 4 formulaire de connexion personnalisé

Le formulaire de connexion par défaut de la sécurité Spring disparaît lors de l'ajout de WebSecurityConfigurerAdapter

Comment configurer la sécurité Spring http pour une API de repos qui doit valider un jeton JWT généré par un tiers et que les utilisateurs n'ont pas de mot de passe?

Comment obtenir la réponse json de Spring WebClient

Comment configurer des règles de sécurité pour une application Flutter qui utilise Cloud Firestore ?

Vaadin reste bloqué en boucle lors de l'utilisation de la sécurité Spring avec un formulaire de connexion

Obtenir une seule variable de la réponse json

la déconnexion de la sécurité spring provoque une exception NullPointerException

Comment implémenter une déconnexion lors de l'utilisation de la sécurité basée sur un formulaire

Comment obtenir une ResponseEntity assez formatée pour un modèle de réponse personnalisé?

Comment obtenir une réponse complète pour la réponse échouée de Groovy RestClient

Comment retourner un JSON personnalisé après une connexion réussie dans la gem de devise_token_auth?

La connexion de sécurité Spring arrive toujours dans une page d'erreur sans message

Une erreur s'est produite lors de la connexion xpc pour configurer la session d'arrière-plan

Sécurité de Spring: utilisation d'une page de connexion personnalisée: Comment Spring traite-t-il les données de connexion fournies?

Comment configurer la sécurité de Spring Cloud Data Flow

Impossible de configurer la sécurité Spring Boot - toujours 403

TOP liste

  1. 1

    Comment utiliser HttpClient avec TOUT cert ssl, quelle que soit la « mauvaise » est

  2. 2

    Comment afficher du texte au milieu de div avec une couleur d'arrière-plan différente?

  3. 3

    Résultat de l'échantillonneur JMeter : comprendre le temps de chargement, le temps de connexion et la latence

  4. 4

    Modbus Python Schneider PM5300

  5. 5

    Pourquoi Object.hashCode () ne suit pas la convention du code Java

  6. 6

    Comment faire une recherche partielle et obtenir un score pertinent dans Elasticsearch

  7. 7

    Existe-t-il un moyen de voir si mon bot est hors ligne ?

  8. 8

    Comment choisir le nombre de fragments et de répliques Elasticsearch

  9. 9

    optimiser les opérations du serveur avec elasticsearch: traitement des filigranes de disque bas

  10. 10

    Comment changer la couleur de la police dans R?

  11. 11

    Autocomplete avec java, Redis, Recherche élastique, Mongo

  12. 12

    MasterService d'ElasticSearch prend trop de temps pour calculer l'état du cluster et lancer ProcessClusterEventTimeoutException

  13. 13

    Comment vérifier si un utilisateur spécifique a un rôle? Discord js

  14. 14

    Spring @RequestParam DateTime format comme ISO 8601 Date Heure facultative

  15. 15

    Comment analyser un hachage Ruby plat en un hachage imbriqué?

  16. 16

    Comment créer une nouvelle application dans Dropbox avec des autorisations complètes

  17. 17

    Quelque chose dans le cluster Elasticsearch 7.4 devient de plus en plus lent avec les délais de lecture de temps en temps

  18. 18

    Ajustement non linéaire avec R

  19. 19

    php ajouter et fusionner des données de deux tables

  20. 20

    Exporter la table de l'arborescence vers CSV avec mise en forme

  21. 21

    帶有 Spring Boot 和 Azure AD 的 KeyCloak

chaudétiquette

Archive