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


Posted in Javascript onAugust 04, 2019

最近在重构公司以前产品的前端代码,摈弃了以前的session-cookie鉴权方式,采用token鉴权,忙里偷闲觉得有必要对几种常见的鉴权方式整理一下。

目前我们常用的鉴权有四种:

  1. HTTP Basic Authentication
  2. session-cookie
  3. Token 验证
  4. OAuth(开放授权)

一.HTTP Basic Authentication

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

认证过程:

  1. 客户端向服务器请求数据,请求的内容可能是一个网页或者是一个ajax异步请求,此时,假设客户端尚未被验证,则客户端提供如下请求至服务器:

  Get /index.html HTTP/1.0
  Host:www.google.com

  2. 服务器向客户端发送验证请求代码401,(WWW-Authenticate: Basic realm=”google.com”这句话是关键,如果没有客户端不会弹出用户名和密码输入界面)服务器返回的数据大抵如下:

  HTTP/1.0 401 Unauthorised
  Server: SokEvo/1.0
  WWW-Authenticate: Basic realm=”google.com”
  Content-Type: text/html
  Content-Length: xxx

  3. 当符合http1.0或1.1规范的客户端(如IE,FIREFOX)收到401返回值时,将自动弹出一个登录窗口,要求用户输入用户名和密码。

  4. 用户输入用户名和密码后,将用户名及密码以BASE64加密方式加密,并将密文放入前一条请求信息中,则客户端发送的第一条请求信息则变成如下内容:

  Get /index.html HTTP/1.0
  Host:www.google.com
  Authorization: Basic d2FuZzp3YW5n

注:d2FuZzp3YW5n表示加密后的用户名及密码(用户名:密码 然后通过base64加密,加密过程是浏览器默认的行为,不需要我们人为加密,我们只需要输入用户名密码即可)

  5. 服务器收到上述请求信息后,将Authorization字段后的用户信息取出、解密,将解密后的用户名及密码与用户数据库进行比较验证,如用户名及密码正确,服务器则根据请求,将所请求资源发送给客户端

效果: 

客户端未未认证的时候,会弹出用户名密码输入框,这个时候请求时属于pending状态,这个时候其实服务当用户输入用户名密码的时候客户端会再次发送带Authentication头的请求。

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

认证成功:

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

server.js

let express = require("express");
let app = express();

  app.use(express.static(__dirname+'/public'));

  app.get("/Authentication_base",function(req,res){
    console.log('req.headers.authorization:',req.headers)
    if(!req.headers.authorization){
      res.set({
        'WWW-Authenticate':'Basic realm="wang"'
      });
      res.status(401).end();
    }else{
      let base64 = req.headers.authorization.split(" ")[1];
      let userPass = new Buffer(base64, 'base64').toString().split(":");
      let user = userPass[0];
      let pass = userPass[1];
      if(user=="wang"&&pass="wang"){
        res.end("OK");
      }else{
        res.status(401).end();
      }

    }

  })

  app.listen(9090)

index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>HTTP Basic Authentication</title>
  </head>
  <body>
    <div></div>
    <script src="js/jquery-3.2.1.js"></script>
    <script>
      $(function(){
       send('./Authentication_base');
      })
      var send = function(url){
            $.ajax({ 
            url : url, 
            method : 'GET', 
          });
      }
    </script>
  </body>
</html>

  当然有登陆就有注销,我们会发现当我们认证成功后每次请求请求头都会带上Authentication及里面的内容,那么如何做到让这次登陆失效的?

  网上查了半天,目前最有效的方式就是在注销操作的时候,专门在服务器设置一个专门的注销账号,当接收到的Authentication信息为注销用户名密码的时候纠就带便注销成功了,而客户端在注销操作的时候,手动的的去修改请求头重的Authentication,将他设置未服务器默认的注销账号和密码。

  通过上面的简单讲解 其实我们已经可以返现这种验证方式的缺陷加密方式简单,仅仅是base64加密,这种加密方式是可逆的。同时在每个请求的头上都会附带上用户名和密码信息,这样在外网是很容易被嗅探器探测到的。

总结:

  正式因为这样,这种加密方式一般多被用在内部安全性要求不高的的系统上,只是相对的多,总的来说现在使用这种鉴权比较少了。如果项目需要部署在公网上,这种方式不推荐,当然你也可以和SSL来加密传输,这样会好一点,这个如果我后面有时间来研究一下。

二.session-cookie

第二种这个方式是利用服务器端的session(会话)和浏览器端的cookie来实现前后端的认证,由于http请求时是无状态的,服务器正常情况下是不知道当前请求之前有没有来过,这个时候我们如果要记录状态,就需要在服务器端创建一个会话(seesion),将同一个客户端的请求都维护在各自得会会话中,每当请求到达服务器端的时候,先去查一下该客户端有没有在服务器端创建seesion,如果有则已经认证成功了,否则就没有认证。
session-cookie认证主要分四步:
  1,服务器在接受客户端首次访问时在服务器端创建seesion,然后保存seesion(我们可以将seesion保存在内存中,也可以保存在redis中,推荐使用后者),然后给这个session生成一个唯一的标识字符串,然后在响应头中种下这个唯一标识字符串。
   2.签名。这一步只是对sid进行加密处理,服务端会根据这个secret密钥进行解密。(非必需步骤)
  3.浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次http请求de 请求头中会带上该域名下的cookie信息,
   4.服务器在接受客户端请求时会去解析请求头cookie中的sid,然后根据这个sid去找服务器端保存的该客户端的session,然后判断该请求是否合法。

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

server.js(nodejs+express+seesion+redis)

var express = require('express');
var RedisStore = require('connect-redis')(express.session);
var app = express();
var secret = "wang839305939"
// 设置 Cookie
app.use(express.cookieParser(secret));

// 设置 Session
app.use(express.session({
 store: new RedisStore({
  host: "127.0.0.1",
  port: 6379,
  db: "session_db"
 }),
 secret: secret
}))

app.get("/", function(req, res) {
 var session = req.session;
 session.time= session.time|| 0;
 var n = session.time++;
 res.send('hello, session id:' + session.id + ' count:' + n);
});

app.listen(9080);

三.Token 验证

使用基于 Token 的身份验证方法,大概的流程是这样的:

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

  总的来说就是客户端在首次登陆以后,服务端再次接收http请求的时候,就只认token了,请求只要每次把token带上就行了,服务器端会拦截所有的请求,然后校验token的合法性,合法就放行,不合法就返回401(鉴权失败)。

  乍的一看好像和前面的seesion-cookie有点像,seesion-cookie是通过seesionid来作为浏览器和服务端的链接桥梁,而token验证方式貌似是token来起到seesionid的角色。其实这两者差别是很大的。
  1. sessionid 他只是一个唯一标识的字符串,服务端是根据这个字符串,来查询在服务器端保持的seesion,这里面才保存着用户的登陆状态。但是token本身就是一种登陆成功凭证,他是在登陆成功后根据某种规则生成的一种信息凭证,他里面本身就保存着用户的登陆状态。服务器端只需要根据定义的规则校验这个token是否合法就行。
  2. session-cookie是需要cookie配合的,居然要cookie,那么在http代理客户端的选择上就是只有浏览器了,因为只有浏览器才会去解析请求响应头里面的cookie,然后每次请求再默认带上该域名下的cookie。但是我们知道http代理客户端不只有浏览器,还有原生APP等等,这个时候cookie是不起作用的,或者浏览器端是可以禁止cookie的(虽然可以,但是这基本上是属于吃饱没事干的人干的事)…,但是token 就不一样,他是登陆请求在登陆成功后再请求响应体中返回的信息,客户端在收到响应的时候,可以把他存在本地的cookie,storage,或者内存中,然后再下一次请求的请求头重带上这个token就行了。简单点来说cookie-session机制他限制了客户端的类型,而token验证机制丰富了客户端类型。
   3. 时效性。session-cookie的sessionid实在登陆的时候生成的而且在登出事时一直不变的,在一定程度上安全就会低,而token是可以在一段时间内动态改变的。
   4. 可扩展性。token验证本身是比较灵活的,一是token的解决方案有许多,常用的是JWT,二来我们可以基于token验证机制,专门做一个鉴权服务,用它向多个服务的请求进行统一鉴权。

下面就拿最常用的JWT(JSON WEB TOKEN)来说:

   JWT是Auth0提出的通过对JSON进行加密签名来实现授权验证的方案,就是登陆成功后将相关信息组成json对象,然后对这个对象进行某中方式的加密,返回给客户端,客户端在下次请求时带上这个token,服务端再收到请求时校验token合法性,其实也就是在校验请求的合法性。
JWT对象通常由三部分构成:

Headers: 包括类别(typ)、加密算法(alg)

{
   "alg": "HS256",
   "typ": "JWT"
  }

Claims :包括需要传递的用户信息

{
   "sub": "1234567890",
   "name": "John Doe",
   "admin": true
  }

Signature: 根据alg算法与私有秘钥进行加密得到的签名字串, 这一段是最重要的敏感信息,只能在服务端解密;

HMACSHA256( 
  base64UrlEncode(Headers) + "." +
  base64UrlEncode(Claims),
  SECREATE_KEY
)

编码之后的JWT看起来是这样的一串字符:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

nodejs+express+jwt-simple

auth.js

let jwt = require('jwt-simple');
let secret = "wangyy";
let time = 10;
 module.exports = { 
 /*
 *检验token合法性
 */ 
 validate:function(req,res,next){ 
   let token = req.body.token||req.headers["xssToken"];
    if(token){ 
     let decodeToken = null;
     try { //防止假冒token解析?箦e 
       decodeToken = jwt.decode(token,secret,'HS256'); 
     } catch (err) { 
      res.status(401).send("非法访问"); return; 
     } 
    let exp = decodeToken.exp; if(!exp){
    res.status(401).send("非法访问");
   }
   let now = new Date().getTime();
   if(exp>(now+time*60*1000)){
    res.send({code:'002',"errorMsg":"授权超时"})
   }
   next();
  }else{ 
    res.status(401).send("非法访问");
  }
 },
 /* 生成token*/ 
 makeToken(){ 
   let Token = null; 
   let payload = { 
       time:new Date().getTime(), 
       exp:this.makeExp(time) 
       } 
   Token = jwt.encode(payload,secret,HS256) return Token; 
 }, 
 /*生成token过期时间*/ 
 makeExp:function(time){
   let stam = time601000; 
  } 
 }

server.js

let express = require("express"); 
let app = express(); 
let bodyParser = require('body-parser'); 
let auth = require('./lib/auth.js'); 
let chalk = require('chalk'); app.use(bodyParser.json()); app.post('/login',function(req,res,next){ 
      let Token = auth.makeToken(); 
      res.json({result:"success",token:Token},200)
  });
app.use('*',[auth.validate],function(req,res,next){ 
   res.send('success'); 
 }); 
app.listen('9999')

  上面只是一个简单的token生成和校验,如果有需要可以根据实际需要进行逻辑处理

四.OAuth(开放授权)

  OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容,为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。我们常见的提供OAuth认证服务的厂商有支付宝,QQ,微信。
  OAuth协议又有1.0和2.0两个版本。相比较1.0版,2.0版整个授权验证流程更简单更安全,也是目前最主要的用户身份验证和授权方式。

下面是一张auth2.0的流程图:

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

从图中我们可以看出,auth2.0流程分为六布(我们就以csdn登陆为例):

第一步. 向用户请求授权,现在很多的网站在登陆的时候都有第三方登陆的入口,当我们点击等第三方入口时,第三方授权服务会引导我们进入第三方登陆授权页面。

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

通过第三方请求授权页面的浏览器地址栏地址可以看出,

https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=100270989&redirect_uri=https%3A%2F%2Fpassport.csdn.net%2Faccount%2Flogin%3Foauth_provider%3DQQProvider&state=test

这里的地址里面的%是浏览器强制编码后的显示我们可以使用decodeURIComponent进行解码,解码后是这样:

https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=100270989&redirect_uri=https://passport.csdn.net/account/login?oauth_provider=QQProvider&state=test

这个url地址我们可以看见Auth2.0常见的几个参数:
      response_type,返回类型
      client_id,第三方应用id,由授权服务器(qq)在第三方应用提交时颁发给第三方应用。
      redirect_uri,登陆成功重定向页面
      oauth_provider,第三方授权提供方
      state,由第三方应用给出的随机码
第二步. 返回用户凭证(code),并返回一个凭证(code),当用户点击授权并登陆后,授权服务器将生成一个用户凭证(code)。这个用户凭证会附加在重定向的地址redirect_uri的后面

https://passport.csdn.net/account/login?code=9e3efa6cea739f9aaab2&state=XXX

第3步. 请求授权服务器授权:

  经过第二部获取code后后面的工作就可以交给后台去处理的,和用户的交互就结束了。接下来我的需要获取Access Token,我们需要用他来向授权服务器获取用户信息等资源。
  第三方应用后台通过第二步的凭证(code)向授权服务器请求Access Token,这时候需要以下几个信息:

  • client_id 标识第三方应用的id,由授权服务器(Github)在第三方应用提交时颁发给第三方应用
  • client_secret 第三方应用和授权服务器之间的安全凭证,由授权服务器(Github)在第三方应用提交时颁发给第三方应用
  • code 第一步中返回的用户凭证redirect_uri 第一步生成用户凭证后跳转到第二步时的地址
  • state 由第三方应用给出的随机码

第四步. 授权服务器同意授权后,返回一个资源访问的凭证(Access Token)。

第五步. 第三方应用通过第四步的凭证(Access Token)向资源服务器请求相关资源。

第六步. 资源服务器验证凭证(Access Token)通过后,将第三方应用请求的资源返回。

从用户角度来说,第三方授权可以让我们快速的登陆应用,无需进行繁琐的注册,同时不用记住各种账号密码。只需要记住自己常用的几个账号就ok了。
从产品经理的角度来所,这种授权方式提高用户的体验满意度。另一方面可以获取更多的用户。

总结:

  授权方式多种多样,主要还是要取决于我们对于产品的定位。如果我们的产品只是在企业内部使用,token和session就可以满足我们的需求,如果是面向互联网的大众用户,那么第三方授权在用户体验度上会有一个很大的提升。

  还是那句话,上面可能有很多‘通假字'勿怪,我写作的目的一方面是希望和大家分享我掌握的点点滴滴,另一方面也是梳理一下掌握的知识。

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

Javascript 相关文章推荐
IE8 引入跨站数据获取功能说明
Jul 22 Javascript
JavaScript 对象的属性和方法4种不同的类型
Mar 19 Javascript
解析使用JS 清空File控件的路径值
Jul 08 Javascript
httpclient模拟登陆具体实现(使用js设置cookie)
Dec 11 Javascript
jquery选择器之层级过滤选择器详解
Jan 27 Javascript
jquery ui bootstrap 实现自定义风格
Nov 14 Javascript
js在指定位置增加节点函数insertBefore()用法实例
Jan 12 Javascript
javascript防篡改对象实例详解
Apr 10 Javascript
通过封装scroll.js 获取滚动条的值
Jul 13 Javascript
vue项目引入字体.ttf的方法
Sep 28 Javascript
ES11屡试不爽的新特性,你用上了几个
Oct 21 Javascript
解决Antd Table表头加Icon和气泡提示的坑
Nov 17 Javascript
vue的注意规范之v-if 与 v-for 一起使用教程
Aug 04 #Javascript
vue v-for 使用问题整理小结
Aug 04 #Javascript
在Vue项目中用fullcalendar制作日程表的示例代码
Aug 04 #Javascript
JS实现的tab切换并显示相应内容模块功能示例
Aug 03 #Javascript
原生JS 实现的input输入时表格过滤操作示例
Aug 03 #Javascript
vue.js基于v-for实现批量渲染 Json数组对象列表数据示例
Aug 03 #Javascript
vue.js实现数据库的JSON数据输出渲染到html页面功能示例
Aug 03 #Javascript
You might like
php 获取mysql数据库信息代码
2009/03/12 PHP
php自定义函数之递归删除文件及目录
2010/08/08 PHP
让PHP以ROOT权限执行系统命令的方法
2011/02/10 PHP
一个显示效果非常不错的PHP错误、异常处理类
2014/03/21 PHP
php如何实现不借助IDE快速定位行数或者方法定义的文件和位置
2017/01/17 PHP
form自动提交实例讲解
2017/07/10 PHP
PHP闭包定义与使用简单示例
2018/04/13 PHP
php使用gearman进行任务分发操作实例详解
2020/02/26 PHP
Iframe自适应高度绝对好使的代码 兼容IE,遨游,火狐
2011/01/27 Javascript
js下获得客户端操作系统的函数代码(1:vista,2:windows7,3:2000,4:xp,5:2003,6:2008)
2011/10/31 Javascript
jQuery EasyUI API 中文文档 - DataGrid数据表格
2011/11/17 Javascript
简单实用的反馈表单无刷新提交带验证
2013/11/15 Javascript
window.location.href中url中数据量太大时的解决方法
2013/12/23 Javascript
浅谈JavaScript函数的四种存在形态
2016/06/08 Javascript
Javascript基础_标记文字的实现方法
2016/06/14 Javascript
js数组去重的hash方法
2016/12/22 Javascript
微信小程序 自定义对话框实例详解
2017/01/20 Javascript
微信小程序的生命周期的详解
2017/10/19 Javascript
微信小程序中遇到的iOS兼容性问题小结
2018/11/14 Javascript
Node.js Buffer模块功能及常用方法实例分析
2019/01/05 Javascript
angularjs自定义过滤器demo示例
2019/08/24 Javascript
vue新建项目并配置标准路由过程解析
2019/12/09 Javascript
python保存网页图片到本地的方法
2018/07/24 Python
在macOS上搭建python环境的实现方法
2019/08/13 Python
解决python 虚拟环境删除包无法加载的问题
2020/07/13 Python
基于Python爬取素材网站音频文件
2020/10/21 Python
全球性的女装店:storets
2019/06/12 全球购物
办公室文员自荐书
2014/02/03 职场文书
销售内勤岗位职责
2014/04/15 职场文书
2014年小学生迎国庆65周年演讲稿
2014/09/27 职场文书
工程部岗位职责
2015/02/10 职场文书
2015年学校教育教学工作总结
2015/04/22 职场文书
详解Nginx 工作原理
2021/03/31 Servers
PHP控制循环操作的时间
2021/04/01 PHP
opencv读取视频并保存图像的方法
2021/06/04 Python
Python下opencv库的安装过程及问题汇总
2021/06/11 Python