3種經常使用鑑權方法原理與實現

cookie

誕生

HTTP協議是無狀態的協議。一旦數據交換完畢,客戶端與服務器端的鏈接就會關閉,再次交換數據須要創建新的鏈接。html

爲解決這個問題,有了cookie出現前端

cookie介紹

Cookie是由服務器端生成,發送給User-Agent(通常是瀏覽器),(通知瀏覽器設置一下cookie),瀏覽器自動會將Cookie以進行保存(以key/value的形式)。當下次請求同一網站時也會自動發送該Cookie給服務器,即添加在請求頭部。vue

存放在瀏覽器端ios

原理即代碼實現

const http = require("http")
http.createServer((req, res) => {
    // 觀察cookie是否存在  若是存在就打印cookie
    console.log('cookie:', req.headers.cookie)
    // 設置cookie
    res.setHeader('Set-Cookie''cookie=abc;')
    res.end('hello cookie!!')
}).listen(3000)

結果展現

爲何會有兩次?git

第一次 請求的時候 發現沒有cookie,就進行了設置github

第二次請求,發現有cookie了,就直接輸出了web

session

  1. 基於 cookie 實現,存放在服務端
  2. 裏面只存一個 cookieId,其餘的保存在服務器中

代碼即原理展現

// principle
const http = require("http")
const session = {} // 用來存放cookie
http.createServer((req, res) => {
    // 觀察cookie存在
    console.log('cookie:', req.headers.cookie)
    const sessionKey = 'sid'
    const cookie = req.headers.cookie
    if (cookie && cookie.indexOf(sessionKey) > -1) { // 證實cookie 存在
        res.end('Come Back ')
        // 簡略寫法未必具備通用性
        const pattern = new RegExp(`${sessionKey}=([^;]+);?\s*`)  // 正則表達式對象 判斷是否又sid = XX等
        console.log(pattern)
        const sid = pattern.exec(cookie)[1// 檢索字符串中指定的值。返回找到的值
        console.log('session:', sid, session, session[sid])
    } else {
        const sid = (Math.random() * 99999999).toFixed()
        // 設置cookie
        res.setHeader('Set-Cookie'`${sessionKey}=${sid};`)
        session[sid] = {name'oldWang'}
        res.end('Hello')
    }
    res.end('hello cookie!!')
}).listen(3000)

分析

瀏覽器不存在cookie 的時候,走else,隨機設置一個sid,(通常是本身設置,儘可能麻煩一點),而後 設置cookie, 在session集合中保存用戶信息,(通常存到數據庫裏面), 最後返回結果正則表達式

第二次進入的時候,會走if ,進行取值判斷。數據庫

Token

使用緣由

  1. Session不足,服務器存在狀態
  2. 不靈活,( cookie只存在於瀏覽器)

過程

  1. 客戶端使用戶名跟密碼請求登陸
  2. 服務端收到請求,去驗證用戶名與密碼
  3. 驗證成功後,服務端會簽發一個令牌(Token),再把這個 Token 發送給客戶端
  4. 客戶端收到 Token 之後能夠把它存儲起來,⽐好比放在 Cookie 里或者 Local Storage ⾥
  5. 客戶端每次向服務端請求資源的時候須要帶着服務端簽發的 Token
  6. 服務端收到請求,而後去驗證客戶端請求裏面帶着的 Token,若是驗證成功,就向客戶端返回 請求的數據

例子實現

前臺

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <div><input v-model="username"/> <input v-model="password"/></div>
    <div>
        <button @click="login">登錄</button>
        <button @click="logout">退出</button>
        <button @click="getUser" >獲取用戶</button>
    </div>
    <div>
        <button @click="logs=[]">清除頁面</button>
    </div>  
    <ul>
        <li v-for="(log,idx) in logs" :key="idx"> {{ log }}</li>
    </ul>
</div>
<script>
    axios.defaults.baseURL = 'http://localhost:3000'
    axios.defaults.withCredentials = true
    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"admin",
            password"admin",
            logs: []
        },
        methods: {
            loginasync function ({
                const res = await axios.post("/users/login-token", {
                    usernamethis.username,
                    passwordthis.password
                });
                localStorage.setItem("token", res.data.token);
            }, logoutasync function ({
                this.logs.push('退出登錄')
                localStorage.removeItem("token");
            }, getUserasync function ({
                await axios.get("/users/getUser-token");
            }
        }
    });
</script>
</body>
</html>

後臺代碼

const Koa = require('koa')
const router = require('koa-router')() // koa路由
const jwt = require("jsonwebtoken")
const jwtAuth = require("koa-jwt")
const secret = "it's a secret"
const cors = require('koa2-cors')
// https://www.jb51.net/article/135924.htm 跨域參考
const bodyParser = require('koa-bodyparser'// 不支持table,支持json
const static = require('koa-static')
const app = new Koa();

app.keys = ['some secret'];
app.use(cors({credentialstrue})) // 解決跨域
app.use(static(__dirname + '/'));
app.use(bodyParser())
router.post("/users/login-token"async ctx => {
    const {body} = ctx.request;
    console.log(body)
    //登陸邏輯,略略
    // 設置session
    const userinfo = body.username;
    ctx.body = {
        message"登陸成功"user: userinfo,
        // ⽣生成 token 返回給客戶端
        token: jwt.sign({
            data: userinfo,
            // 設置 token 過時時間,一⼩小時後,秒爲單位
            exp: Math.floor(Date.now() / 1000) + 60 * 60
        }, secret)
    };
})

router.get("/users/getUser-token", jwtAuth({secret}), async ctx => {
    // 驗證經過,state.user
    console.log(ctx.state.user);
    //獲取session
    ctx.body = {message"獲取數據成功"userinfo: ctx.state.user.data};
});

app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);

jsonwebtoken githubnpm


本文分享自微信公衆號 - 阿琛前端成長之路(lwkWyc)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索