聊聊鉴权那些事(推荐)

在系统级项目开发时常常会遇到一个问题就是鉴权,身为一个前端来说可能我们距离鉴权可能比较远,一般来说我们也只是去应用,并没有对权限这一部分进行深入的理解。

什么是鉴权

鉴权:是指验证用户是否拥有访问系统的权利。传统的鉴权是通过密码来验证的。这种方式的前提是,每个获得密码的用户都已经被授权。在建立用户时,就为此用户分配一个密码,用户的密码可以由管理员指定,也可以由用户自行申请。这种方式的弱点十分明显:一旦密码被偷或用户遗失密码,情况就会十分麻烦,需要管理员对用户密码进行重新修改,而修改密码之前还要人工验证用户的合法身份。 -- 节选自百度百科

上述简单扼要的说明了一下鉴权的概念,但是这也只是简单的鉴权,也是项目中最最常见的及安全形式了,但是对于后端鉴权又是如何去做的,我们仍是一无所知,一般来说对于后端来说,鉴权最长见的方式分为三种:

  • Session/Cookie
  • Token或Jwt
  • OAuth

这种授权方式是浏览器遵守http协议实现的基本授权方式,HTTP协议进行通信的过程中,HTTP协议定义了基本认证认证允许HTTP服务器对客户端进行用户身份证的方法。接下来就一一介绍一下这三种鉴权方式。

Session/Cookie

Cookie是一个非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。Cookie由服务器生成,发送给浏览器,浏览器把CookieKV形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该Cookie发送给服务器。由于Cookie是存在客户端上的,所以浏览器加入了一些限制确保Cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的Cookie数量是有限的。

Cookie.js

const Http = require("http");
const app = Http.createServer((req,res) => {
 if(req.url === "/favicon.ico"){
  return;
 }else{
  res.setHeader("Set-Cookie","cx=Segmentfault");
  res.end("hello cookie");
 };
});
app.listen(3000);

使用node Cookie.js运行上面代码,等程序启动后访问http://localhost:3000/,就可以看到hello cookie字样,这样的话就代表该服务已经启动了。若想查看到到我们所设置的Cookie,首先观察一下在NetworkResponse Headers中,可以看到我们所写的Set-Cookie属性,当我们访问http://localhost:3000/的时候,当浏览器接收到Set-Cookie这个属性的时候,浏览器会根据其内部约定,并在其浏览器内部对其cookie进行存储,打开浏览器控制台,在Application中找到Cookies中找到相对应的域名,就可以看到我们所设置的cookie值了。当在同域的情况下,当再次请求数据的时候浏览器会默认发送cookie在该请求中,一起发送给后端。为了证实上面的说法,刷新一下http://localhost:3000/页面,在控制台Network找到Request Headers中可以看到Cookie: cx=Segmentfault属性,既然发送给服务端之后,相应的在后端也是可以接收到该Cookie的,修改一下上面的例子:

const Http = require("http");
const app = Http.createServer((req,res) => {
 if(req.url === "/favicon.ico"){
  return;
 }else{
  console.log("cookie",req.headers.cookie)
  res.setHeader("Set-Cookie","cx=Segmentfault");
  res.end("hello cookie");
 };
});
app.listen(3000);

在接收到访问的时候,就可以接收到了cx=Segmentfault,如果说现在这份Cookie是一份加密的数据的话,里面包含一些用户信息,在通过前后端进行交互之后,当客户端再次请求服务端的时候,服务端拿到相对应的Cookie并对其进行解密,对其中用户的信息进行鉴权处理就可以了。

服务端通过Set-CookieResponse Headers设置了一段加密数据,客户端接收到了其相对应的数据之后,浏览器对其进行存储,当可客户端再次发送请求的时候,会携带已有的CookieRequest Headers中一并发送给服务端,服务端解密数据完成鉴权,由此可以得出Cookie是服务端存储在客服端的状态标志,再由客户端发送给服务端,由服务端解析。Cookie在使用中必须是同域的情况下才可以,一般常用的是在MVC这种开发形式中很常用。

说了半天Cookie,但是对于Session却只字未提,接下来就介绍一下SessionSession从字面上讲,就是会话。这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三。Session也是类似的道理,服务器要知道当前发请求给自己的是谁。为了做这种区分,服务器就要给每个客户端分配不同的身份标识,然后客户端每次向服务器发请求的时候,都带上这个身份标识,服务器就知道这个请求来自于谁了。至于客户端怎么保存这个身份标识,可以有很多种方式,对于浏览器客户端,大家都默认采用Cookie的方式。

const Http = require("http");
let session = {};
const app = Http.createServer((req,res) => {
 const sessionKey = "uId";
 if(req.url === "/favicon.ico"){
  return;
 }else{
  const uId = parseInt(Math.random() * 10e10);
  const cookie = req.headers.cookie;
  if(cookie && cookie.indexOf(sessionKey) !== -1){
   let _uId = cookie.split("=")[1];
   res.end(`${session[_uId].name} Come back`);
  }
  else{
   res.setHeader("Set-Cookie",`${sessionKey}=${uId}`);
   session[uId] = {"name":"Aaron"};
   res.end("hello cookie");
  }
 };
});
app.listen(3000);

代码中解析cookie只是用了和很简单的方式,只是为了完成Dome而已,在实际项目中获取cookie比这个要复杂很多。

Session/Cookie认证主要分四步:

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

利用服务器端的session和浏览器端的cookie来实现前后端的认证,由于http请求时是无状态的,服务器正常情况下是不知道当前请求之前有没有来过,这个时候我们如果要记录状态,就需要在服务器端创建一个会话(seesion),将同一个客户端的请求都维护在各自得会会话中,每当请求到达服务器端的时候,先去查一下该客户端有没有在服务器端创建seesion,如果有则已经认证成功了,否则就没有认证。

redis结合使用:

const koa = require("koa");
const session = require("koa-session");
const redisStore = require("koa-redis");
const redis = require("redis");
const wrapper = require("co-redis");
const app = new koa();
const redisClient = redis.createClient(6379,"localhost");
const client = wrapper(redisClient);
// 类似于密钥
app.keys = ["Aaron"];
const SESSION_CONFIG = {
 // 所设置的session的key
 key:"sId",
 // 最大有效期
 maxAge:8640000,
 // 是否防止js读取
 httpOnly:true,
 // cookie二次签名
 signed:true,
 // 存储方式
 stroe:redisStore({client})
};
app.use(session(SESSION_CONFIG,app));
app.use((ctx) => {
 redisClient.keys("*",(err,keys) => {
  keys.forEach(key => {
   redisClient.get(key,(err,val) => {
    console.log(val);
   });
  })
 })
 if(ctx.path === "/favicon.ico") return;
 let n = ctx.session.count || 0;
 ctx.session.count = ++n;
 ctx.body = `第${n}次访问`
});
app.listen(3000);

虽然Session/Cookie可以解决鉴权问题,但是会有很大的问题,对于服务端来说说是一个巨大的开销,严重的限制了服务器扩展能力,比如说我用两个机器组成了一个集群,小F通过机器A登录了系统,那sessionId会保存在机器A上,假设小F的下一次请求被转发到机器B怎么办?机器B可没有小F的sessionId,有时候会采用一点小伎俩:session sticky,就是让小F的请求一直粘连在机器A上,但是这也不管用,要是机器A挂掉了, 还得转到机器B去。那只好做session的复制了,把sessionId在两个机器之间搬来搬去,再好的服务器也经不起这样的折腾。

Token或Jwt

在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般作为邀请、登录系统使用。现在前后端分离火热,Token混的风生水起,很多项目开发过程中都会用到Token,其实Token是一串字符串,通常因为作为鉴权凭据,最常用的使用场景是API鉴权。

客户端使用用户名跟密码请求登录服务端收到请求,去验证用户名与密码验证成功后,服务端会签发一个Token,再把这个Token发送给客户端客户端收到Token以后可以把它存储起来,比如放在Cookie里或者Local Storage里客户端每次向服务端请求资源的时候需要带着服务端签发的Token服务端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功,就向客户端返回请求的数据

示例:

前端

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title>Document</title>
</head>
<body>
<div id="app">
 <div>
  <input type="text" v-model="username">
  <input type="text" v-model="passwrold">
 </div>
 <div>
  <button @click="login">登陆</button>
  <button @click="loginOut">退出</button>
  <button @click="getUserInfo">获取用户信息</button>
 </div>
 <div>
  <button @click="logs = []">清空日志</button>
 </div>
 <ul>
  <li v-for="(item,index) of logs" :key="index">{{item}}</li>
 </ul>
</div>
<script>
axios.defaults.baseURL = "http://localhost:3000"
// 请求拦截
axios.interceptors.request.use((config) => {
 const token = localStorage.getItem("token");
 if(token){
  // 判断是否存在token,如果存在的话
  // 每次发起HTTP请求时在headers中添加token
  // Bearer是JWT的认证头部信息
  config.headers["Authorization"] = `Bearer ${token}`
 }
 return config;
},error => alert(error));
// 响应拦截
axios.interceptors.response.use((res) => {
 app.logs.push(JSON.stringify(res.data))
 return res;
},error => alert(error));
const app = new Vue({
 el:"#app",
 data:{
  username:"",
  passwrold:"",
  logs:[]
 },
 methods:{
  login() {
   let {username,passwrold} = this;
   axios.post("/users/login/token",{
    username,passwrold
   }).then((res) => {
    localStorage.setItem("token",res.data.token)
   })
  },
  loginOut(){
   axios.post("/users/logout").then((res) => {
    localStorage.removeItem("token")
   })

  },
  getUserInfo(){
   axios.get("/users/get/user/info").then((res) => {
    console.log(res)
   });
  }
 }
})
</script>
</body>
</html>

后端:

const Koa = require("koa");
const jwt = require("jsonwebtoken");
const jwtAuth = require("koa-jwt");
const Router = require('koa-router'); // koa 路由中间件
const bodyParser = require("koa-bodyparser");
const cors = require("koa2-cors");
const app = new Koa();
const router = new Router();
// 密钥
const secret = "this is a secret";
app.use(bodyParser());
app.use(cors());
router.post("/users/login/token",(ctx) => {
 const {body} = ctx.request;
 const {username} = body;
 ctx.body = {
  code:1,
  message:"登陆成功",
  body:{
  username
  },
  token:jwt.sign({
   data:body,
   exp:Math.floor(Date.now() / 1000) + 60 * 60,
  },secret)
 }
});
router.post("/users/logout",(ctx) => {
 const {body} = ctx.request;
 ctx.body = {
  code:1,
  message:"退出成功"
 }
})
router.get("/users/get/user/info",jwtAuth({secret}),(ctx) => {
 // jwtAuth token参数
 console.log(ctx.state.user.data)
 ctx.body = {
  code:1,
  message:"成功",
  data:ctx.state.user.data
 }
})
app.use(router.routes());
app.listen(3000);

上面代码用到了很多的依赖模块,最关键的的是jsonwebtokenkoa-jwt,这两个模块一个是用来对token进行加密,一个是用来对数据进行解密的,同时在每次访问需要保护的路由的时候需要使用jwtAuth对其进行拦截处理,jwtAuth会根据其secret进行数据解密,把解密的数据存放到ctx.state中,供用户读取。

有关jwt相关请查看深入理解令牌认证机制详细的解释了其加密后数据token的构成。

加密后的数据主要分为三个部分机密头部、载荷、数据如果我们想查看其加密前内容是什么样子的,可以通过base64对其没一部分进行解密。

  • 机密头部:声明加密规则,可反解
  • 载荷:数据信息,也就是我们需要加密的信息,可反解
  • 验证:这部分是对前两部分使用hash算法的摘要,是不可逆的

在使用jsonwebtoken时需要注意的是,由于加密信息是可以反解的所以,尽量不要在加密数据中存放敏感信息,比如用户的密码,用户私密信息等等(千万不要效仿Dome,这是不对的O(∩_∩)O)。同过上面所述,所传递给前端的token一旦发生变化,仅仅是一个字母大小写发生变化也是不行的,当服务端接收到token解密时,是无法正确解密的,这种token可以是发篡改的。如果想要篡改token必须要有其secret才可以对其进行篡改和伪造。

OAuth

OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容,为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。我们常见的提供OAuth认证服务的厂商有支付宝,QQ,微信

OAuth协议又有1.02.0两个版本。相比较1.0版,2.0版整个授权验证流程更简单更安全,也是目前最主要的用户身份验证和授权方式。

OAuth认证主要经历了如下几步:

  • 需要第三方应用存储资源所有者的凭据,以供将来使用,通常是明文密码。
  • 需要服务器支持密码身份认证,尽管密码认证天生就有安全缺陷。
  • 第三方应用获得的资源所有者的受保护资源的访问权限过于宽泛,从而导致资源所有者失去对资源使用时限或使用范围的控制。
  • 资源所有者不能仅撤销某个第三方的访问权限而不影响其它,并且,资源所有者只有通过改变第三方的密码,才能单独撤销这第三方的访问权限。
  • 与任何第三方应用的让步导致对终端用户的密码及该密码所保护的所有数据的让步。

简单概括,就是用于第三方在用户授权下调取平台对外开放接口获取用户相关信息。OAuth引入了一个授权环节来解决上述问题。第三方应用请求访问受保护资源时,资源服务器在获准资源用户授权后,会向第三方应用颁发一个访问令牌(AccessToken)。该访问令牌包含资源用户的授权访问范围、授权有效期等关键属性。第三方应用在后续资源访问过程中需要一直持有该令牌,直到用户主动结束该次授权或者令牌自动过期。

总结

授权方式多种多样,主要还是要取决于我们对于产品的定位。如果我们的产品只是在企业内部使用,tokensession就可以满足我们的需求,现在前后端分离如此火热jwt认证方式更加适合。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 前后端常见的几种鉴权方式(小结)

    最近在重构公司以前产品的前端代码,摈弃了以前的session-cookie鉴权方式,采用token鉴权,忙里偷闲觉得有必要对几种常见的鉴权方式整理一下. 目前我们常用的鉴权有四种: HTTP Basic Authentication session-cookie Token 验证 OAuth(开放授权) 一.HTTP Basic Authentication 这种授权方式是浏览器遵守http协议实现的基本授权方式,HTTP协议进行通信的过程中,HTTP协议定义了基本认证认证允许HTTP服务器对客

  • nuxt框架中路由鉴权之Koa和Session的用法

    引子 博客的后台管理页面需要有登录系统,所以考虑做一下路由鉴权,实现方式也是 Nuxt 官网给出栗子来改写,顺便也将前后端路由给统一了. 路由拦截 前端方面主要通过利用 Nuxt 的中间件来做路由拦截,这里也是需要 Vuex 状态树来做. middleware middleware/auth.js export default function ({ store, redirect }) { if (!store.state.user) { return redirect('/login') }

  • 详解用JWT对SpringCloud进行认证和鉴权

    JWT(JSON WEB TOKEN)是基于RFC 7519标准定义的一种可以安全传输的小巧和自包含的JSON对象.由于数据是使用数字签名的,所以是可信任的和安全的.JWT可以使用HMAC算法对secret进行加密或者使用RSA的公钥私钥对来进行签名. JWT通常由头部(Header),负载(Payload),签名(Signature)三个部分组成,中间以.号分隔,其格式为Header.Payload.Signature Header:声明令牌的类型和使用的算法 alg:签名的算法 typ:to

  • 详解nuxt路由鉴权(express模板)

    这里我们以用户登录鉴权为例 express依赖express-session中间件实现session功能 若我们不加载express-session组件 router.get('/user/login', function (req, res) { console.log(req,req.session) }) 会发现不存在session对象 引入express-session组件 // server/index.js文件 ... import session from 'express-ses

  • Node.js Koa2使用JWT进行鉴权的方法示例

    前言 在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行保护,那么别人就可以很容易地获取并调用这些 API 进行操作.那么服务器端要如何进行鉴权呢? Json Web Token 简称为 JWT,它定义了一种用于简洁.自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法.JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名. 说得好像跟真的一样,那么到底要怎么进行认证呢? 首先用户登录时,输入用户名和密码后请求服务器登录接口

  • 一步步教会你微信小程序的登录鉴权

    前言 为了方便小程序应用使用微信登录态进行授权登录,微信小程序提供了登录授权的开放接口.乍一看文档,感觉文档上讲的非常有道理,但是实现起来又真的是摸不着头脑,不知道如何管理和维护登录态.本文就来手把手的教会大家在业务里如何接入和维护微信登录态,下面话不多说了,来一起看看详细的介绍吧. 接入流程 这里官方文档上的流程图已经足够清晰,我们直接就该图展开详述和补充. 首先大家看到这张图,肯定会注意到小程序进行通信交互的不止是小程序前端和我们自己的服务端,微信第三方服务端也参与其中,那么微信服务端在其中

  • springmvc用于方法鉴权的注解拦截器的解决方案代码

    最近在用SpringMvc写项目的时候,遇到一个问题,就是方法的鉴权问题,这个问题弄了一天了终于解决了,下面看下解决方法 项目需求:需要鉴权的地方,我只需要打个标签即可,比如只有用户登录才可以进行的操作,一般情况下我们会在执行方法时先对用户的身份进项校验,这样无形中增加了非常大的工作量,重复造轮子,有了java注解只需要在需要鉴权的方法上面打个标签即可: 解决方案: 1.首先创建一个注解类: @Documented @Inherited @Target({ElementType.METHOD,E

  • Vue中axios的封装(报错、鉴权、跳转、拦截、提示)

    统一捕获接口报错 弹窗提示 报错重定向 基础鉴权 表单序列化 实现的功能 统一捕获接口报错 : 用的axios内置的拦截器 弹窗提示: 引入 Element UI 的 Message 组件 报错重定向: 路由钩子 基础鉴权: 服务端过期时间戳和token,还有借助路由的钩子 表单序列化: 我这边直接用 qs (npm模块),你有时间也可以自己写 用法及封装 用法 // 服务层 , import默认会找该目录下index.js的文件,这个可能有小伙伴不知道 // 可以去了解npm的引入和es6引入

  • Java中使用JWT生成Token进行接口鉴权实现方法

    先介绍下利用JWT进行鉴权的思路: 1.用户发起登录请求. 2.服务端创建一个加密后的JWT信息,作为Token返回. 3.在后续请求中JWT信息作为请求头,发给服务端. 4.服务端拿到JWT之后进行解密,正确解密表示此次请求合法,验证通过:解密失败说明Token无效或者已过期. 流程图如下: 一.用户发起登录请求 二.服务端创建一个加密后的JWT信息,作为Token返回 1.用户登录之后把生成的Token返回给前端 @Authorization @ResponseBody @GetMappin

  • thinkjs微信中控之微信鉴权登陆的实现代码

    前言 上一篇文章大概写了一下如何搭一个微信中控服务: [thinkjs搭建微信中控服务] . 接下来这篇,专门写一下如何在此基础上扩展出来一个比较好用的微信鉴权登陆的方案. 由于这一段的逻辑着实有点绕,所以就单独拿出来写了. 有时候,调用方甚至可以通过这个方案,进行多公众号openid的之间的关联. 官方说明 开发文档 微信文档地址:传送门 鉴权逻辑 前端跳转到以下url,重定向或者代码跳转都可以:https://open.weixin.qq.com/connect/oauth2/authori

  • koa2服务端使用jwt进行鉴权及路由权限分发的流程分析

    大体思路 后端书写REST api时,有一些api是非常敏感的,比如获取用户个人信息,查看所有用户列表,修改密码等.如果不对这些api进行保护,那么别人就可以很容易地获取并调用这些 api 进行操作. 所以对于一些api,在调用之前,我们在服务端必须先对操作者进行"身份认证",这就是所谓的鉴权. Json Web Token 简称为 JWT,它定义了一种通信双方之间以 JSON 对象的形式安全传递信息的方法.JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名,复杂度较

随机推荐