版權宣告
一、前置知識
二、環境
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]
欄位。