JWT认证原理及使用

1、JWT原理:前端

  参考文章:https://www.jianshu.com/p/180a870a308aweb

  一、传统的登陆方式:算法

    浏览器输入用户名密码,服务端校验经过,根据用户信息生成一个token,将token和user_id存到数据库或者session中,并将token返回给前端,存入cookie,后面浏览器每次请求都会带上cookie,服务端根据cookie查询用户,验证用户有效性。数据库

    弊端:django

      1)若是出现XSS(跨站脚本攻击)漏洞,因为cookie能够被js读取,XSS漏洞会致使用户token被泄露。后端

       解决方案:浏览器

        a)设置httpOnly,这样的话cookie将不会被js读取,浏览器会自动将它加到请求头信息中,可是带来了新的问题,很容易被XSRF(跨站请求伪造)攻击,由于只要当前浏览器开着,另外一个界面能够很容易的跨站请求这个界面的内容,由于cookie会被默认发送出去。服务器

        b)设置secure,这样cookie就只能经过https传输,能够过滤掉一些使用http协议请求的XSS注入。cookie

      2)将验证信息存到数据库中,每次验证的时候,都须要去数据库中查询,增长了数据库的查询和存储开销。session

      3)若是将token存到session中,也会增长服务器的存储压力。

  二、为何使用 JWT

    1)能够经过URL POST参数或者http header中发送,数据量小,传输速度快。

    2)自包含:负载中包含了用户所须要的全部信息,避免屡次查询数据库。

    3)JWT组成:

      header+payload+signature 三者经过点【.】链接起来就是JWT了。

      

 

      a)header头部:包含token类型和采用的加密算法

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

      b)payload负载:存放信息的地方,能够把用户ID等信息存放在payload中。

        经常使用的信息有:iss(签发者), exp(过时时间), sub(面向用户), aud(接收方), iat(签发时间)等。

      c)signature签名:使用编码后(base64编码)的header和payload再加上咱们提供的一个公钥,而后使用header中指定的签名算法进行签名。做用是保证JWT没有被窜改过。

      JWT只适合向web端传递一些非敏感信息,由于base64编码是可逆的,很容易被破解。

      JWT一般用来设计用户认证和受权系统,还有咱们一般说的单点登陆等。

 

2、JWT的使用过程

        

  1)前端经过表单将用户名和密码发送到后端接口。

  2)后端核对用户名和密码后,将用户的id及其余非敏感信息做为JWT Payload,将其与头部分别进行base64编码后签名,生成JWT

    具体源代码见rest_framework_jwt下面的serializer中,有兴趣研究的能够去跟踪看看

    

 

  3)后端将JWT字符串做为登陆成功的结果返回给前端,前端能够将JWT存到localStorage或者sessionStorage中,退出登陆时,前端删除保存的JWT信息便可。

  4)前端每次在请求时,将JWT放到header中的Authorization

  5)后端验证JWT的有效性

  6)验证经过后,进行其余逻辑操做。

 

3、JWT使用方法:

  一、安装

    pip install djangorestframework-jwt

  二、将JSONWebTokenAuthentication加到settings.py中的DEFAULT_AUTHENTICATION_CLASSES中,它能够将用户post过来的数据进行验证,并将user取出来,它和MIDDLEWARE中的AuthenticationMiddleware的原理是同样的。

    

  三、配置JWT的认证接口的url

    from djangorestframework-jwt.views import obtain_jwt_token

    url(r"^login$", obtain_jwt_token)

  四、前端访问 login,传入用户名和密码,服务器会给浏览器返回一段token(jwt)

    {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1MzEzNzU4NjEsImVtYWlsIjoiMzcwMTkzNTkzQHFxLmNvbSIsInVzZXJuYW1lIjoiYWRtaW4ifQ.BIl1b68VV-LvZOu-wrUeMxUFI3IREapgqfbTtzB6Ftw"}

  五、在后面的请求中,将JWT token信息 加入到header中的authorization中。

  六、扩展:

    DRF默认是经过邮箱做为用户名来登陆的,因此若是咱们是经过手机或者其余方式登陆的话,须要重写DRF的认证方式。

    from django.db.models import Q

    from django.contrib.auth.backends import ModelBackend

    from django.contrib.auth import get_user_model

    User = get_user_model()

    class CustomBackend(ModelBackend):

      def authenticate(self, request, username=None, password=None, **kwargs):

        try:

          user = User.objects.get(Q(username=username) | Q(mobil = username))

          if user.check_password(password):

            return user

          else:

            return None 

        except Exception as e:

          return None

   七、将重写的CustomBackend加入到settings中

    

  八、JWT的额外设置: