Maison  >  Article  >  Sécurisation de l'API REST Spring Boot pour différents points de terminaison à l'aide d'AAD et AWS Cognito

Sécurisation de l'API REST Spring Boot pour différents points de terminaison à l'aide d'AAD et AWS Cognito

王林
王林avant
2024-02-22 13:22:06870parcourir

L'éditeur php Baicao a soigneusement rédigé pour vous un article de questions-réponses Java sur l'utilisation d'AAD et d'AWS Cognito pour protéger l'API REST Spring Boot. Dans cet article, nous explorerons comment tirer parti de ces deux services d'authentification pour protéger différents points de terminaison et garantir que votre API est sûre et sécurisée. Suivez notre guide et découvrez comment implémenter l'authentification et l'autorisation dans votre projet Spring Boot pour rendre votre API REST plus puissante et plus fiable.

Contenu de la question

J'espère que quelqu'un pourra m'aider ici car je ne trouve nulle part de ressources sur ce sujet.

J'ai un restapi Spring Boot, et la configuration actuelle a deux itinéraires : 1. Non autorisé 2. Autorisé via le porteur de aad/entra

Ma méthode de configuration est actuellement configurée comme suit :

@override
protected void configure(httpsecurity http) throws exception {
        super.configure(http);
        http.csrf().disable();
        http.authorizerequests(requests -> requests
                .antmatchers(httpmethod.options, "/**/**").permitall()
                .antmatchers("/api/protected/**").fullyauthenticated()
                .anyrequest().permitall()
        );
}

C'est enveloppé dans un cours qui s'étend aadresourceserverwebsecurityconfigureradapter.

En configurant notre API de cette façon, nous sommes en mesure de sécuriser nos itinéraires comme suit :

@preauthorize("hasauthority('approle_appname.rolename')")
@getmapping(value = "/some-method", produces = mediatype.application_json_value)
public responseentity<list<string>> getstrings() {
    return responseentity.ok(...);
}

Notre API devrait maintenant être étendue pour permettre à de nouveaux types d'utilisateurs d'utiliser le point de terminaison d'autorisation. Ces utilisateurs sont gérés par AWS Cognito. Comment configurer mon websecurityconfigureradapter pour permettre à certains chemins d'être non autorisés, à certains chemins d'être protégés via aad et à certains chemins d'être protégés via aws cognito ?

Le principal problème que je semble avoir est aadresourceserverwebsecurityconfigureradapter de configurer la validation jwt de telle manière qu'elle ne fonctionne qu'avec les porteurs fournis par Microsoft.

Idéalement, j'aimerais quelque chose comme ceci :

@configuration
@enablewebsecurity
@enableglobalmethodsecurity(prepostenabled = true)
public class securityconfig extends websecurityconfigureradapter { 

    @configuration
    @order(1)
    public static class azureadsecurityconfig extends aadresourceserverwebsecurityconfigureradapter {

        @override
        protected void configure(httpsecurity http) throws exception {
            http.authorizerequests(requests -> requests
                    .antmatchers("/api/aad/**").fullyauthenticated()
            );
            http.oauth2resourceserver().jwt([utilize aad jwt validation]);
        }

    }

    @configuration
    @order(2)
    public static class awscognitosecurityconfig extends websecurityconfigureradapter {

        @override
        protected void configure(httpsecurity http) throws exception {
            http.authorizerequests(requests -> requests
                    .antmatchers("/api/cognito/**").fullyauthenticated()
            );
            http.oauth2resourceserver().jwt([utilize aws cognito jwt validation]);
        }
    }

    @configuration
    @order(3)
    public static class defaultsecurityconfig extends websecurityconfigureradapter {

            @override
            protected void configure(httpsecurity http) throws exception {
                http.csrf().disable();
                http.authorizerequests(requests -> requests
                        .antmatchers(httpmethod.options, "/**/**").permitall()
                        .anyrequest().permitall()
                );
            }
    }

}

Un autre problème que j'ai trouvé était aadresourceserverwebsecurityconfigureradapter la définition automatique de tous les préfixes possibles pour les noms jwtclaim "roles" et "scp" sur "scope_" et "approle_". Idéalement, j'aimerais qu'ils soient différents pour aad et aws cognito afin que je préfixe "aad_scope_", "aad_approle_" et "cognito_group_".

J'ai trouvé des informations expliquant comment implémenter l'authentification jwt multi-tenant pour Spring Boot, mais elles utilisent toutes uniquement la base de données SQL pour implémenter l'authentification basée sur le mot de passe/l'utilisateur.

Existe-t-il un moyen de réimplémenter toute la logique aad afin de pouvoir intégrer la validation du jwt donné par aws cognito, ou existe-t-il un moyen de prendre la décision basée sur le routage ?

Je sais déjà que vous pouvez configurer l'utilisation de jwt dans la fonction httpsecurity 上使用 oauth2resourceserver(), mais je n'ai trouvé que des informations sur la façon d'implémenter cette fonctionnalité pour un seul locataire.

Si quelqu'un a mis en œuvre avec succès ce cas spécifique ou similaire, ou peut me pousser dans la bonne direction, je lui en serais très reconnaissant. Ou peut-être que mon idée est complètement fausse, alors dites-le-moi s'il vous plaît.

Utilisation de la mise à jour des solutions de travail (25 janvier 2024)

Merci à @ch4mp pour la réponse, j'ai réussi. >Réponses de travailb57dbc31e5d94a908e5f7c2d7e1572a0, chaque gestionnaire d'authentification a son propre convertisseur d'authentification et son propre convertisseur d'autorisations pour gérer les revendications sources et vous souhaitez le préfixe requis. Parcourez mes tutoriels ou documentation officielle pour des exemples et des conseils de mise en œuvre. Cette autre réponse peut également aider (les exigences de mappage des autorisations sont complètement différentes, mais le résolveur du gestionnaire d'authentification est similaire).

Utilisez boot 3.2.2 et spring-addons

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>3.2.2</version>
        <relativepath/> <!-- lookup parent from repository -->
    </parent>
    <groupid>com.c4-soft.demo</groupid>
    <artifactid>multi-tenant-resource-server</artifactid>
    <version>0.0.1-snapshot</version>

    <properties>
        <java.version>21</java.version>
        <spring-addons.version>7.3.5</spring-addons.version>
    </properties>

    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-oauth2-resource-server</artifactid>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <dependency>
            <groupid>com.c4-soft.springaddons</groupid>
            <artifactid>spring-addons-starter-oidc</artifactid>
            <version>${spring-addons.version}</version>
        </dependency>

        <dependency>
            <groupid>com.c4-soft.springaddons</groupid>
            <artifactid>spring-addons-starter-oidc-test</artifactid>
            <version>${spring-addons.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
            </plugin>
        </plugins>
    </build>

</project>
@configuration
@enablemethodsecurity
public class securityconf {
}

Modifiez ce qui suit application.yaml pour placer votre propre éditeur :

com:
  c4-soft:
    springaddons:
      oidc:
        ops:
        - iss: https://cognito-idp.us-west-2.amazonaws.com/us-west-2_rzhmglwjl
          authorities:
          - path: $.cognito:groups
            prefix: cognito_group_
        - iss: https://sts.windows.net/0a962d63-6b23-4416-81a6-29f88c553998/
          authorities:
          - path: $.approles.*.displayname
            prefix: aad_approle_
          - path: $.scope
            prefix: aad_scope_
        resourceserver:
          # spring-addons whitelist is for permitall() (rather than isauthenticated())
          # which is probably much safer
          permit-all:
          - /actuator/health/readiness
          - /actuator/health/liveness
          - /v3/api-docs/**
          - /api/public/**

La valeur de path ci-dessus est le chemin json. Vous pouvez utiliser des outils tels que jsonpath.com pour tester les expressions de chemin par rapport à votre propre charge utile de jeton (extraite à l'aide d'outils tels que jwt.io).

Oui, c'est aussi simple que cela. Non, je n'ai omis aucune propriété yaml ni configuration java (si vous ne me croyez pas, testez-le simplement dans un nouveau projet).

示例控制器

@restcontroller
public class greetcontroller {

    @getmapping("/greet")
    @preauthorize("isauthenticated()")
    public string getgreet(authentication auth) {
        return "hello %s! you are granted with %s.".formatted(auth.getname(), auth.getauthorities());
    }

    @getmapping(value = "/strings")
    @preauthorize("hasanyauthority('aad_approle_admin', 'cognito_group_admin')")
    public list<string> getstrings() {
        return list.of("protected", "strings");
    }
}

示例测试

@webmvctest(controllers = greetcontroller.class)
@autoconfigureaddonswebmvcresourceserversecurity
@import(securityconf.class)
class greetcontrollertest {
    @autowired
    mockmvcsupport api;

    @test
    @withanonymoususer
    void givenuserisanonymous_whengetgreet_thenunauthorized() throws unsupportedencodingexception, exception {
        api.get("/greet").andexpect(status().isunauthorized());
    }

    @test
    @withjwt("aad_admin.json")
    void givenuserisaadadmin_whengetgreet_thenok() throws unsupportedencodingexception, exception {
        final var actual = api.get("/greet").andexpect(status().isok()).andreturn().getresponse().getcontentasstring();
        assertequals(
            "hello aad-admin! you are granted with [aad_approle_msiam_access, aad_approle_admin, aad_scope_openid, aad_scope_profile, aad_scope_machin:truc].",
            actual);
    }

    @test
    @withjwt("cognito_admin.json")
    void givenuseriscognitoadmin_whengetgreet_thenok() throws unsupportedencodingexception, exception {
        final var actual = api.get("/greet").andexpect(status().isok()).andreturn().getresponse().getcontentasstring();
        assertequals("hello amazon-cognito-admin! you are granted with [cognito_group_admin, cognito_group_machin:truc].", actual);
    }

    @test
    @withjwt("aad_machin-truc.json")
    void givenuserisaadmachintruc_whengetgreet_thenok() throws unsupportedencodingexception, exception {
        final var actual = api.get("/greet").andexpect(status().isok()).andreturn().getresponse().getcontentasstring();
        assertequals("hello aad-user! you are granted with [aad_approle_msiam_access, aad_scope_openid, aad_scope_profile, aad_scope_machin:truc].", actual);
    }

    @test
    @withjwt("cognito_machin-truc.json")
    void givenuseriscognitomachintruc_whengetgreet_thenok() throws unsupportedencodingexception, exception {
        final var actual = api.get("/greet").andexpect(status().isok()).andreturn().getresponse().getcontentasstring();
        assertequals("hello amazon-cognito-user! you are granted with [cognito_group_machin:truc].", actual);
    }

    @test
    @withanonymoususer
    void givenuserisanonymous_whengetstrings_thenunauthorized() throws unsupportedencodingexception, exception {
        api.get("/strings").andexpect(status().isunauthorized());
    }

    @test
    @withjwt("aad_admin.json")
    void givenuserisaadadmin_whengetstrings_thenok() throws unsupportedencodingexception, exception {
        final var actual = api.get("/strings").andexpect(status().isok()).andreturn().getresponse().getcontentasstring();
        assertequals("[\"protected\",\"strings\"]", actual);
    }

    @test
    @withjwt("cognito_admin.json")
    void givenuseriscognitoadmin_whengetstrings_thenok() throws unsupportedencodingexception, exception {
        final var actual = api.get("/strings").andexpect(status().isok()).andreturn().getresponse().getcontentasstring();
        assertequals("[\"protected\",\"strings\"]", actual);
    }

    @test
    @withjwt("aad_machin-truc.json")
    void givenuserisaadmachintruc_whengetstrings_thenforbidden() throws unsupportedencodingexception, exception {
        api.get("/strings").andexpect(status().isforbidden());
    }

    @test
    @withjwt("cognito_machin-truc.json")
    void givenuseriscognitomachintruc_whengetstrings_thenforbidden() throws unsupportedencodingexception, exception {
        api.get("/strings").andexpect(status().isforbidden());
    }

}

使用此测试资源:

  • aad_admin.json
{
    "sub": "aad-admin",
    "iss": "https://sts.windows.net/0a962d63-6b23-4416-81a6-29f88c553998/",
    "approles": [
        {
          "allowedmembertypes": [
            "user"
          ],
          "description": "msiam_access",
          "displayname": "msiam_access",
          "id": "ef7437e6-4f94-4a0a-a110-a439eb2aa8f7",
          "isenabled": true,
          "origin": "application",
          "value": null
        },
        {
          "allowedmembertypes": [
            "user"
          ],
          "description": "administrators only",
          "displayname": "admin",
          "id": "4f8f8640-f081-492d-97a0-caf24e9bc134",
          "isenabled": true,
          "origin": "serviceprincipal",
          "value": "administrator"
        }
    ],
    "scope": "openid profile machin:truc"
}
  • aad_machin-truc.json
{
    "sub": "aad-user",
    "iss": "https://sts.windows.net/0a962d63-6b23-4416-81a6-29f88c553998/",
    "approles": [
        {
          "allowedmembertypes": [
            "user"
          ],
          "description": "msiam_access",
          "displayname": "msiam_access",
          "id": "ef7437e6-4f94-4a0a-a110-a439eb2aa8f7",
          "isenabled": true,
          "origin": "application",
          "value": null
        }
    ],
    "scope": "openid profile machin:truc"
}
  • cognito_admin.json
{
    "sub": "amazon-cognito-admin",
    "iss": "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_rzhmglwjl",
    "cognito:groups": ["admin", "machin:truc"],
    "scope": "openid profile cog:scope"
}
  • cognito_machin-truc.json
{
    "sub": "amazon-cognito-user",
    "iss": "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_RzhmgLwjl",
    "cognito:groups": ["machin:truc"],
    "scope": "openid profile cog:scope"
}

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer