鉴权

http是一种无状态的协议,这就意味着每一次的数据交互,服务是没办法确定请求人身份,所以需要用到用户鉴权。

常见的鉴权方式

session/cookie

  1. 服务器在接受客户端首次访问时在服务器端创建session,然后保存session(我们可以将session保存在内存中,也可以保存在redis中,推荐使用后者),然后给这个session生成一个唯一的标识字符串,然后再响应头中种下这个唯一标识字符串。

  2. 签名,这一步通过密钥对sid进行签名处理,避免客户端修改sid(非必须步骤)

  3. 浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次http请求的请求头中会带上该域名下的cookie信息。

  4. 服务器在接受客户端请求时回去解析请求头cookie中的sid,然后根据这个sid去找服务器端保存的该客户端的session,然后判断该请求是否合法。

cookie:存在浏览器里,容量有限4k,不安全(防篡改,加密)

session: 存在服务器,容量无限,安全的。

session不能单独存在,就是基于cookie的。

浏览器第一次访问服务器,服务器会生成一个sess_id, 会把这个id作为cookie还给浏览器。浏览器第二次访问的时候,服务器查询到sess_id,可以识别到这个用户。

// npm install koa-session -s;

app.keys = ['some secret', 'another secret'];
const SESS_CONFIG = {
    key: 'yd:sess',
    maxAge: 86400000,
    httpOnly: true,
    signed: true,
}
app.use(session(SESS_CONFIG, app));
app.use(ctx => {
    if (ctx.path === '/facicon.ico') {
        retrun;
    }
    let n = ctx.session.count || 0;
    ctx.session.count = ++n;
    ctx.body = `第${n}次`;
})
// npm install koa-redis -s;
const redisStore = require('koa-redis');
const redis = require('redis');
const client = redis.createClient(6379, '127.0.0.1');
app.use(session({
    key: 'yd:sess',
    store: redisStore({ client })
}, app));
app.use(ctx => {
    client.keys('*', (err, keys) => {
        console.log(keys);
        client.get(key, (err, val) => {
            console.log(val);
        })
    })
})

前端登录代码

axios.defaults.withCredentials = true; // 跨域写到cookie
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');
        }
    }
})

登录注销接口

router.post('/login', async ctx => {
    const body = ctx.request;
    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 atx => {
    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(''), async ctx => {});

存在风险

session劫持,把ssid拿走,粘贴到网站上

  1. session定期更换id -- 设置有效期
  2. 签名

cookieSession 可以传入keys 或者secret。

token验证

  1. 登录成功,服务器生成一个令牌,可以通过jwt,返回给浏览器
  2. 浏览器取到cookie, 存在cookie中,再次发送请求时,检验token是否合法
  3. 从token中解析用户信息
  4. 建议使用加密后的密码来做签名,确保每个人都不一样

优点
服务端更加轻便可以实现很多可能
缺点:
不安全,任何人拿到令牌都可以通过验证
数据一般通过base64编码的,所以不是很安全,因此不要存入敏感信息,密码之类的

// npm install jsonwebtoken koa-jwt -S;
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 // 失效时间1小时
            },
            secret
        )
    }
})

router.get('/getUser-token', jwtAuth({
    secret
}), async ctx => {
    ctx.body = {
        message: '成功',
        userinfo: ctx.state.user.data
    }
})

jwt签名原理:

签名只是防篡改 原理为HMAC SHA256(散列反篡改)

因为前端不知道服务器的secret,所以没办法准确的修改token,任何的改动都是不合法的,所以可以有效防止篡改

中间的数据部分,使用的是base64编码,他可以反解出来,因此不能存放敏感信息

Beare 是一种认证类型,(基于Oauth2.0), 使用 Bearer关键词进行定义

Oauth开放授权

三方登入主要基于Oauth2.0,Oauth协议为用户资源的授权提供了一个安全的,开放而又简单的标准,与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的账号信息(如用户名和密码), 即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAuth是安全的。

流程:

  1. 选择授权方式,发送请求
  2. 接口响应02,重定向到第三方网站,传入一个callback回调地址
  3. 用户在第三方网站点击授权,接口返回302,重定向回来,url携带参数code
  4. 通过code获取token
  5. 通过token获取到需要的用户信息

图形验证码

trek-captcha

npm i trek-captcha -s
const captcha = require('trek-captcha');
const { token, buffer } = await captcha({size: 4})