Comment Accéder aux Objets S3 Privés avec AWS Cognito

Scénario
Supposons que vous développez certaines applications pour votre client. Cependant, il existe des fichiers tels que PDF, Word, Excel, etc. liés aux enregistrements dans les applications. Pour la simplicité du scénario, supposons que ces fichiers sont stockés dans un seul bucket S3 privé sur AWS.
Les utilisateurs doivent pouvoir accéder à ces fichiers concernés depuis le bucket S3 privé via un lien URL dans les applications. Notre solution doit fonctionner comme une solution portable pour tout logiciel interne à l'entreprise.
Introduction
L'objectif de cet article est de montrer comment télécharger des fichiers depuis un bucket S3 privé en utilisant les pools d'utilisateurs Cognito. En plus de Cognito, le flux de Cognito vers API Gateway avec Authorizer et la collaboration entre API Gateway et Lambda sont démontrés.
Autant de captures d'écran que possible ont été partagées pour chaque étape depuis la console AWS. De nombreuses images ont été ajoutées pour rendre les étapes plus claires, en particulier pour les débutants.
Contexte
Quelques lectures préalables peuvent être utiles pour mieux comprendre ce qui est développé dans cet article. Les liens suivants seront particulièrement utiles pour les débutants sur AWS :
Que Faut-il Faire ?
De nombreux flux ou méthodes peuvent être codés pour une telle tâche. Ici, nous allons implémenter la méthode montrée ci-dessous. Une brève description de la façon dont le scénario sera implémenté est présentée dans l'image suivante.
L'image suivante montre que nous devons créer certains éléments tels que le Pool d'Utilisateurs Cognito, les buckets S3, les Méthodes API Gateway, les Fonctions Lambda, etc. Après avoir créé toutes les entités dans l'environnement AWS, nous devons les configurer correctement pour qu'elles puissent toutes fonctionner ensemble en collaboration.

Il est préférable de créer tous les éléments dans l'environnement AWS dans l'ordre inverse. Par exemple, pour utiliser Lambda avec une méthode API, si la fonction Lambda est d'abord développée, elle peut facilement être liée lorsque la méthode API Gateway est créée. De même, nous devons créer le bucket web S3 à l'étape 5 et y placer le fichier
callback.html, afin de pouvoir utiliser ce fichier lors de la création du Pool d'Utilisateurs Cognito à l'étape 6. Bien sûr, ce n'est pas obligatoire, mais cet ordre facilitera le développement. C'est pourquoi cette approche est préférée ici.Plan
Nous allons chercher les réponses aux questions suivantes. N'oubliez pas que vous devez avoir un compte AWS pour appliquer toutes les étapes de cet article.
- Comment Créer un Bucket S3 Privé ?
- Comment Créer une Politique Personnalisée pour Autoriser l'Accès aux Objets du Bucket S3 Privé ?
- Comment Créer une Fonction Lambda pour Accéder aux Objets du Bucket S3 Privé ?
- Comment Créer une API Gateway pour Utiliser la Fonction Lambda ?
- Comment Créer un Bucket S3 Public pour l'Utiliser comme Dossier Web ?
- Comment Créer et Configurer un Pool d'Utilisateurs Cognito ?
- Comment Tester le Scénario ?
1. Comment Créer un Bucket S3 Privé ?
S3 est l'un des services basés sur les régions dans AWS. Les éléments dans les buckets S3 sont appelés objets. Par conséquent, dans AWS, les termes objet et fichier peuvent être utilisés de manière interchangeable pour les buckets S3.
Gardez la case "Bloquer tout accès public" cochée. Un bucket S3 privé a été créé ici. Bien qu'il existe de nombreuses options de configuration supplémentaires, nous le créons avec les valeurs par défaut pour la simplicité de la solution.

Téléchargez quelques objets dans le bucket S3 pour tester l'accès privé. Ensuite, essayez d'accéder à ces objets avec des utilisateurs non autorisés ou des liens d'accès potentiels. Bien que nous connaissions les fichiers PDF, DOC, XLS, etc., dans la terminologie AWS S3, ils sont tous appelés objets.

2. Création d'une Politique pour Autoriser l'Accès aux Objets du Bucket S3 Privé
Dans AWS, IAM (Identity and Access Management) est la base de tous les services ! Les Utilisateurs, Groupes, Rôles et Politiques sont les concepts de base que nous devons connaître.
Il existe de nombreux rôles intégrés et chaque rôle a de nombreuses politiques intégrées qui signifient des permissions. Ceux-ci sont appelés "AWS Managed". Cependant, il est également possible de créer des rôles et politiques "Customer Managed" (Gérés par le Client). Ainsi, une politique personnalisée a été créée ici.
- Créez une politique IAM personnalisée pour récupérer des objets depuis votre bucket S3 privé.
- Trouvez la liste des politiques existantes dans AWS et créez-en une nouvelle pour effectuer uniquement l'opération
GetObjectdepuis votre bucket S3 privé comme montré ci-dessous :

Créez une politique personnalisée comme montré ci-dessous. Sélectionnez S3 comme service et uniquement
GetObject comme action :
Sélectionnez "specific" comme ressource et spécifiez votre bucket S3 privé pour que la politique ait les capacités souhaitées :

Donnez un nom à votre politique et créez-la. Vous pouvez donner n'importe quel nom, mais vous devrez vous en souvenir.

Le résumé de votre politique personnalisée ressemblera à ceci. Il est également possible de créer une politique en utilisant directement ce contenu JSON :

Définition JSON de la Politique :
JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::private-s3-for-interfacing/*"
}
]
}
3. Création d'une Fonction Lambda pour Accéder aux Objets du Bucket S3 Privé
La dernière version de NodeJS a été utilisée ici pour la fonction Lambda. Créez une fonction Lambda et sélectionnez NodeJS. Vous pouvez choisir n'importe quel langage supporté pour la fonction Lambda comme Python, Go, Java, .NET Core, etc.

Lorsque vous créez la fonction Lambda, un exemple de code "hello" est affiché. Nous devons plutôt développer notre propre code.
Comme on peut le voir, l'environnement de développement Lambda ressemble à un IDE léger basé sur le web.

Remplacez le code existant par le court exemple de code fourni. Le nouveau code ressemblera à ceci. Après avoir modifié le code, appuyez sur le bouton "Deploy" pour utiliser la fonction Lambda.
Pour la simplicité du scénario, le nom du bucket est utilisé de manière statique. Le nom du fichier est envoyé comme paramètre avec le nom
fn. Bien que le type de contenu par défaut soit supposé être pdf, il peut s'agir de n'importe quel type de fichier implémenté dans le code de la fonction Lambda. Comme nous préférerons utiliser la fonctionnalité proxy de la fonction Lambda dans la connexion API Gateway, l'en-tête de réponse contient certaines données supplémentaires requises.Code Lambda NodeJS (retour en Blob) :
JavaScript
// Lambda fonksiyonu kodu bu şekilde görünür
// Bu kod yanıtı blob içeriği olarak döndürecektir
// Dosyayı indirmek için eklentilerdeki Callback-to-Download-Blob.html kullanılabilir
const AWS = require('aws-sdk');
const S3= new AWS.S3();
exports.handler = async (event, context) => {
let fileName;
let bucketName;
let contentType;
let fileExt;
try {
bucketName = 'private-s3-for-interfacing';
fileName = event["queryStringParameters"]['fn']
contentType = 'application/pdf';
fileExt = 'pdf';
//------------
fileExt = fileName.split('.').pop();
switch (fileExt) {
case 'pdf': contentType = 'application/pdf'; break;
case 'png': contentType = 'image/png'; break;
case 'gif': contentType = 'image/gif'; break;
case 'jpeg': case 'jpg': contentType = 'image/jpeg'; break;
case 'svg': contentType = 'image/svg+xml'; break;
case 'docx': contentType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; break;
case 'xlsx': contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; break;
case 'pptx': contentType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'; break;
case 'doc': contentType = 'application/msword'; break;
case 'xls': contentType = 'application/vnd.ms-excel'; break;
case 'csv': contentType = 'text/csv'; break;
case 'ppt': contentType = 'application/vnd.ms-powerpoint'; break;
case 'rtf': contentType = 'application/rtf'; break;
case 'zip': contentType = 'application/zip'; break;
case 'rar': contentType = 'application/vnd.rar'; break;
case '7z': contentType = 'application/x-7z-compressed'; break;
default: ;
}
//------------
const data = await S3.getObject({Bucket: bucketName, Key: fileName}).promise();
return {
headers: {
'Content-Type': contentType,
'Content-Disposition': 'attachment; filename=' + fileName, // Başarının anahtarı
'Content-Encoding': 'base64',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
'Access-Control-Allow-Methods': 'GET,OPTIONS'
},
body: data.Body.toString('base64'),
isBase64Encoded: true,
statusCode: 200
}
} catch (err) {
return {
statusCode: err.statusCode || 400,
body: err.message || JSON.stringify(err.message) + ' - fileName: '+ fileName + ' - bucketName: ' + bucketName
}
}
}
Il est également possible d'utiliser du code Python dans la fonction Lambda comme montré ci-dessous :
Python
# Aşağıdaki kod yukarıdaki NodeJS örneği gibi geliştirilebilir
import base64
import boto3
import json
import random
s3 = boto3.client('s3')
def lambda_handler(event, context):
try:
fileName = event['queryStringParameters']['fn']
bucketName = 'private-s3-for-interfacing'
contentType = 'application/pdf'
response = s3.get_object(
Bucket=bucketName,
Key=fileName,
)
file = response['Body'].read()
return {
'statusCode': 200,
'headers': {
'Content-Type': contentType,
'Content-Disposition': 'attachment; filename='+ fileName,
'Content-Encoding': 'base64'
# Gerekirse buraya CORS ile ilgili kodlar eklenebilir
},
'body': base64.b64encode(file).decode('utf-8'),
'isBase64Encoded': True
}
except:
return {
'headers': { 'Content-type': 'text/html' },
'statusCode': 200,
'body': 'Error occurred in Lambda!'
}
Une autre méthode pourrait être de créer une presigned URL avec Lambda :
JavaScript
// Bu yöntem presigned url sağlayacaktır
// Presigned URL bağlantısını kullanmak için Callback-for-preSignedUrl.html dosyası kullanılabilir
var AWS = require('aws-sdk');
var S3 = new AWS.S3({
signatureVersion: 'v4',
});
exports.handler = async (event, context) => {
let fileName;
let bucketName;
let contentType;
bucketName = 'private-s3-for-interfacing';
fileName = event["queryStringParameters"]['fn'];
contentType = 'application/json';
const presignedUrl = S3.getSignedUrl('getObject', {
Bucket: bucketName,
Key: fileName,
Expires: 300 // saniye
});
let responseBody = {'presignedUrl': presignedUrl};
return {
headers: {
'Content-Type': contentType,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
'Access-Control-Allow-Methods': 'GET,OPTIONS'
},
body: JSON.stringify(responseBody),
statusCode: 200
}
};
Lorsque la fonction Lambda est créée, un rôle est créé avec elle. Cependant, ce rôle n'a pas la permission d'accéder aux objets de votre bucket S3 privé. Maintenant, nous devons attacher la politique "Customer Managed" que nous avons créée dans les étapes précédentes à ce rôle créé avec la fonction Lambda.
Après avoir créé la fonction Lambda, nous pouvons trouver le rôle automatiquement créé comme montré ci-dessous :

Attachez la politique personnalisée que vous avez créée à l'étape précédente à ce rôle ; ainsi, la fonction Lambda aura un droit d'accès
GetObject restreint sur votre bucket S3.
C'est tout ce qui doit être fait pour l'accès de Lambda à votre bucket S3. Maintenant, il est temps de créer une méthode AWS Gateway pour utiliser notre fonction Lambda.
4. Création d'une API Gateway pour Utiliser la Fonction Lambda
Créez une AWS Gateway REST API comme montré ci-dessous. On peut voir qu'il y a de nombreuses options, mais nous créons une API "REST" comme "New API". Donnez un nom à votre API Gateway.

Il y a quelques étapes pour créer et exécuter l'AWS GW API :
- Créer l'API
- Créer la Resource
- Créer la Method
- Déployer l'API
Créez une
Resource pour votre REST API comme montré ci-dessous :
La ressource créée ici sera plus tard utilisée dans l'URL de l'API.

Créez une méthode
GET pour la ressource que vous avez créée :
Ici, n'importe quelle méthode HTTP comme
GET, POST, PUT, DELETE, etc. peut être créée. Pour notre besoin, nous ne créons que GET. N'oubliez pas de lier la fonction Lambda que nous avons créée dans les étapes précédentes à cette méthode.Lambda Proxy Integration est cochée ici. Cette approche nous permet de traiter tout le contenu lié à la réponse dans la Fonction Lambda.

Après la création de la méthode
GET, le flux entre la Méthode API Gateway et la fonction Lambda ressemblera à ceci :
Activez CORS pour l'API Gateway comme montré ci-dessous. Les options Default 4xx et Default 5xx peuvent être cochées ; ainsi, même les erreurs peuvent être retournées sans problème.

Après avoir créé et configuré tout ce qui concerne la méthode AWS Gateway, il est maintenant temps de déployer l'API. L'API est déployée sur un stage comme montré. De plus, ce nom de stage sera utilisé dans l'URL publique de l'API.

Après le déploiement, l'URL ressemblera à ceci. Il est maintenant possible d'utiliser ce lien depuis n'importe quelle application.

Pour restreindre l'accès à l'API gateway, nous devons définir un Authorizer (Autorisateur). Nous pouvons définir un Authorizer Cognito comme montré ci-dessous.
Comme on peut le voir dans l'image suivante, l'Authorization est le jeton JWT qui doit être ajouté à la section header de la requête pour utiliser la méthode API autorisée.
Lorsque la Hosted UI Cognito est envoyée avec un utilisateur/mot de passe Cognito, Cognito redirigera l'utilisateur vers l'URL de callback en passant le
id_token et des données state additionnelles.Notez que le jeton que nous devons ajouter à la section header est nommé "Authorization" sous Token Source.

Après que l'Authorizer basé sur Cognito est défini, il peut être utilisé comme ci-dessous :

D'autre part, si vous ne voulez pas définir d'Authorizer pour l'API Gateway, vous pouvez restreindre l'accès à l'URL de l'API avec une "Resource Policy" (Politique de Ressource) comme montré ci-dessous.
Si la Resource Policy est modifiée/ajoutée, l'API doit être redéployée. L'IP montrée comme
xxx.xxx.xxx.xxx peut être l'IP du serveur. Lorsque quelqu'un essaie d'accéder à l'URL depuis une IP différente, le message suivant sera affiché :{"Message":"User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:eu-west-2:********8165:... with an explicit deny"}
Le code JSON de la Resource Policy sera comme suit :
JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "*"
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": "xxx.xxx.xxx.xxx"
}
}
}
]
}
5. Bucket S3 Public pour Utilisation comme Dossier Web
Nous avons besoin de deux buckets S3 pour la solution. Le premier a été créé dans les sections précédentes. Le second est maintenant créé et sera utilisé comme dossier web. Le premier était utilisé comme bucket privé pour stocker tous les fichiers.

Créez un bucket S3 public comme dossier web. Ce bucket contient un fichier
callback.html, afin qu'il puisse être utilisé comme adresse de callback Cognito.
Le bucket S3 pour le web doit être public. Par conséquent, la politique suivante peut être appliquée :
JSON
// Politika JSON'ı bu şekilde görünecektir
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::web-s3-for-interfacing/*"
}
]
}
Télécharger les Fichiers Source
Vous pouvez télécharger
Callback.html et d'autres fichiers source depuis les liens suivants :6. Création et Configuration du Pool d'Utilisateurs Cognito
- Adresse de callback :
https://web-s3-for-interfacing.s3.eu-west-2.amazonaws.com/Callback.html - Flux OAuth 2.0 : cochez l'option "implicit grant".
- Scopes OAuth 2.0 : email, openid, profile.
Examinez le lien hosted UI ci-dessous.
Pour envoyer un paramètre à la page de connexion Cognito hosted, ajoutez un paramètre URL "state" supplémentaire. Le paramètre "state" sera transmis au fichier
Callback.html.Le lien de la Hosted UI Cognito contient de nombreux paramètres URL comme montré ci-dessous :
https://test-for-user-pool-for-s3.auth.eu-west-2.amazoncognito.com/login?client_id=7uuggclp7269oguth08mi2ee04&response_type=token&scope=openid+profile+email&redirect_uri=https://web-s3-for-interfacing.s3.eu-west-2.amazonaws.com/Callback.html&state=fn=testFile.pdfChamps :
client_id=7uuggclp7269oguth08mi2ee04response_type=tokenscope=openid+profile+emailredirect_uri=https://web-s3-for-interfacing.s3.eu-west-2.amazonaws.com/Callback.htmlstate=fn=testFile.pdf
state est un paramètre URL spécial. Il peut être envoyé à la page hosted UI et retourné à la page Callback.html.Une app client doit être créée comme montré ci-dessous :

Les paramètres de l'app client peuvent être confirmés comme montré ci-dessous :

Un nom de domaine doit être défini pour être utilisé comme URL pour la hosted UI.

7. Comment Tester le Scénario ?
Voyons comment tester l'API avec accès restreint en utilisant le Pool d'Utilisateurs Cognito.
N'importe quel utilisateur final peut cliquer sur un lien pour démarrer ce processus. Supposons que nous ayons une page web hébergeant le contenu HTML suivant. Comme on peut le voir, le lien pour chaque fichier est l'URL de la hosted UI Cognito.
Le fichier
LinkToS3Files.html peut être utilisé pour tester le scénario.Télécharger les Fichiers de Test
Conclusion
J'espère que cet article a été utile pour les débutants dans l'environnement cloud AWS.
Services de Cloud Computing
Nous offrons des services de conception d'infrastructure, migration, gestion et optimisation sur les plateformes AWS, Azure et Google Cloud.
Découvrir Notre ServiceContactez-nous
Contactez notre équipe pour des informations détaillées sur nos solutions AWS et cloud computing.
Contact