# 鉴权
鉴权(authentication)是指验证用户是否拥有访问系统的权利。
# cookie-session
由于 http
是无状态的,它不对之前发生过的请求和响应的状态进行管理。也就是说,无法根据之前的状态进行本次的请求处理。
于是引入了 Cookie
技术。Cookie
技术通过在请求和响应报文中写入 Cookie
信息来控制客户端的状态。
# 原生实现session
const http = require('http')
const session = {};
http.createServer((req, res) => {
console.log('cookie:', req.headers.cookie)
const sessionKey = 's_sid'
const cookie = req.headers.cookie
// 根据cookie判断是否登录
if(cookie && ~cookie.indexOf(sessionKey)) {
const pattern = new RegExp(`${sessionKey}=([^;]+);?\s*`)
const sid = pattern.exec(cookie)[1]
console.log('session:',sid ,session ,session[sid])
res.end('hello world');
} else {
const sid = (Math.random() * 99999999).toFixed()
console.log(sid);
// 设置cookie
res.setHeader('Set-Cookie', `${sessionKey}=${sid};`)
session[sid] = {name : 'laotie'}
res.end('login or register, please');
}
}).listen(3000, (res) => {
console.log('app listen to *: 3000')
});
# koa-session
npm i koa-session -S
// index.js
const koa = require('koa')
const app = new koa()
const session = require('koa-session')
// 签名key keys作⽤ ⽤来对cookie进⾏签名
app.keys = ['some secret'];
// 配置项
const SESS_CONFIG = {
key: 'key: session', // cookie键名
maxAge: 86400000, // 有效期,默认⼀天
httpOnly: true, // 仅服务器修改
signed: true, // 签名cookie
};
// 注册
app.use(session(SESS_CONFIG, app));
// 测试
app.use(ctx => {
// 获取
let n = ctx.session.count || 0;
// 设置
ctx.session.count = ++n;
ctx.body = '第' + n + '次访问';
});
app.listen(3000)
# Token
# cookie的不足:
- 服务器有状态(存储了session)
- 不适用于
app
端
Token可以解决这个问题。
# Token鉴权流程:
1. 客户端使⽤⽤户名跟密码请求登录
2. 服务端收到请求,去验证⽤户名与密码
3. 验证成功后,服务端会签发⼀个令牌(Token),再把这个 Token 发送给客户端
4. 客户端收到 Token 以后可以把它存储起来,⽐如放在 Cookie ⾥或者 Local Storage
⾥
5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
6. 服务端收到请求,然后去验证客户端请求⾥⾯带着的 Token,如果验证成功,就向客户端返回
请求的数据
# koa实现token
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<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.interceptors.request.use(
config => {
config.baseURL = 'http://localhost:3000/';
const token = localStorage.getItem('token');
if(token) {
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);
}
);
let app = new Vue({
el: '#app',
data: {
username: 'test',
password: 'test',
logs: []
},
methods: {
async login() {
const res = await axios.post('/users/login-token', {
username: this.username,
password: this.password
})
localStorage.setItem("token", res.data.token);
},
async logout() {
localStorage.removeItem("token");
},
async getUser() {
await axios.get("/users/getUser-token");
}
}
})
</script>
</body>
</html>
index.js
const Koa = require('koa')
const router = require('koa-router')()
const jwt = require("jsonwebtoken")
const jwtAuth = require("koa-jwt")
const secret = "it's a secret"
const bodyParser = require('koa-bodyparser')
const static = require('koa-static')
const app = new Koa();
app.keys = ['some secret'];
app.use(static(__dirname + '/'));
app.use(bodyParser())
router.post("/users/login-token", async ctx => {
const { body } = ctx.request;
//登录逻辑,略
//设置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);
← HTTP状态码 webpack常用配置解析 →