web-dev-qa-db-fra.com

Spring boot + oauth2: Une authentification complète est requise pour accéder à cette ressource.

J'essaie d'implémenter un serveur d'authentification utilisant Spring Boot, Spring Cloud Security et Spring Cloud oauth2.

Je reçois une erreur ci-dessous lorsque j'essaie de frapper http: // localhost: 8080/auth/oauth/token de postman

{ "erreur": "non autorisé", "error_description": "Une authentification complète est requise pour accéder à cette ressource" }

Ci-dessous mon 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>com.teckink.tp</groupId>
<artifactId>tp-auth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>tp-auth-server</name>
<description>Demo project for Spring Boot</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>
    <spring-cloud.version>Finchley.M9</spring-cloud.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
</project>

Classe Débutant:

package com.teckink.tp.authserver;

import Java.util.HashMap;
import Java.util.Map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
@EnableResourceServer
@EnableAuthorizationServer
public class App {
    @RequestMapping(value = { "/user" }, produces = "application/json")
    public Map<String, Object> user(OAuth2Authentication user) {
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("user", user.getUserAuthentication().getPrincipal());
        userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
        return userInfo;
    }


    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

Classe OAuth2Config qui définit le client et son secret:

package com.teckink.tp.authserver.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;

@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("eagleeye")
                .secret("thisissecret")
                .authorizedGrantTypes("refresh_token", "password", "client_credentials")
                .scopes("webclient", "mobileclient");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      endpoints
        .authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
    }
}

Classe WebSecurityConfigurer qui définit un utilisateur, un mot de passe et des rôles en mémoire: 

package com.teckink.tp.authserver.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
        @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

   @Override
    @Bean
    public UserDetailsService userDetailsServiceBean() throws Exception {
        return super.userDetailsServiceBean();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("john.carnell").password("password1").roles("USER")
                .and()
                .withUser("william.woodward").password("password2").roles("USER", "ADMIN");
    }
}

J'appelle Rest API de POSTMAN comme ci-dessous - La demande avec l'écran d'authentification s'affiche comme ci-dessous:  enter image description here

Demande avec les données de formulaire ci-dessous  enter image description here

5
Prithvipal Singh

Lorsque vous exécutez votre exemple d’application, l’exception suivante apparaît.

Java.lang.IllegalArgumentException: aucun PasswordEncoder mappé pour l'id "null"

Dans spring-security-core: 5.0, PasswordEncoder par défaut est créé en tant que DelegatingPasswordEncoder. Par conséquent, lorsque vous stockez les utilisateurs en mémoire, vous fournissez les mots de passe en texte brut, puis lorsque vous essayez de récupérer l'encodeur à partir de DelegatingPasswordEncoder pour valider le mot de passe, il ne peut pas en trouver.

Plus de détails dans ce lien Encodage du mot de passe

Pour remédier à cela, pour une implémentation en production, vous devez activer une instance de BCryptPasswordEncoder 

Pour le développement, vous pouvez essayer les modifications suivantes afin de remplacer le codage du mot de passe en ajoutant {noop} à la valeur du mot de passe. Cela traitera le mot de passe en activant la NoOpPasswordEncoder au lieu de la valeur par défaut DelegatingPasswordEncoder et traitera votre mot de passe en tant que texte brut.

Classe OAuth2Config

clients.inMemory()
            .withClient("eagleeye")
            .secret("{noop}thisissecret")
            .authorizedGrantTypes("refresh_token", "password", "client_credentials")
            .scopes("webclient", "mobileclient");

Classe WebSecurityConfigurer

 auth
                .inMemoryAuthentication()
                .withUser("john.carnell"). password("{noop}password1").roles("USER")
                .and()
                .withUser("william.woodward").password("{noop}password2").roles("USER", "ADMIN");

Maintenant, lorsque vous essayez de Postman, vous pouvez générer un jeton  enter image description here

MODIFIER

Projet Github avec une démo de travail ici

3
Chids

J'ai remarqué que votre type de subvention est répertorié comme "mot de passe" et non "mot de passe" dans votre message.

0
Jaron F

C'est bizarre, la même chose s'est passée ici. Je l'ai résolu en utilisant la commande curl. 

curl -v -X POST  http://url:8080/api/oauth/token -u "yourclientid:yourclient_secret"   -d "grant_type=password"   -d "username=yourusername" -d "password=yourpassword"

Ou Si vous le voulez dans postman, allez dans Autorisation -> sélectionnez le type OAUTH2 -> Obtenir un jeton d'accès entrez la description de l'image ici

0
Dan