3 koa2 实战鉴权

课程目标

  • 掌握三种常见鉴权方式:Session/Cookie、Token、OAuth

1

session-cookie方式

  • 原理

1 登录 -> 服务端生成sid(内存或者redis中) -> 主动设置sid或者返回给客户端 -> 客户端带着sid发送请求 -> sid匹配认证是否通过

  • koa中的session使用: npm i koa-session -S <font color=#00ffff size=72>color=#00ffff</font>

为什么这里会有两个?

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存储session

    • 安装: npm i -S koa-redis
    • 配置使用:
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实现用户鉴权

    • 登录页面,./public/login.html
<!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>
  • 登录页面,./public/login.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
 };
});
  • 路由守卫中间件,./middleware/auth.js
module.exports = async (ctx, next) => {
 if (!ctx.session.userinfo) {
  ctx.body = {
   ok: 0,
   message: "用户未登录"
 };
} else {
  await next();
}
};
  • 应用守卫,./routes/users.js 这里第二个参数是代表什么?
router.get("/getUser", require("./middleware/auth"), async ctx => {...})

Token 验证

  • 原理

image.png

  • 案例:令牌认证

    • 登录页,public/login-token.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 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>
  • 登录接口

    • 安装依赖: npm i jsonwebtoken koa-jwt -S
    • 接口编写,routes/users.js
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
 };
}
);

OAuth 2.0(开放授权)

  • 概述:授权不会有第三方信息和账号
  • 案例:OAuth登录
<html>
<head>
 <title>OAuth</title>
</head>
<body>
 <div id="app">
   <a href='/users/login-github'>Github登录</a>
 </div>
</body>
</html>
  • 登录接口,routes/users.js
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");
}); 
阅读 307

推荐阅读
Web前端路上
用户专栏

Web前端路上,专注Vue,html5,css3等

9 人关注
18 篇文章
专栏主页