React(TypeScript)无AD认证下如何获取Blob的SAS URL/SAS令牌?
Got it, since your React/TypeScript app isn’t using AD authentication, you can’t generate SAS URLs directly in the frontend—that would expose your storage account keys, which is a huge security red flag. Instead, you’ll need a backend service to handle the SAS generation safely, with your frontend only requesting the pre-signed URL when needed. Here’s a step-by-step implementation:
Step 1: Build a Backend Proxy (Node.js + TS Example)
We’ll use a simple Express backend to interact with your storage service (I’ll assume Azure Blob Storage here, since SAS is most commonly used with Azure; adjust the SDK if you’re using another provider like AWS S3).
Install Dependencies
npm install @azure/storage-blob dotenv express cors npm install -D typescript @types/express @types/cors @types/node
Backend Code (server.ts)
import { BlobServiceClient, generateBlobSASQueryParameters, BlobSASPermissions } from "@azure/storage-blob"; import dotenv from "dotenv"; import express from "express"; import cors from "cors"; dotenv.config(); const app = express(); // Allow CORS from your frontend domain (replace with your actual URL in production) app.use(cors({ origin: "http://localhost:3000" })); app.use(express.json()); // Load storage credentials from environment variables (never hardcode these!) const STORAGE_ACCOUNT_NAME = process.env.AZURE_STORAGE_ACCOUNT_NAME!; const STORAGE_ACCOUNT_KEY = process.env.AZURE_STORAGE_ACCOUNT_KEY!; const CONTAINER_NAME = process.env.AZURE_BLOB_CONTAINER_NAME!; // Endpoint to generate and return SAS URL app.post("/api/get-sas-url", async (req, res) => { const { documentName } = req.body; if (!documentName) { return res.status(400).json({ error: "Document name is required" }); } try { // Initialize blob service client const blobServiceClient = BlobServiceClient.fromConnectionString( `DefaultEndpointsProtocol=https;AccountName=${STORAGE_ACCOUNT_NAME};AccountKey=${STORAGE_ACCOUNT_KEY};EndpointSuffix=core.windows.net` ); const containerClient = blobServiceClient.getContainerClient(CONTAINER_NAME); const blobClient = containerClient.getBlobClient(documentName); // Configure SAS permissions and expiration (adjust based on your needs) const sasConfig = { containerName: CONTAINER_NAME, blobName: documentName, permissions: BlobSASPermissions.parse("r"), // Read-only access expiresOn: new Date(Date.now() + 3600 * 1000), // Expire after 1 hour }; // Generate SAS token const sasToken = generateBlobSASQueryParameters( sasConfig, STORAGE_ACCOUNT_KEY, STORAGE_ACCOUNT_NAME ).toString(); // Combine blob URL with SAS token const sasUrl = `${blobClient.url}?${sasToken}`; res.json({ sasUrl }); } catch (err) { console.error("Failed to generate SAS URL:", err); res.status(500).json({ error: "Could not generate SAS URL" }); } }); const PORT = process.env.PORT || 3001; app.listen(PORT, () => { console.log(`Backend running on port ${PORT}`); });
Environment Variables (.env)
AZURE_STORAGE_ACCOUNT_NAME=your-storage-account-name AZURE_STORAGE_ACCOUNT_KEY=your-storage-account-key AZURE_BLOB_CONTAINER_NAME=your-container-name
Step 2: Frontend React/TS Implementation
Now, update your React app to pull the document name from query parameters and request the SAS URL from your backend.
import { useEffect, useState } from "react"; const DocumentManager = () => { const [sasUrl, setSasUrl] = useState<string | null>(null); const [errorMessage, setErrorMessage] = useState<string | null>(null); useEffect(() => { // Extract document name from query parameters const searchParams = new URLSearchParams(window.location.search); const documentName = searchParams.get("documentName"); if (documentName) { fetchSasUrl(documentName); } else { setErrorMessage("No document name found in URL query parameters"); } }, []); const fetchSasUrl = async (docName: string) => { try { const response = await fetch("http://localhost:3001/api/get-sas-url", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ documentName: docName }), }); if (!response.ok) { throw new Error("Failed to retrieve SAS URL"); } const data = await response.json(); setSasUrl(data.sasUrl); // Do something with the SAS URL here (e.g., preview, download) console.log("Ready to use SAS URL:", data.sasUrl); } catch (err) { setErrorMessage(err instanceof Error ? err.message : "Unknown error occurred"); } }; return ( <div className="document-manager"> {errorMessage && <p className="error">{errorMessage}</p>} {sasUrl && ( <div> <h3>SAS URL Generated</h3> <p>{sasUrl}</p> {/* Add your后续处理 logic here */} </div> )} </div> ); }; export default DocumentManager;
Critical Security & Best Practices
- Never expose storage keys in frontend: Frontend code is client-side, so any keys you include will be visible to users. Always keep keys in backend environment variables.
- Limit SAS permissions: Only grant the permissions you need (e.g.,
rfor read-only if you just need to view the document). Avoid overprivileged access. - Shorten SAS expiration: Set a reasonable expiration time (1 hour or less is ideal) to minimize the risk of stolen SAS URLs being abused.
- CORS configuration: Ensure your storage account and backend allow requests from your frontend domain. For Azure Blob Storage, configure CORS rules in the Azure Portal to permit your frontend origin.
- Validate input: In the backend, validate that the document name is valid (e.g., no path traversal attempts) before generating the SAS URL.
If you’re using a different storage provider (like AWS S3), the core idea stays the same: use a backend to generate pre-signed URLs instead of doing it client-side.
内容的提问来源于stack exchange,提问作者vaibhav

