1html
1 登陸 -> 服務端生成sid(內存或者redis中) -> 主動設置sid或者返回給客戶端 -> 客戶端帶着sid發送請求 -> sid匹配認證是否經過vue
爲何這裏會有兩個?ios
app.keys = ['some secret', 'another secret'];
const SESS_CONFIG = { key: 'kkb:sess', maxAge: 86400000, httpOnly: true, signed: true, }; app.use(session(SESS_CONFIG, app)); app.use(ctx => { if (ctx.path === '/favicon.ico') return; let n = ctx.session.count || 0; ctx.session.count = ++n; ctx.body = '第' + n + '次訪問'; });
使用redis存儲sessiongit
const redisStore = require('koa-redis'); const redis = require('redis') const client = redis.createClient(6379, "localhost"); app.use(session({ key:'kkb:sess', store: redisStore({client}) }, app)); app.use(ctx => { //... client.keys('*',(err,keys)=>{ console.log(keys); keys.forEach(key=>{ client.get(key,(err,val)=>{ console.log(val); }) }) }) });
案例:經過session實現用戶鑑權github
<!DOCTYPE html> <html> <head> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <div id="app"> <div> <input v-model="username"/> <input v-model="password"/> </div> <div> <button @click="login">Login</button> <button @click="logout">Logout</button> <button @click="getUser">GetUser</button> </div> <div> <button @click="logs=[]">Clear Log</button> </div> <!-- 日誌 --> <ul> <li v-for="(log,idx) in logs" :key="idx"> {{ log }} </li> </ul> </div> <script> axios.defaults.withCredentials = true; axios.interceptors.response.use(response => { app.logs.push(JSON.stringify(response.data)); return response; }); var app = new Vue({ el: "#app", data: { username: "test", password: "test", logs: [] }, methods: { login: async function () { await axios.post("/users/login", { username: this.username, password: this.password }); }, logout: async function () { await axios.post("/users/logout"); }, getUser: async function () { await axios.get("/users/getUser"); } } }); </script> </body> </html>
router.post("/login", async ctx => { const { body } = ctx.request; console.log("body", body); ctx.session.userinfo = body.username; ctx.body = { ok: 1, message: "登陸成功" }; }); router.post("/logout", async ctx => { delete ctx.session.userinfo; ctx.body = { ok: 1, message: "登出系統" }; }); router.get("/getUser", async ctx => { ctx.body = { ok: 1, message: "獲取數據成功", userinfo: ctx.session.userinfo }; });
module.exports = async (ctx, next) => { if (!ctx.session.userinfo) { ctx.body = { ok: 0, message: "用戶未登陸" }; } else { await next(); } };
router.get("/getUser", require("./middleware/auth"), async ctx => {...})
案例:令牌認證web
<html> <head> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <div id="app"> <div> <input v-model="username" /> <input v-model="password" /> </div> <div> <button v-on:click="login">Login</button> <button v-on:click="logout">Logout</button> <button v-on:click="getUser">GetUser</button> </div> <div> <button @click="logs=[]">Clear Log</button> </div> <!-- 日誌 --> <ul> <li v-for="(log,idx) in logs" :key="idx"> {{ log }} </li> </ul> </div> <script> axios.interceptors.request.use( config => { const token = window.localStorage.getItem("token"); if (token) { // 判斷是否存在token,若是存在的話,則每一個http header都加上token // Bearer是JWT的認證頭部信息 config.headers.common["Authorization"] = "Bearer " + token; } return config; }, err => { return Promise.reject(err); } ); axios.interceptors.response.use( response => { app.logs.push(JSON.stringify(response.data)); return response; }, err => { app.logs.push(JSON.stringify(response.data)); return Promise.reject(err); } ); var app = new Vue({ el: "#app", data: { username: "test", password: "test", logs: [] }, methods: { login: async function() { const res = await axios.post("/users/login-token", { username: this.username, password: this.password }); localStorage.setItem("token", res.data.token); }, logout: async function() { localStorage.removeItem("token"); }, getUser: async function() { await axios.get("/users/getUser-token"); } } }); </script> </body> </html>
登陸接口redis
const jwt = require("jsonwebtoken"); const jwtAuth = require("koa-jwt"); const secret = "it's a secret"; router.post("/login-token", async ctx => { const { body } = ctx.request; const userinfo = body.username; ctx.body = { message: "登陸成功", user: userinfo, token: jwt.sign( { data: userinfo, exp: Math.floor(Date.now() / 1000) + 60 * 60 //一小時後過時 }, secret ) }; }); router.get( "/getUser-token", jwtAuth({ secret }), async ctx => { //獲取session ctx.body = { message: "獲取數據成功", userinfo: ctx.state.user.data }; } );
<html> <head> <title>OAuth</title> </head> <body> <div id="app"> <a href='/users/login-github'>Github登陸</a> </div> </body> </html>
const config = { client_id: "a141111525bac2f1edf2", client_secret: "8e37306c1451e60412754ace80edee4ca937564a" }; const axios = require('axios') const querystring = require('querystring') router.get("/login-github", async ctx => { //重定向到認證接口,並配置參數 const path = `https://github.com/login/oauth/authorize? client_id=${config.client_id}`; //轉發到受權服務器 ctx.redirect(path); }); router.get("/oauth/github/callback", async ctx => { const code = ctx.query.code; const params = { client_id: config.client_id, client_secret: config.client_secret, code: code }; let res = await axios.post( "https://github.com/login/oauth/access_token", params ); console.log(res.data); const access_token = querystring.parse(res.data).access_token; res = await axios.get( "https://api.github.com/user?access_token=" + access_token ); console.log("userAccess:", res.data); ctx.redirect("/hello.html"); });