koa实战 - 鉴权

session-cookie方式

const http = require('http')
const app = http.createServer((req, res) => {
  console.log(`cookie: ${req.headers.cookie}`)
  res.setHeader('Set-Cookie', 'cookie=admin');
  res.end('set cookie') 
})
app.listen(3000, () => {
  console.log(`start server at localhost:3000`)
})

Set-Cookie负责设置cookie;
请求传递cookie

session原理

const http = require('http')
const session = {}

http.createServer((req,res)=>{
  const sessionKey = 'sid'
  const cookie = req.headers.cookie;
  console.log(cookie)
  if(cookie && cookie.indexOf(sessionKey)>-1){
    res.end('welcome back')
    const pattern = new RegExp(`${sessionKey}=([^;]+);?\s*`) 
    const sid = pattern.exec(cookie)[1]
    console.log(`session: ${sid}`,session,session[sid])
  }else{
    const sid = (Math.random()*99999999).toFixed()
    res.setHeader('Set-Cookie',`${sessionKey}=${sid}`)
    session[sid] = {name:'lau'}
    res.end('welcome')
  }
}).listen(3000,()=>{
  console.log(`start server at localhost:3000`)
})

实现原理:

  1. 服务器在接受客户端首次访问时在服务器端创建seesion,然后保存seesion(我们可以将
    seesion保存在内存中,也可以保存在redis中,推荐使用后者),然后给这个session生成一
    个唯一的标识字符串,然后在响应头中种下这个唯一标识字符串。
  2. 签名。这一步通过秘钥对sid进行签名处理,避免客户端修改sid。
  3. 浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次
    http请求的请求头中会带上该域名下的cookie信息,
  4. 服务器在接受客户端请求时会去解析请求头cookie中的sid,然后根据这个sid去找服务器端
    保存的该客户端的session,然后判断该请求是否合法。

在koa中使用session
安装koa-session

npm i -S koa-session
const Koa = require('koa')
const app = new Koa();
const session = require('koa-session')

app.keys = ['secret']; // 签名key keys作用 用来对cookie进行签名

const SESS_CONFIG = { // 配置
  key: 'koa:Session', //cookie key (default is koa:sess)
  maxAge: 1000, // cookie的过期时间 maxAge in ms (default is 1 days)
  httpOnly: true, //cookie是否只有服务器端可以访问 httpOnly or not (default true)
  signed: true //签名默认true
}

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, () => {
  console.log(`start server at localhost:3000`)
})

使用redis存储session
要使用redis,先在官网下载后进行连接。
开启redis
在这里插入图片描述
node连接redis

const redis = require('redis')
const client = redis.createClient(6379,'127.0.0.1')
client.on('error',(err)=>{
  console.log(`Error occur ${err}`)
})
client.set('name','leo',redis.print) // print 打印设置数据的结果
client.get('name',(err,val)=>{
  if(err) throw err;
  console.log(`Name : ${val}`) 
})

在koa中使用redis
安装koa-redis

npm i -S koa-redis
const Koa = require('koa')
const app = new Koa();
const session = require('koa-session');
const koaRedis = require('koa-redis')
const redis = require('redis')
const redisClient = redis.createClient(6379,'127.0.0.1')
const wrapper = require('co-redis')
const client = wrapper(redisClient);

app.use(session({
  key:'koa:Session',
  store: koaRedis({client}) // 此处可以不必指定client
},app));
app.use(async (ctx,next)=>{
  const keys = await client.keys('*')
  keys.forEach(async key=>{
    console.log(await client.get(key))
  })
  await next()
})
app.listen(3000, () => {
  console.log(`start server at localhost:3000`)
})

token验证

原理

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个令牌(Token),再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据。
const Koa = require('koa')
const router = require('koa-router')()
const jwt = require('jsonwebtoken');
const jwtAuth = require('koa-jwt');
const secret = 'secret key';
const app = new Koa();
router.get('/login', async (ctx) => {
  console.log('access')
  ctx.body = {
    message: 'login success',
    user: 'leo',
    token: jwt.sign({
      data: 'leo',
      exp: Math.floor(Date.now() / 1000) + 5 // 5秒后过期
    }, secret)
  }
})
router.get('/token', jwtAuth({ secret }), async ctx => {
  console.log(ctx.state.user);
  ctx.body = {
    message: 'get message',
    user: ctx.state.user.data
  }
})
app.use(async (ctx, next) => {
  ctx.set('Access-Control-Allow-Origin', '*');
  ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
  ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
  if (ctx.method == 'OPTIONS') {
    ctx.body = 200;
  } else {
    await next();
  }
})
app.use(router.routes())
app.listen(3000, () => {
  console.log(`start server at localhost:3000`)
})

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>token</title>
</head>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js"></script>
<body>
  <div id="login">login</div>
  <div id="getToken">getToken</div>
</body>
<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); } );
async function getResult(){
  const res = await axios.get('http://localhost:3000/token');
  console.log(res)
}
async function Login(){ 
  const res = await axios.get('http://localhost:3000/login');
  localStorage.setItem('token',res.data.token)
}
var login = document.querySelector('#login')
login.addEventListener('click',(e)=>{
  Login()
})
var to = document.querySelector('#getToken')
to.addEventListener('click',(e)=>{
  getResult()
})
</script>
</html>

如果token过期,则会返回401错误
在这里插入图片描述
jwt验证过程:

  • 用户登录的时候,服务端生成一个token返回给客户端
  • 客户端后续的请求都带上这个token
  • 服务端解析token获取用户信息,并响应用户的请求
  • token会有过期时间,客户端登出的时候也会废弃token,但是服务端不需要任何操作

JWT(JSON WEB TOKEN)原理

Bearer Token包含三个组成部分:令牌头、payload、哈希

  1. 签名:默认使用base64对payload编码,使用hs256算法对令牌头、payload和密钥进行签名生成哈希
  2. 验证:默认使用hs256算法对hs256算法对令牌中数据签名并将结果和令牌中哈希比对
const jwt = require('jsonwebtoken');
const secret = 'secret key'
const userinfo={
  name:'leo',
  password:'123'
}
const token = jwt.sign({
  data:userinfo,
  exp:Math.floor(Date.now()/1000) + 2 // 2s后过期
},secret)
console.log(token)
const options={
  key:'userinfo'
}
console.log(`解码:`,jwt.verify(token,secret,options))

HMAC SHA256
HMAC(Hash Message Authentication Code,散列消息鉴别码,基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。

BASE64
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节
描述为一种不易被人直接识别的形式。常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64编码的

Beare
Beare作为一种认证类型(基于OAuth 2.0),使用"Bearer"关键词进行定义

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章