版權宣告
一、前置知識
二、環境
-
Microsoft Visual Studio 2017
整合開發環境 -
Node.js v8.9.4
Javascript執行環境
三、開始動手,一步步來完善
1、建立基礎的靜態資源伺服器、基礎架構
以下是基本的程式碼,實現靜態伺服器,以及一個當token驗證異常時候的處理。
下面我們將在這個基本程式碼下逐步增加註冊、登入、資訊的功能。
const path = require(`path`); // 用於處理目錄路徑
const Koa = require(`koa`); // web開發框架
const serve = require(`koa-static`); // 靜態資源處理
const route = require(`koa-route`); // 路由中介軟體
const jwt = require(`jsonwebtoken`); // 用於簽發、解析`token`
const jwtKoa = require(`koa-jwt`); // 用於路由許可權控制
const koaBody = require(`koa-body`); // 用於查詢字串解析到`ctx.request.query`
const app = new Koa();
const website = {
scheme: `http`,
host: `localhost`,
port: 1337,
join: function () {
return `${this.scheme}://${this.host}:${this.port}`
}
}
/* jwt金鑰 */
const secret = `secret`;
/* 當token驗證異常時候的處理,如token過期、token錯誤 */
app.use((ctx, next) => {
return next().catch((err) => {
if (err.status === 401) {
ctx.status = 401;
ctx.body = {
ok: false,
msg: err.originalError ? err.originalError.message : err.message
}
} else {
throw err;
}
});
});
/* 查詢字串解析到`ctx.request.query` */
app.use(koaBody());
/* 路由許可權控制 */
// 待辦事項……
/* POST /api/register 註冊 */
// 待辦事項……
/* GET /api/login 登入 */
// 待辦事項……
/* GET /api/info 資訊 */
// 待辦事項……
/* 靜態資源處理 */
app.use(serve(path.join(__dirname, `static`)));
/* 監聽伺服器埠 */
app.listen(website.port, () => {
console.log(`${website.join()} 伺服器已經啟動!`);
});
下面,我們將在註冊、登入、資訊的註釋底下新增實現的程式碼。
2、路由許可權控制
註冊、登入介面、其它資源不需要認證,資訊介面需要認證。
/* 路由許可權控制 */
app.use(jwtKoa({ secret: secret }).unless({
// 設定login、register介面,可以不需要認證訪問
path: [
/^/api/login/,
/^/api/register/,
/^((?!/api).)*$/ // 設定除了私有介面外的其它資源,可以不需要認證訪問
]
}));
3、註冊
/* POST /api/register 註冊 */
app.use(route.post(`/api/register`, async (ctx, next) => {
const body = ctx.request.body;
/*
* body = {
* user : `御焱`,
* password : `123456`
* }
*/
// 判斷 body.user 和 body.password 格式是否正確
// 待辦事項……
// 判斷使用者是否已經註冊
// 待辦事項……
// 儲存到新使用者到資料庫中
// 待辦事項……
// 是否註冊成功
let 是否註冊成功 = true;
if (是否註冊成功) {
// 返回一個註冊成功的JOSN資料給前端
return ctx.body = {
ok: true,
msg: `註冊成功`,
token: getToken({ user: body.user, password: body.password })
}
} else {
// 返回一個註冊失敗的JOSN資料給前端
return ctx.body = {
ok: false,
msg: `註冊失敗`
}
}
}));
/* 獲取一個期限為4小時的token */
function getToken(payload = {}) {
return jwt.sign(payload, secret, { expiresIn: `4h` });
}
3、登入
/* GET /api/login 登入 */
app.use(route.get(`/api/login`, async (ctx, next) => {
const query = ctx.request.query;
/*
* query = {
* user : `御焱`,
* password : `123456`
* }
*/
// 判斷 query.user 和 query.password 格式是否正確
// 待辦事項……
// 判斷是否已經註冊
// 待辦事項……
// 判斷姓名、學號是否正確
// 待辦事項……
return ctx.body = {
ok: true,
msg: `登入成功`,
token: getToken({ user: query.user, password: query.password })
}
}));
前端獲取到token之後,可以儲存在任意本地儲存裡。
4、資訊
/* GET /api/info 資訊 */
app.use(route.get(`/api/info`, async (ctx, next) => {
// 前端訪問時會附帶token在請求頭
payload = getJWTPayload(ctx.headers.authorization)
/*
* payload = {
* user : "御焱",
* iat : 1524042454,
* exp : 1524056854
* }
*/
// 根據 payload.user 查詢該使用者在資料庫中的資訊
// 待辦事項……
const info = {
name: `御焱`,
age: 10,
sex: `男`
}
let 獲取資訊成功 = true;
if (獲取資訊成功) {
return ctx.body = {
ok: true,
msg: `獲取資訊成功`,
data: info
}
} else {
return ctx.body = {
ok: false,
msg: `獲取資訊失敗`
}
}
}));
/* 通過token獲取JWT的payload部分 */
function getJWTPayload(token) {
// 驗證並解析JWT
return jwt.verify(token.split(` `)[1], secret);
}
訪問需要認證的介面時,需要在request
頭附帶Authorization:Bearer [token]
欄位。