
Pengesahan ialah salah satu bahagian terpenting dalam mana-mana aplikasi web. Tutorial ini membincangkan sistem pengesahan berasaskan token dan cara ia berbeza daripada sistem log masuk tradisional. Pada penghujung tutorial ini, anda akan melihat demo berfungsi sepenuhnya yang ditulis dalam Angular dan Node.js.
Sistem Pengesahan Tradisional
Sebelum beralih kepada sistem pengesahan berasaskan token, mari kita lihat sistem pengesahan tradisional.
- Pengguna menyediakan nama pengguna dan kata laluan dalam borang log masuk dan klik Log masuk.
- Selepas membuat permintaan, sahkan pengguna di bahagian belakang dengan menanyakan pangkalan data. Jika permintaan itu sah, sesi dibuat menggunakan maklumat pengguna yang diperoleh daripada pangkalan data dan maklumat sesi dikembalikan dalam pengepala respons supaya ID sesi disimpan dalam penyemak imbas.
- Menyediakan maklumat sesi untuk mengakses titik akhir terhad dalam aplikasi anda.
- Jika maklumat sesi adalah sah, benarkan pengguna mengakses titik akhir yang ditentukan dan membalas dengan kandungan HTML yang diberikan.

Setakat ini bagus. Aplikasi web berfungsi dengan baik dan dapat mengesahkan pengguna supaya mereka boleh mengakses titik akhir terhad. Tetapi apakah yang berlaku apabila anda ingin membangunkan pelanggan lain untuk aplikasi anda (seperti pelanggan Android)? Adakah anda dapat mengesahkan pelanggan mudah alih dan menyediakan kandungan terhad menggunakan aplikasi semasa anda? Seperti yang dinyatakan, tidak. Terdapat dua sebab utama untuk ini:
- Sesi dan kuki tidak bermakna untuk aplikasi mudah alih. Anda tidak boleh berkongsi sesi atau kuki yang dibuat di bahagian pelayan dengan pelanggan mudah alih.
- Dalam aplikasi semasa, mengembalikan HTML yang diberikan. Dalam pelanggan mudah alih, anda perlu memasukkan sesuatu seperti JSON atau XML sebagai respons.
Dalam kes ini, anda memerlukan aplikasi bebas pelanggan.
Pengesahan berasaskan token
Dalam pengesahan berasaskan token, kuki dan sesi tidak digunakan. Token akan digunakan untuk mengesahkan pengguna untuk setiap permintaan yang dibuat kepada pelayan. Mari kita reka bentuk semula senario pertama menggunakan pengesahan berasaskan token.
Ia akan menggunakan aliran kawalan berikut:
- Pengguna menyediakan nama pengguna dan kata laluan dalam borang log masuk dan klik Log masuk.
- Selepas membuat permintaan, sahkan pengguna di bahagian belakang dengan membuat pertanyaan dalam pangkalan data. Jika permintaan itu sah, token dibuat menggunakan maklumat pengguna yang diperoleh daripada pangkalan data dan kemudian dikembalikan dalam pengepala respons supaya kami boleh menyimpan penyemak imbas token dalam storan tempatan.
- Berikan maklumat token dalam setiap pengepala permintaan untuk mengakses titik akhir terhad dalam aplikasi anda.
- Jika token yang diperoleh daripada maklumat pengepala permintaan adalah sah, benarkan pengguna mengakses titik akhir yang ditentukan dan bertindak balas dengan JSON atau XML.
Dalam kes ini, kami tidak memulangkan sesi atau kuki, mahupun sebarang kandungan HTML. Ini bermakna kita boleh menggunakan seni bina ini untuk sebarang aplikasi khusus pelanggan. Anda boleh lihat seni bina seni bina di bawah:

Jadi apakah JWT ini?
JWT
JWT bermaksud JSON Web Token dan merupakan format token yang digunakan dalam pengepala kebenaran. Token ini membantu anda mereka bentuk komunikasi antara kedua-dua sistem dengan cara yang selamat. Untuk tujuan tutorial ini, kami akan merumuskan semula JWT sebagai "Token Pembawa". Token pembawa terdiri daripada tiga bahagian: pengepala, muatan dan tandatangan.
-
Header ialah bahagian token yang memegang jenis token dan kaedah penyulitan, yang turut disulitkan menggunakan Base-64.
-
Payloadmengandungi maklumat. Anda boleh memasukkan sebarang jenis data seperti maklumat pengguna, maklumat produk, dsb., yang semuanya disimpan menggunakan penyulitan Base-64.
-
Tandatangan terdiri daripada gabungan pengepala, muatan dan kunci. Kunci mesti disimpan dengan selamat di bahagian pelayan.
Anda boleh lihat skema JWT dan contoh token di bawah:

Anda tidak perlu melaksanakan penjana token pembawa kerana anda boleh menemui pakej mantap untuk banyak bahasa. Anda boleh lihat sebahagian daripadanya di bawah:
Node.js |
https://github.com/auth0/node-jsonwebtoken |
PHP |
http://github.com/firebase/php-jwt |
Jawa |
http://github.com/auth0/java-jwt |
紅寶石 |
https://github.com/jwt/ruby-jwt |
.BERSIH |
https://github.com/auth0/java-jwt |
Python |
http://github.com/progrium/pyjwt/ |
Contoh praktikal
Setelah merangkumi beberapa maklumat asas tentang pengesahan berasaskan token, kini kita boleh beralih kepada contoh praktikal. Lihat seni bina di bawah, kemudian kami akan menganalisisnya dengan lebih terperinci:

- Berbilang pelanggan (seperti aplikasi web atau pelanggan mudah alih) membuat permintaan kepada API untuk tujuan tertentu.
- Permintaan dibuat kepada perkhidmatan seperti
https://api.yourexampleapp.com
. Jika ramai orang menggunakan aplikasi itu, berbilang pelayan mungkin diperlukan untuk menyediakan operasi yang diminta. https://api.yourexampleapp.com
等服務發(fā)出的。如果很多人使用該應用程序,則可能需要多個服務器來提供請求的操作。
- 這里,負載均衡器用于平衡請求,以最適合后端的應用程序服務器。當您向
https://api.yourexampleapp.com
發(fā)出請求時,負載均衡器首先會處理請求,然后會將客戶端重定向到特定服務器。
- 有一個應用程序,并且該應用程序部署到多臺服務器(server-1、server-2、...、server-n)。每當向
https://api.yourexampleapp.com
Di sini, pengimbang beban digunakan untuk mengimbangi permintaan agar paling sesuai dengan pelayan aplikasi di bahagian belakang. Apabila anda membuat permintaan untuk https://api.yourexampleapp.com
, pengimbang beban mula-mula mengendalikan permintaan dan kemudian mengubah hala klien ke pelayan tertentu.
Kelebihan
Pengesahan berasaskan token mempunyai pelbagai kelebihan yang menyelesaikan masalah yang serius. Ini antaranya:
Perkhidmatan bebas pelanggan
Dalam pengesahan berasaskan token, token dihantar melalui pengepala permintaan dan bukannya meneruskan maklumat pengesahan dalam sesi atau kuki. Ini bermakna tidak ada negeri. Anda boleh menghantar permintaan kepada pelayan daripada sebarang jenis klien yang boleh membuat permintaan HTTP.
Rangkaian Penghantaran Kandungan (CDN)
Dalam kebanyakan aplikasi web semasa, paparan dipaparkan pada bahagian belakang dan kandungan HTML dikembalikan kepada penyemak imbas. Logik bahagian hadapan bergantung pada kod bahagian belakang.
Tidak perlu mencipta kebergantungan sedemikian. Ini menimbulkan beberapa persoalan. Contohnya, jika anda bekerja dengan agensi reka bentuk yang melaksanakan HTML, CSS dan JavaScript bahagian hadapan, anda perlu memindahkan kod bahagian hadapan itu ke kod hujung belakang supaya beberapa operasi pemaparan atau pengisian boleh berlaku. Selepas beberapa ketika, kandungan HTML yang anda berikan akan sangat berbeza daripada apa yang dilaksanakan oleh agensi kod.
Dalam pengesahan berasaskan token, anda boleh membangunkan projek bahagian hadapan anda secara berasingan daripada kod bahagian belakang anda. Kod hujung belakang anda akan mengembalikan respons JSON dan bukannya HTML yang diberikan dan anda boleh meletakkan versi kod bahagian hadapan yang dikecilkan dan digzip ke dalam CDN. Apabila anda melawat halaman web, kandungan HTML akan disampaikan daripada CDN dan kandungan halaman akan diisi oleh perkhidmatan API menggunakan token dalam pengepala Kebenaran.
Sesi tanpa kuki (atau tiada CSRF)??
??CSRF adalah masalah utama dalam keselamatan rangkaian moden kerana ia tidak menyemak sama ada sumber permintaan itu boleh dipercayai. Untuk menyelesaikan masalah ini, gunakan kumpulan token untuk menghantar token ini pada setiap siaran borang. Dalam pengesahan berasaskan token, token digunakan dalam pengepala kebenaran, dan CSRF tidak mengandungi maklumat ini. ??
Storan Token Berterusan
Apabila operasi membaca, menulis atau memadam sesi berlaku dalam aplikasi, ia menjalankan operasi fail dalam folder temp
sistem pengendalian, sekurang-kurangnya untuk kali pertama. Katakan anda mempunyai berbilang pelayan dan anda membuat sesi pada pelayan pertama. Apabila anda membuat permintaan lain dan permintaan anda jatuh ke pelayan lain, maklumat sesi tidak akan berada di sana dan anda akan mendapat respons "Tidak Dibenarkan". Saya tahu, anda boleh menyelesaikan masalah ini dengan sesi melekit. Walau bagaimanapun, dalam pengesahan berasaskan token, keadaan ini diselesaikan secara semula jadi. Tiada isu sesi melekit kerana token permintaan dipintas pada setiap permintaan pada mana-mana pelayan. temp
文件夾中進行文件操作,至少第一次是這樣。假設您有多個服務器,并且在第一臺服務器上創(chuàng)建了一個會話。當您發(fā)出另一個請求并且您的請求落入另一臺服務器時,會話信息將不存在并且將得到“未經(jīng)授權”的響應。我知道,你可以通過粘性會話來解決這個問題。然而,在基于令牌的認證中,這種情況自然就解決了。不存在粘性會話問題,因為請求令牌在任何服務器上的每個請求上都會被攔截。
這些是基于令牌的身份驗證和通信的最常見優(yōu)點。關于基于令牌的身份驗證的理論和架構討論就到此結束。是時候看一個實際例子了。
示例應用程序
您將看到兩個應用程序來演示基于令牌的身份驗證:
- 基于令牌的身份驗證后端
- 基于令牌的身份驗證前端
在后端項目中,會有服務的實現(xiàn),服務結果將是JSON格式。服務中沒有返回視圖。在前端項目中,將有一個用于前端 HTML 的 Angular 項目,然后前端應用程序將由 Angular 服務填充,以向后端服務發(fā)出請求。
基于令牌的身份驗證后端
在后端項目中,主要有三個文件:
-
package.json 用于依賴管理。
-
models/User.js 包含一個用戶模型,用于對用戶進行數(shù)據(jù)庫操作。
-
server.js 用于項目引導和請求處理。
就是這樣!這個項目非常簡單,因此您無需深入研究即可輕松理解主要概念。
{
"name": "angular-restful-auth",
"version": "0.0.1",
"dependencies": {
"body-parser": "^1.20.2",
"express": "4.x",
"express-jwt": "8.4.1",
"jsonwebtoken": "9.0.0",
"mongoose": "7.3.1",
"morgan": "latest"
},
"engines": {
"node": ">=0.10.0"
}
}
?
package.json 包含項目的依賴項: express
用于 MVC,body-parser
用于模擬 post Node.js 中的請求處理,morgan
用于請求日志記錄,mongoose
用于我們的 ORM 框架連接到 MongoDB,和 jsonwebtoken
用于使用我們的用戶模型創(chuàng)建 JWT 令牌。還有一個名為 engines
的屬性,表示該項目是使用 Node.js 版本 >= 0.10.0 制作的。這對于 Heroku 等 PaaS 服務很有用。我們還將在另一節(jié)中討論該主題。
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
email: String,
password: String,
token: String
});
module.exports = mongoose.model('User', UserSchema);
?
我們說過我們將使用用戶模型有效負載生成令牌。這個模型幫助我們對MongoDB進行用戶操作。在User.js中,定義了用戶模式并使用貓鼬模型創(chuàng)建了用戶模型。該模型已準備好進行數(shù)據(jù)庫操作。
我們的依賴關系已經(jīng)定義,我們的用戶模型也已經(jīng)定義,所以現(xiàn)在讓我們將所有這些組合起來構建一個用于處理特定請求的服務。
// Required Modules
const express = require("express");
const morgan = require("morgan");
const bodyParser = require("body-parser");
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
const app = express();
?
在 Node.js 中,您可以使用 require
在項目中包含模塊。首先,我們需要將必要的模塊導入到項目中:
const port = process.env.PORT || 3001;
const User = require('./models/User');
// Connect to DB
mongoose.connect(process.env.MONGO_URL);
?
我們的服務將通過特定端口提供服務。如果系統(tǒng)環(huán)境變量中定義了任何端口變量,則可以使用它,或者我們定義了端口 3001
。之后,包含了User模型,并建立了數(shù)據(jù)庫連接,以進行一些用戶操作。不要忘記為數(shù)據(jù)庫連接 URL 定義一個環(huán)境變量 MONGO_URL
Ini adalah kelebihan paling biasa bagi pengesahan dan komunikasi berasaskan token. Ini menyimpulkan perbincangan teori dan seni bina tentang pengesahan berasaskan token. Sudah tiba masanya untuk melihat contoh praktikal.
Contoh permohonan
Anda akan melihat dua aplikasi yang menunjukkan pengesahan berasaskan token: ??
- Halaman belakang pengesahan berasaskan token
- Ujung hadapan pengesahan berasaskan token
??Dalam projek bahagian belakang, akan ada pelaksanaan perkhidmatan, dan keputusan perkhidmatan akan dalam format JSON. Tiada pandangan dikembalikan daripada perkhidmatan. Dalam projek bahagian hadapan, akan ada projek Angular untuk HTML bahagian hadapan dan kemudian aplikasi bahagian hadapan akan diisi oleh perkhidmatan Angular untuk membuat permintaan kepada perkhidmatan bahagian belakang. ??
Halaman belakang pengesahan berasaskan token
??Dalam projek bahagian belakang, terdapat tiga fail utama: ??
-
package.json digunakan untuk pengurusan pergantungan.
-
models/User.js mengandungi model pengguna untuk operasi pangkalan data pada pengguna.
-
server.js digunakan untuk bootstrap projek dan pemprosesan permintaan.
??Itu sahaja! Projek ini sangat mudah supaya anda boleh memahami konsep utama dengan mudah tanpa perlu mendalaminya. ??
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(morgan("dev"));
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type, Authorization');
next();
});
,
??
package.json mengandungi kebergantungan projek:
express
untuk MVC,
body-parser
< strong> digunakan untuk mensimulasikan pemprosesan permintaan pos dalam Node.js,
morgan
digunakan untuk pengelogan permintaan,
mongoose
digunakan untuk menyambungkan rangka kerja ORM kami kepada MongoDB dan
jsonwebtoken
digunakan untuk mencipta token JWT menggunakan model pengguna kami. Terdapat juga atribut yang dipanggil
engines
yang menunjukkan bahawa projek itu dibuat menggunakan versi Node.js >= 0.10.0. Ini berguna untuk perkhidmatan PaaS seperti Heroku. Kami juga akan membincangkan topik ini dalam bahagian lain. ??
app.post('/authenticate', async function(req, res) {
try {
const user = await User.findOne({ email: req.body.email, password: req.body.password }).exec();
if (user) {
res.json({
type: true,
data: user,
token: user.token
});
} else {
res.json({
type: false,
data: "Incorrect email/password"
});
}
} catch (err) {
res.json({
type: false,
data: "Error occurred: " + err
});
}
});
,
??Kami berkata kami akan menggunakan muatan model pengguna untuk menjana token. Model ini membantu kami melaksanakan operasi pengguna pada MongoDB. Dalam
User.js, corak pengguna ditentukan dan model pengguna dibuat menggunakan model mongoose. Model sedia untuk operasi pangkalan data. ??
??Kebergantungan kami ditakrifkan, model pengguna kami ditakrifkan, jadi sekarang mari kita susun semuanya untuk membina perkhidmatan yang mengendalikan permintaan tertentu. ??
app.post('/signin', async function(req, res) {
try {
const existingUser = await User.findOne({ email: req.body.email }).exec();
if (existingUser) {
res.json({
type: false,
data: "User already exists!"
});
} else {
const userModel = new User();
userModel.email = req.body.email;
userModel.password = req.body.password;
const savedUser = await userModel.save();
savedUser.token = jwt.sign(savedUser.toObject(), process.env.JWT_SECRET);
const updatedUser = await savedUser.save();
res.json({
type: true,
data: updatedUser,
token: updatedUser.token
});
}
} catch (err) {
res.json({
type: false,
data: "Error occurred: " + err
});
}
});
,
??Dalam Node.js, anda boleh menggunakan
require
untuk memasukkan modul dalam projek anda. Pertama, kita perlu mengimport modul yang diperlukan ke dalam projek: ??
app.get('/me', ensureAuthorized, async function(req, res) {
try {
const user = await User.findOne({ token: req.token }).exec();
res.json({
type: true,
data: user
});
} catch (err) {
res.json({
type: false,
data: "Error occurred: " + err
});
}
});
,
??Perkhidmatan kami akan disediakan melalui port tertentu. Anda boleh menggunakannya jika mana-mana pembolehubah port ditakrifkan dalam pembolehubah persekitaran sistem atau kami telah menentukan port
3001
. Selepas itu, model Pengguna disertakan dan sambungan pangkalan data diwujudkan untuk beberapa operasi pengguna. Jangan lupa untuk menentukan pembolehubah persekitaran
MONGO_URL
untuk URL sambungan pangkalan data. ??
function ensureAuthorized(req, res, next) {
var bearerToken;
var bearerHeader = req.headers["authorization"];
if (typeof bearerHeader !== 'undefined') {
var bearer = bearerHeader.split(" ");
bearerToken = bearer[1];
req.token = bearerToken;
next();
} else {
res.send(403);
}
}
,
??Dalam bahagian di atas, kami membuat beberapa konfigurasi menggunakan Express untuk mensimulasikan pemprosesan permintaan HTTP dalam Node. Kami membenarkan permintaan daripada domain yang berbeza untuk membangunkan sistem bebas pelanggan. Jika anda tidak membenarkan ini, anda akan mencetuskan ralat CORS (Cross-Origin Request Sharing) dalam pelayar web anda. ??
-
Access-Control-Allow-Origin
允許所有域。
- 您可以向此服務發(fā)送
POST
和 GET
請求。
-
X-Requested-With
和 content-type
標頭是允許的。
app.post('/authenticate', async function(req, res) {
try {
const user = await User.findOne({ email: req.body.email, password: req.body.password }).exec();
if (user) {
res.json({
type: true,
data: user,
token: user.token
});
} else {
res.json({
type: false,
data: "Incorrect email/password"
});
}
} catch (err) {
res.json({
type: false,
data: "Error occurred: " + err
});
}
});
?
我們已經(jīng)導入了所有必需的模塊并定義了我們的配置,所以現(xiàn)在是時候定義請求處理程序了。在上面的代碼中,每當你使用用戶名和密碼向 /authenticate
發(fā)出 POST
請求時,你都會得到一個 JWT
令牌。首先,使用用戶名和密碼處理數(shù)據(jù)庫查詢。如果用戶存在,則用戶數(shù)據(jù)將與其令牌一起返回。但是如果沒有與用戶名和/或密碼匹配的用戶怎么辦?
app.post('/signin', async function(req, res) {
try {
const existingUser = await User.findOne({ email: req.body.email }).exec();
if (existingUser) {
res.json({
type: false,
data: "User already exists!"
});
} else {
const userModel = new User();
userModel.email = req.body.email;
userModel.password = req.body.password;
const savedUser = await userModel.save();
savedUser.token = jwt.sign(savedUser.toObject(), process.env.JWT_SECRET);
const updatedUser = await savedUser.save();
res.json({
type: true,
data: updatedUser,
token: updatedUser.token
});
}
} catch (err) {
res.json({
type: false,
data: "Error occurred: " + err
});
}
});
?
當您使用用戶名和密碼向 /signin
發(fā)出 POST
請求時,將使用發(fā)布的用戶信息創(chuàng)建一個新用戶。在 14th
行,您可以看到使用 jsonwebtoken
模塊生成了一個新的 JSON 令牌,該令牌已分配給 jwt
變量。認證部分沒問題。如果我們嘗試訪問受限端點怎么辦?我們如何設法訪問該端點?
app.get('/me', ensureAuthorized, async function(req, res) {
try {
const user = await User.findOne({ token: req.token }).exec();
res.json({
type: true,
data: user
});
} catch (err) {
res.json({
type: false,
data: "Error occurred: " + err
});
}
});
?
當您向 /me
發(fā)出 GET
請求時,您將獲得當前用戶信息,但為了繼續(xù)請求的端點,確保Authorized
函數(shù)將被執(zhí)行。
function ensureAuthorized(req, res, next) {
var bearerToken;
var bearerHeader = req.headers["authorization"];
if (typeof bearerHeader !== 'undefined') {
var bearer = bearerHeader.split(" ");
bearerToken = bearer[1];
req.token = bearerToken;
next();
} else {
res.send(403);
}
}
?
在該函數(shù)中,攔截請求頭,并提取authorization
頭。如果此標頭中存在承載令牌,則該令牌將分配給 req.token
以便在整個請求中使用,并且可以使用 next( )
。如果令牌不存在,您將收到 403(禁止)響應。讓我們回到處理程序 /me
,并使用 req.token
使用此令牌獲取用戶數(shù)據(jù)。每當您創(chuàng)建新用戶時,都會生成一個令牌并將其保存在數(shù)據(jù)庫的用戶模型中。這些令牌是獨一無二的。
對于這個簡單的項目,我們只有三個處理程序。之后,您將看到:
process.on('uncaughtException', function(err) {
console.log(err);
});
?
如果發(fā)生錯誤,Node.js 應用程序可能會崩潰。使用上面的代碼,可以防止崩潰,并在控制臺中打印錯誤日志。最后,我們可以使用以下代碼片段啟動服務器。
// Start Server
app.listen(port, function () {
console.log( "Express server listening on port " + port);
});
?
總結一下:
- 模塊已導入。
- 配置已完成。
- 已定義請求處理程序。
- 定義中間件是為了攔截受限端點。
- 服務器已啟動。
我們已經(jīng)完成了后端服務。為了讓多個客戶端可以使用它,您可以將這個簡單的服務器應用程序部署到您的服務器上,或者也可以部署在 Heroku 中。項目根文件夾中有一個名為 Procfile
的文件。讓我們在 Heroku 中部署我們的服務。
Heroku 部署
您可以從此 GitHub 存儲庫克隆后端項目。
我不會討論如何在 Heroku 中創(chuàng)建應用程序;如果您之前沒有創(chuàng)建過 Heroku 應用程序,可以參考這篇文章來創(chuàng)建 Heroku 應用程序。創(chuàng)建 Heroku 應用程序后,您可以使用以下命令將目標添加到當前項目:
git remote add heroku <your_heroku_git_url>
現(xiàn)在您已經(jīng)克隆了一個項目并添加了一個目標。在 git add
和 git commit
之后,您可以通過執(zhí)行 git push heroku master
將代碼推送到 Heroku。當您成功推送項目時,Heroku 將執(zhí)行 npm install
命令將依賴項下載到 Heroku 上的 temp
文件夾中。之后,它將啟動您的應用程序,您可以使用 HTTP 協(xié)議訪問您的服務。
基于令牌的-auth-frontend
在前端項目中,您將看到一個 Angular 項目。在這里,我只提及前端項目中的主要部分,因為 Angular 不是一個教程可以涵蓋的內容。
您可以從此 GitHub 存儲庫克隆該項目。在此項目中,您將看到以下文件夾結構:

我們擁有三個組件——注冊、配置文件和登錄——以及一個身份驗證服務。
您的app.component.html 如下所示:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">Home</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" routerLink="/profile">Me</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/login">Signin</a></li>
<li class="nav-item"><a class="nav-link" routerLink="/signup">Signup</a></li>
<li class="nav-item"><a class="nav-link" (click)="logout()">Logout</a></li>
</ul>
</div>
</div>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>
</body>
</html>
?
在主組件文件中,<router-outlet></router-outlet>
?定義各個組件的路由。
在 auth.service.ts 文件中,我們定義 AuthService
類,該類通過 API 調用來處理身份驗證,以登錄、驗證 Node.js 應用程序的 API 端點。
import { Injectable } from '@angular/core';
import { HttpClient,HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private apiUrl = 'your_node_app_url';
public token: string ='';
constructor(private http: HttpClient) {
}
signin(username: string, password: string): Observable<any> {
const data = { username, password };
return this.http.post(`${this.apiUrl}/signin`, data);
}
authenticate(email: string, password: string): Observable<any> {
const data = { email, password };
console.log(data)
return this.http.post(`${this.apiUrl}/authenticate`, data)
.pipe(
tap((response:any) => {
this.token = response.data.token; // Store the received token
localStorage.setItem('token',this.token)
console.log(this.token)
})
);
}
profile(): Observable<any> {
const headers = this.createHeaders();
return this.http.get(`${this.apiUrl}/me`,{ headers });
}
private createHeaders(): HttpHeaders {
let headers = new HttpHeaders({
'Content-Type': 'application/json',
});
if (this.token) {
headers = headers.append('Authorization', `Bearer ${this.token}`);
}
return headers;
}
logout(): void {
localStorage.removeItem('token');
}
}
在 authenticate()
方法中,我們向 API 發(fā)送 POST 請求并對用戶進行身份驗證。從響應中,我們提取令牌并將其存儲在服務的 this.token
屬性和瀏覽器的 localStorage
中,然后將響應作為 Observable
返回。
在 profile()
方法中,我們通過在 Authorization 標頭中包含令牌來發(fā)出 GET 請求以獲取用戶詳細信息。
createHeaders()
方法在發(fā)出經(jīng)過身份驗證的 API 請求時創(chuàng)建包含身份驗證令牌的 HTTP 標頭。當用戶擁有有效令牌時,它會添加一個授權標頭。該令牌允許后端 API 對用戶進行身份驗證。
如果身份驗證成功,用戶令牌將存儲在本地存儲中以供后續(xù)請求使用。該令牌也可供所有組件使用。如果身份驗證失敗,我們會顯示一條錯誤消息。
不要忘記將服務 URL 放入上面代碼中的 baseUrl
中。當您將服務部署到 Heroku 時,您將獲得類似 appname.herokuapp.com
的服務 URL。在上面的代碼中,您將設置 var baseUrl = "appname.herokuapp.com"
。
注銷功能從本地存儲中刪除令牌。
在 signup.component.ts
文件中,我們實現(xiàn)了 signup ()
方法,該方法獲取用戶提交的電子郵件和密碼并創(chuàng)建一個新用戶。
import { Component } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-signup',
templateUrl: './signup.component.html',
styleUrls: ['./signup.component.css']
})
export class SignupComponent {
password: string = '';
email: string = '';
constructor(private authService:AuthService){}
signup(): void {
this.authService.signin(this.email, this.password).subscribe(
(response) => {
// success response
console.log('Authentication successful', response);
},
(error) => {
// error response
console.error('Authentication error', error);
}
);
}
}
?
login.component.ts 文件看起來與注冊組件類似。
?
import { Component } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
email: string = '';
password: string = '';
constructor(private authService: AuthService) {}
login(): void {
this.authService.authenticate(this.email, this.password).subscribe(
(response) => {
// success response
console.log('Signin successful', response);
},
(error) => {
// error response
console.error('Signin error', error);
}
);
}
}
配置文件組件使用用戶令牌來獲取用戶的詳細信息。每當您向后端的服務發(fā)出請求時,都需要將此令牌放入標頭中。 profile.component.ts 如下所示:
import { Component } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent {
myDetails: any;
constructor(private authService: AuthService) { }
ngOnInit(): void {
this.getProfileData();
}
getProfileData(): void {
this.authService.me().subscribe(
(response: any) => {
this.myDetails = response;
console.log('User Data:', this.myDetails);
},
(error: any) => {
console.error('Error retrieving profile data');
}
);
}
?
在上面的代碼中,每個請求都會被攔截,并在標頭中放入授權標頭和值。然后,我們將用戶詳細信息傳遞到 profile.component.html 模板。
<h2>User profile </h2>
<div class="row">
<div class="col-lg-12">
<p>{{myDetails.data.id}}</p>
<p>{{myDetails.data.email}}</p>
</div>
</div>
最后,我們在 app.routing.module.ts 中定義路由。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { ProfileComponent } from './profile/profile.component';
import { SignupComponent } from './signup/signup.component';
const routes: Routes = [
{path:'signup' , component:SignupComponent},
{path:'login' , component:LoginComponent},
{ path: 'profile', component: ProfileComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
?
從上面的代碼中您可以很容易地理解,當您轉到/時,將呈現(xiàn)app.component.html頁面。另一個例子:如果您轉到/signup,則會呈現(xiàn)signup.component.html。這個渲染操作將在瀏覽器中完成,而不是在服務器端。
結論
基于令牌的身份驗證系統(tǒng)可幫助您在開發(fā)獨立于客戶端的服務時構建身份驗證/授權系統(tǒng)。通過使用這項技術,您將只需專注于您的服務(或 API)。
身份驗證/授權部分將由基于令牌的身份驗證系統(tǒng)作為服務前面的一層進行處理。您可以從任何客戶端(例如網(wǎng)絡瀏覽器、Android、iOS 或桌面客戶端)訪問和使用服務。
Atas ialah kandungan terperinci Pengesahan berasaskan token dengan Angular dan Node. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!