用户认证(Authentication)进化之路:由Basic Auth到Oauth2再到jwt

原文转载自「刘悦的技术博客」v3u.cn/a_id_98前端

用户认证是一个在web开发中亘古不变的话题,由于不管是什么系统,什么架构,什么平台,安全性是一个永远也绕不开的问题web

在HTTP中,基本认证(Basic access authentication)是一种用来容许网页浏览器或其余客户端程序在请求时提供用户名和口令形式的身份凭证的一种登陆验证方式。
虽然基本认证很是容易实现,但该方案建立在如下的假设的基础上,即:客户端和服务器主机之间的链接是安全可信的。特别是,若是没有使用SSL/TLS(https)这样的传输层安全的协议,那么以明文传输的密钥和口令很容易被拦截。该方案也一样没有对服务器返回的信息提供保护。
现存的浏览器保存认证信息直到标签页或浏览器被关闭,或者用户清除历史记录。HTTP没有为服务器提供一种方法指示客户端丢弃这些被缓存的密钥。这意味着服务器端在用户不关闭浏览器的状况下,并无一种有效的方法来让用户注销。面试

OAuth 是一个关于受权(authorization)的开放网络标准。容许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。如今的版本是2.0版。
严格意义上来说,OAuth2不是一个标准协议,而是一个安全的受权框架。它详细描述了系统中不一样角色、用户、服务前端应用(好比API),以及客户端(好比网站或移动App)之间怎么实现相互认证。
最后,重点介绍一下JWT,JWT是一种安全标准。基本思路就是用户提供用户名和密码给认证服务器,服务器验证用户提交信息信息的合法性;若是验证成功,会产生并返回一个Token(令牌),用户可使用这个token访问服务器上受保护的资源。算法

JWT 特色:
1 体积小,于是传输速度快
2 传输方式多样,能够经过URL/POST参数/HTTP头部等方式传输
3 严格的结构化。它自身(在 payload 中)就包含了全部与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去链接数据库验证信息的有效性,而且 payload 支持为你的应用而定制化。
4 支持跨域验证,能够应用于单点登陆。数据库

JWT是Auth0提出的经过对JSON进行加密签名来实现受权验证的方案,编码以后的JWT看起来是这样的一串字符:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ  
复制代码

由 . 分为三段,经过解码能够获得:django

1 Header头部分头部分简单声明了类型(JWT)以及产生签名所使用的算法。{"alg":"AES256","typ":"JWT"}跨域

2 playload(载荷)中的Claims声明部分是整个token的核心,表示要发送的用户详细信息。有些状况下,咱们极可能要在一个服务器上实现认证,而后访问另外一台服务器上的资源;或者,经过单独的接口来生成token,token被保存在应用程序客户端(好比浏览器)使用。一个简单的声明(claim)的例子:{"sub":"1234567890","name":"John Doe","admin":true}浏览器

3 Signature签名签名的目的是为了保证上边两部分信息不被篡改。若是尝试使用Bas64对解码后的token进行修改,签名信息就会失效。通常使用一个私钥(private key)经过特定算法对Header和Claims进行混淆产生签名信息,因此只有原始的token才能于签名信息匹配。这里有一个重要的实现细节。只有获取了私钥的应用程序(好比服务器端应用)才能彻底认证token包含声明信息的合法性。因此,永远不要把私钥信息放在客户端(好比浏览器)。缓存

签名的目的:签名其实是对头部以及载荷内容进行签名。因此,若是有人对头部以及载荷的内容解码以后进行修改,再进行编码的话,那么新的头部和载荷的签名和以前的签名就将是不同的。并且,若是不知道服务器加密的时候用的密钥的话,得出来的签名也必定会是不同的。
这样就能保证token不会被篡改。安全

最后,咱们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,咱们还须要提供一个密钥(secret)。相似盐

这里在第三步咱们获得 JWT 以后,须要将JWT存放在 client,以后的每次须要认证的请求都要把JWT发送过来。(请求时能够放到 header 的 Authorization )

在web框架Django中的具体应用:

安装pyjwt

pip3 install pyjwt
复制代码

在用户登陆成功后,生成一个token

import jwt
encoded_jwt = jwt.encode({'username':'admin','site':'https://v3u.cn'},'secret_key',algorithm='HS256')

复制代码

将这个token交给前端,之后前端访问任意接口都将在header里带着这个令牌(token),用来作认证,而后咱们确定不能每个视图方法都作验证,因此能够利用装饰器作一个统一用户认证模块

#定义验证装饰器
from django.http import JsonResponse
import jwt
def auth_required():
    def decorator(view_func):
        def _wrapped_view(self,request, *args, **kwargs):


            try:
                auth = request.META.get('HTTP_AUTHORIZATION').split()
            except AttributeError:
                return HttpResponse('没权限')
            
            try:
                dict = jwt.decode(auth[1], settings.SECRET_KEY, algorithms=['HS256'])
                username = dict.get('data').get('username')
            except jwt.ExpiredSignatureError:
                return JsonResponse({"status_code": 401, "message": "Token expired"})
            except jwt.InvalidTokenError:
                return JsonResponse({"status_code": 401, "message": "Invalid token"})
            except Exception as e:
                return JsonResponse({"status_code": 401, "message": "Can not get user object"})
            return view_func(request, *args, **kwargs)

        return _wrapped_view

    return decorator
复制代码

至此,一个简单的jwt用户认证方法就写好了,至于jwt中的令牌存在客户端的什么位置呢?能够参考这一篇文章来寻找答案:完全弄清楚session,cookie,sessionStorage,localStorage的区别及应用场景(面试向)

原文转载自「刘悦的技术博客」 v3u.cn/a_id_98

相关文章
相关标签/搜索