掌握三种常见的鉴权方式: Session/Cookie、Token+jwt、OAuth
Session/Cookie
Cookie
cookie的原理是,浏览器第一次向服务器发送请求时,服务器在response头部设置Set-Cookie字段,浏览器收到响应就会设置cookie并储存,在下一次该浏览器向服务器发送请求时,就会在repuset头部自动带上Cookie字段,服务器端收到该cookie用以区分不同的浏览器。当然,这个cookie与某个用户的对应关系应该在第一次访问时就存在服务器端,这时就需要session。
Session
session是会话的意思,浏览器第一次访问服务端,服务端就会创建一次会话,在绘画中保存表示该浏览器的信息。它与cookie的区别就是session是缓存在服务端的,cookie则是缓存在客户端,他们都由服务端生成,为了弥补Http协议无状态的缺陷。
下载并引用session
1. 下载并引入koa-session
2. app.keys来加密cookie
//keys作用:用来对cookie进行签名
//app.keys=签名的cookie 密钥数组
app.keys = ['session secret']
3. 配置session对象(具体配置属性用途看注释)
const SESSION_CONFIG = {
key:'qlq:session',//设置cookie的key的名字
maxAge: 86400000,//有效期,默认是一天
httpOnly: true,// 仅服务端修改 防止客户端修改
signed:true,//签名cookie
}
4. app.use使用session
app.use(session(session配置,要引用的服务器))
app.use(session(SESSION_CONFIG,app));
使用redis来储存session
1. 自行百度下载redis软件(windows端会有些特殊 需要下载特定的版本)
1.1 打开
1.2 如果有下图即打开了redis服务端
2. 下载并引入koa-redis、redis模块
2.1 用redis模块里的redis.createClient创建客户端redis
redis里的createClient方法(redis服务器端口,'主机名')
2.2 koa-redis中的redisStore是用来存储的
const redisStore = require('koa-redis')
const redis = require('redis')
const client = redis.createClient(6379,'localhost')
3. 将redis存储写入session配置中
store:redisStore({解构客户端}) 通过redisStore存储到redis客户端中
const SESSION_CONFIG = {
...
//如果有redis 就注册客户端到这里
store:redisStore({...client})
}
例子
假设前端通过axios.post向后端传递了数据
async handleLogin(){
let res = await axios.post('/login',{
user:xxx,
pwd:yyy
})
console.log(res);
}
在后端中可以直接用ctx.session.userInfo = body.user;来存储session(这里是koa框架的路由文件)
router.post('/login',async (ctx,next)=>{
// console.log(ctx.request.body);
const {body} = ctx.request;
// console.log('body',body);
//验证数据库 做存储
ctx.session.userInfo = body.user;
ctx.body = {
ok:1,
message:'登陆成功'
}
})
然后在需要用户登录才能获得数据的接口做是否有 ctx.session.userInfo的处理 如果有,则获取数据,没有则不获取数据
假设前端有个请求要获取登录后信息
async getUser(){
let res = await axios.get('/user/getUser')
console.log(res);
}
后端处理,这里第二个参数是写了一个鉴权文件来判断是否有session.userInfo
router.get('/getUser',require('../middleware/auth'),async (ctx,next)=>{
ctx.body = {
ok:1,
message:'获取数据成功',
userInfo: ctx.session.userInfo
}
})
鉴权文件
如果有session则放行 无则直接返回
module.exports = async(ctx,next)=>{
if(!ctx.session.userInfo){
ctx.body={
ok:0,
message:'用户未登录'
}
}else{
await next();
}
}
#### 删除session
在需要删除session的对应后端接口 用delete
```js
delete ctx.session.userInfo;
Token+jwt
token存储在localStorage
下载并引入koa-jwt、jsonwebtoken中间件(在对应路由)
const jwt = require('jsonwebtoken') //生产令牌
const jwtAuth = require('koa-jwt') //验证令牌
用户登录获取信息实例
前端请求代码
async handleLogin(){
let res = await axios.post('/user/login-token',{
user:this.user,
pwd:this.pwd
})
localStorage.setItem('token',res.data.token);
// console.log(res);
},
1. 后端接受到前端请求时要给其创建一个token
1.1 秘钥签名
const secret = 'this is a secreat' //签名
1.2 在ctx.body里用token属性:jwt.sigh()创建
jwt.sign() 两个参数 第一个参数是包含当前数据以及过期时间的对象 第二个参数是秘钥签名
router.post('/login-token',async (ctx,next)=>{
const {body} = ctx.request;
const userInfo = body.user;
ctx.body = {
ok:1,
message:'登陆成功',
user:userInfo,
token: jwt.sign({
data:userInfo, //由于签名不加密,令牌不要生成敏感信息如密码
exp:Math.floor(Date.now()/1000) + 60 * 60, //过期时间一分钟
},secret)
}
})
2. 前端对token进行操作
2.1 在前端登录的post请求中将token添加到localStorage
async handleLogin(){
let res = await axios.post('/user/login-token',{
user:this.user,
pwd:this.pwd
})
localStorage.setItem('token',res.data.token);
},
2.2 将token放入请求头中
创建一个请求拦截器,判断localStorage是否有token,如果有用
config.headers.common['authorization'] = 'Bearer ' + token;给每个http header都加上token
Bearer是jwt的认证头部信息 记得要加空格
axios.interceptors.request.use(config=>{
//对请求数据做点什么
const token = localStorage.getItem('token');
if(token){
// Bearer是jwt的认证头部信息 记得要加空格
config.headers.common['authorization'] = 'Bearer ' + token;
}
return config;
},err=>{
return Promise.reject(error);
})
3. 对需要鉴权的接口鉴权
假设前端有个获取用户信息需要权限
async getUser(){
let res = await axios.get('/user/getUser-token')
}
3.1 在后端接口用jwtAuth({验证签名})来验证
如果没有token或验证不通过会自动报错
router.get('/getUser-token', jwtAuth({ secret }),async (ctx,next)=>{
console.log(ctx.state);
ctx.body = {
userInfo: ctx.state.user.data,
message:'数据获取成功'
}
})
4.移除token
只需要在需要移除token的操作直接把token从localStorage移除,甚至都不需要访问后端
localStorage.removeItem('token')
OAuth(第三方鉴权)
做一个github第三方登录
首先一个前端页面登录
<a href="/user/login-github">github登录</a>
1.先模拟一个已登录用户
1.1 登录github 在setting中找到developer settings
1.2 找到OAuth Apps 创建一个OAuth App
下面的回调url就是oauth流程图里的
1.3 设置好后会有对应的id和secret
1.4 后端设置一个配置含有这两个值
const config = {
client_id: '7853729cd163dc5bfa58',
client_secret: 'c0495c79c022afc992930bc580fe5ebafb080dfa'
}
2. 后端收到get请求后跳转到github授权页面
注意这里是拿到授权id跟secret
router.get('/login-github', async (ctx, next) => {
//重定向到github授权页面
const path = `https://github.com/login/oauth/authorize?client_id=${config.client_id}`
ctx.redirect(path);
})
3. 后端创建一个github回调的接口(步骤1中回调url)来获取授权码
- 这里可以用ctx.query.code来获取code授权码
router.get('/oauth/github/callback', async (ctx, next) => { //这是一个授权回调,用于获取授权码
const code = ctx.query.code; //github 回调传回的code授权码
//带着授权码code、client_id、client_secret向github认证服务器请求token
const params = {
client_id: config.client_id,
client_secret: config.client_secret,
code: code
}
})
4. 携带client_id、client_secret、code向github要令牌token
这里用了queryString 要引入该模块
queryString.parse转化res.data 获取对应的access_token
这步骤卸载回调接口里
let res = await axios.post('https://github.com/login/oauth/access_token', params)
//要令牌token
const access_token = querystring.parse(res.data).access_token;
console.log(access_token);
5. 带着token从Github获取用户信息
注意要设置请求头 其它方式有可能会报错
res = await axios({
method: 'get',
url: 'https://api.github.com/user',
headers:{
accept:'application/json',
Authorization:`token ${access_token}`
}
})
后端的示例代码
const config = {
client_id: '7853729cd163dc5bfa58',
client_secret: 'c0495c79c022afc992930bc580fe5ebafb080dfa'
}
//OAuth授权登录
router.get('/login-github', async (ctx, next) => {
//重定向到github授权页面
const path = `https://github.com/login/oauth/authorize?client_id=${config.client_id}`
ctx.redirect(path);
})
router.get('/oauth/github/callback', async (ctx, next) => { //这是一个授权回调,用于获取授权码
const code = ctx.query.code; //github 回调传回的code授权码
//带着授权码code、client_id、client_secret向github认证服务器请求token
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)
//要令牌token
const access_token = querystring.parse(res.data).access_token;
console.log(access_token);
//带着token从Github获取用户信息
res = await axios({
method: 'get',
url: 'https://api.github.com/user',
headers:{
accept:'application/json',
Authorization:`token ${access_token}`
}
})
// res = await axios.get('https://api.github.com/user?access_token=' + access_token)
console.log('userAccess', res.data);
ctx.redirect('/hello.html')
})