在前端專案開發中,使用者認證主要有四種方式: Session、JWT、SSO、OAuth 2.0 。
那麼,這四種方式各有什麼優缺點呢?今天我們就來比較一下吧!
基於 session 的身份驗證是前端和後端系統中常用的使用者身份驗證方法。
它主要依靠伺服器來建立和管理用戶 session 。
session 認證過程包括六個步驟:
使用者登入:使用者在登入頁面輸入其憑證(例如使用者名稱和密碼)。這些憑證從前端發送到後端伺服器進行驗證。
建立 session :驗證憑證後,後端伺服器會建立一個 session ,通常包括一個唯一的 session ID,該 ID 儲存在伺服器上。
傳回 session ID :伺服器將 session ID 傳送回前端,通常透過 cookie。該 cookie 儲存在使用者的瀏覽器中,並將隨後續請求自動發送。
儲存 session ID :瀏覽器儲存 cookie 並自動將其包含在傳送至伺服器的每個請求中。這使得伺服器能夠辨識使用者的 session 並對其進行身份驗證。
session 驗證:伺服器尋找 session ID 並驗證相關的 session 資訊以確定使用者的身份。它還可以使用 session 資料進行權限檢查和存取控制。
session 過期和管理:伺服器可以設定 session 過期時間並定期清除過期的 session 。當使用者登出或 session 逾時時,伺服器將刪除該 session 或使該 session 無效。
從上面的流程我們可以看出,在基於 session 的認證中,前端是不需要主動參與的。關鍵操作在瀏覽器和伺服器之間處理。
簡單易用:管理 session 和使用者身份驗證對於開發人員來說相對簡單。
相容性好:大部分瀏覽器都支援cookies,可以實現自動發送和接收。
可擴展性差:在分散式系統中,多台伺服器可能需要共用 session 存儲,從而增加複雜性。
需要 HTTPS :如果 cookie 被盜,則可能導致 session 劫持。因此,應該使用HTTPS來保護資料傳輸,並採取其他安全措施(例如,設定具有HttpOnly
和Secure
屬性的cookie)。
以下是使用Express實作 session 身份驗證的範例:
const express = require('express');
const session = require('express-session');
const app = express();
// Configure and use express-session middleware
app.use(
session({
secret: 'your-secret-key', // Key used to sign the session ID cookie, ensuring session security
resave: false, // Whether to save the session on every request, even if it hasn’t changed
saveUninitialized: true, // Whether to save an uninitialized session
cookie: {
secure: true, // Whether to send cookies only over HTTPS (requires HTTPS support)
maxAge: 24 * 60 * 60 * 1000, // Cookie expiration time (set to 24 hours)
},
})
);
// Login route handler
app.post('/login', (req, res) => {
// Authenticate user (assume the user has been validated)
const user = { id: 123 }; // Example user ID
req.session.userId = user.id; // Store user ID in session
res.send('Login successful');
});
app.get('/dashboard', (req, res) => {
if (req.session.userId) {
// If the session contains a user ID, the user is logged in
res.send('Dashboard content...');
} else {
// If no user ID is found in the session, the user is not logged in
res.send('Please log in...');
}
});
app.listen(3000, () => {
console.log('Server is listening on port 3000...');
});
JWT認證是目前最常用的認證方式之一。
伺服器傳回代表使用者身分的token 。在請求中,token被加入到請求頭中,用於使用者驗證。
由於 HTTP 請求是無狀態的,因此這種方法也稱為無狀態驗證。
使用者登入:使用者在登入頁面輸入其憑證(例如使用者名稱和密碼),這些憑證會被傳送到後端伺服器進行驗證。
產生 JWT :驗證使用者憑證後,後端伺服器產生 JWT。此令牌通常包含基本使用者資訊(例如,使用者ID)和元資料(例如,到期時間)。
傳回 JWT :伺服器將產生的 JWT 傳回前端,通常以 JSON 回應的形式。
儲存 JWT :前端將 JWT 儲存在客戶端,通常在localStorage
中。在極少數情況下,它可能會儲存在 cookie 中,但這會帶來跨站點腳本(XSS)和跨站點請求偽造(CSRF)等安全風險。
使用 JWT 進行請求:在進行 API 呼叫時,前端將 JWT 令牌附加在Authorization
標頭中(格式: Bearer <token>
)並將其傳送到伺服器。
驗證 JWT :伺服器收到請求後,提取 JWT,驗證其有效性(例如檢查簽名和過期時間)。如果有效,伺服器將處理請求並傳回相應的資源或資料。
回應請求:伺服器處理請求並回傳回應,前端可以根據需要使用該回應。
無狀態:JWT 是自包含的,這意味著伺服器不需要儲存 session 訊息,從而簡化了可擴展性和負載平衡。
跨域支援:JWT 可用於跨域請求(例如, API 和前端分開時)。
以下是使用Express實作 JWT 身份驗證的範例:
const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const secretKey = 'your-secret-key'; // Secret key for signing and verifying JWTs
// Login route that generates a JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
// User authentication (assuming the user is valid)
const user = { id: 1, username: 'user' }; // Example user data
const token = jwt.sign(user, secretKey, { expiresIn: '24h' }); // Generate JWT
res.json({ token }); // Return JWT
});
// Protected route
app.get('/dashboard', (req, res) => {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) {
return res.status(401).send('No token provided');
}
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
return res.status(401).send('Invalid token');
}
res.send('Dashboard content');
});
});
app.listen(3000, () => {
console.log('Server is listening on port 3000...');
});
SSO 身份驗證通常用於「基於套件」的應用程式。透過中央登入系統,使用者只需登入一次即可存取多個應用程式,而無需重新進行身份驗證。
使用者存取應用程式:使用者嘗試存取需要身份驗證的應用程式(稱為服務提供者 (SP) )。
重新導向至身分辨識提供者 (IdP) :由於使用者未登錄,應用程式將其重新導向至SSO 身分提供者 (IdP) (通常稱為登入中心)。登入中心處理使用者身份驗證。
使用者登入:使用者在登入中心輸入其憑證。如果他們已經登入(例如,登入公司的內部 SSO 系統),他們可以繞過此步驟。
SSO 令牌產生:一旦通過身份驗證,身分提供者就會產生一個SSO 令牌(例如,OAuth 令牌或 SAML 斷言)並將使用者重新導向回原始應用程式並附加該令牌。
令牌驗證:應用程式(服務提供者)接收令牌並將其傳送給 SSO 身分提供者進行驗證。 IdP驗證令牌並傳回使用者的身分資訊。
使用者獲得存取權限:身份驗證成功後,應用程式根據使用者的身份資訊授予存取權限。用戶現在可以存取應用程式的受保護資源。
存取其他應用程式:如果用戶存取也使用相同登錄中心的其他應用程式,他們會被重定向到那裡。由於他們已經登錄,登入中心會自動對他們進行身份驗證,並將他們重定向回目標應用程式,以實現無縫登錄。
簡化用戶體驗:用戶只需登入一次即可存取多個應用程式,減少重複登入的努力。
集中管理:管理員可以集中管理使用者身分和存取權限,提高效率和安全性。
增強的安全性:降低密碼外洩的風險,因為使用者只需記住一個密碼。還可以實施更強大的身份驗證機制(例如,多因素身份驗證,MFA)。
單點故障:如果登入中心(SSO 身分提供者)遇到問題,所有依賴它的應用程式都可能會受到影響。
複雜的實作:部署和維護 SSO 解決方案可能很複雜,需要適當的安全配置和系統間的互通性。
基於 XML 的標準,用於在身分辨識提供者 (IdP) 和服務提供者 (SP) 之間交換身分驗證和授權資料。
常用於企業環境。
OAuth 2.0是用於授予第三方存取使用者資源的授權框架。
OpenID Connect建立在 OAuth 2.0 之上,為使用者驗證新增了身分層。
常用於網路和行動應用程式。
OAuth 2.0是用於授權第三方應用程式存取使用者資源的標準協定。範例包括:
Facebook 登入
Discord 登入
App二維碼登入
OAuth 2.0 主要用於授權而不是身份驗證,但它通常與身份驗證結合使用以實現使用者登入。
OAuth 2.0 很複雜,因此在了解其流程之前,我們需要澄清一些關鍵概念。
資源擁有者:通常是擁有受保護資源(例如個人資料、文件)的使用者。
資源伺服器:託管受保護資源並確保只有授權使用者才能存取的伺服器。
客戶端:想要存取受保護資源的應用程式或服務。客戶端必須獲得資源所有者的授權。
授權伺服器:負責驗證資源擁有者並授予客戶端授權的伺服器。它發出允許客戶端存取資源伺服器的存取權杖。
使用者授予授權:當使用者與客戶端應用程式互動時,應用程式請求授權存取使用者的資源。使用者被重定向到授權伺服器。
取得授權碼:如果使用者同意,授權伺服器將產生授權碼並將其發送回客戶端(透過重新導向 URL)。
取得存取權杖:客戶端透過向授權伺服器發出請求來交換授權碼以獲得存取權杖。
存取資源:客戶端使用存取權杖(Access Token)向資源伺服器請求受保護的資源。資源伺服器驗證令牌並傳回請求的資料。
適用於需要使用者互動的網路應用程式。
客戶端在獲得使用者同意後,用授權碼換取存取令牌。
專為公共客戶端(例如單頁應用程式)設計。
使用者直接收到存取令牌而不是授權碼。
出於安全問題,不再推薦此流程。
使用者直接向客戶端提供其使用者名稱和密碼。
客戶端使用這些憑證請求存取權杖。
由於存在安全風險,不建議用於面向公眾的應用程式。
當客戶端應用程式需要存取自己的資源而不是代表使用者行事時使用。
適用於服務之間的 API 存取。
靈活性:支援多種授權流程,適合不同的客戶類型和應用程式場景。
安全性:透過分離授權和身份驗證,OAuth 2.0 增強了安全性。它不使用使用者名稱和密碼,而是依靠令牌進行存取控制。
複雜性:實施和配置 OAuth 2.0 可能具有挑戰性,需要適當的令牌管理和安全性配置。
安全風險:如果令牌洩露,則會帶來安全風險。因此,必須採取適當的安全措施(例如,使用 HTTPS 和安全性令牌管理策略)。
以下是 OAuth 2.0 身份驗證的Express實作:
const express = require('express');
const axios = require('axios');
const app = express();
// OAuth 2.0 Configuration
const clientId = 'your-client-id';
const clientSecret = 'your-client-secret';
const redirectUri = 'http://localhost:3000/callback';
const authorizationServerUrl = 'https://authorization-server.com';
const resourceServerUrl = 'https://resource-server.com';
// Login route - Redirects user to the Authorization Server
app.get('/login', (req, res) => {
const authUrl = `${authorizationServerUrl}/authorize?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}&scope=read`;
res.redirect(authUrl);
});
// Callback route - Handles Authorization Code exchange
app.get('/callback', async (req, res) => {
const { code } = req.query;
if (!code) {
return res.status(400).send('Authorization code is missing');
}
try {
// Exchange authorization code for an access token
const response = await axios.post(`${authorizationServerUrl}/token`, {
grant_type: 'authorization_code',
code,
redirect_uri: redirectUri,
client_id: clientId,
client_secret: clientSecret,
});
const { access_token } = response.data;
// Use the access token to fetch protected resources
const resourceResponse = await axios.get(`${resourceServerUrl}/user-info`, {
headers: { Authorization: `Bearer ${access_token}` },
});
res.json(resourceResponse.data);
} catch (error) {
res.status(500).send('Error during token exchange or resource access');
}
});
app.listen(3000, () => {
console.log('Server is listening on port 3000...');
});
這四種身份驗證方法各有其優點、缺點和適用情況:
session :適用於簡單的伺服器呈現應用程式。
JWT :適用於現代無狀態架構和行動應用。
SSO :最適合多種相關服務的企業環境。
OAuth 2.0 :第三方整合和 API 存取的首選。
Leapcell是用於 Web 託管、非同步任務和 Redis 的下一代無伺服器平台:
多語言支援
免費部署無限專案
無與倫比的成本效率
按需付費,無閒置費用。
例如:25 美元支援 694 萬個請求,平均回應時間為 60 毫秒。
簡化的開發人員體驗
直覺的使用者介面,輕鬆設定。
完全自動化的 CI/CD 管道和 GitOps 整合。
即時指標和日誌記錄可提供可操作的見解。
輕鬆實現可擴展性和高性能
自動擴展以輕鬆處理高並發。
零營運開銷-只需專注於建設。
在文件中探索更多!
在 X 上關注我們: @LeapcellHQ
原文出處:https://dev.to/leapcell/which-authentication-to-use-a-comparison-of-4-popular-approaches-24jc