首先,在token验证模块中,我们看到的是这一函数:
def get_model(self):
if self.model is not None:
return self.model
from rest_framework.authtoken.models import Token
return Token
这里我们先不看 if 语句,直接去看 Token (如下图)
class Token(models.Model):
"""
The default authorization token model.
"""
key = models.CharField(_("Key"), max_length=40, primary_key=True)
user = models.OneToOneField(
settings.AUTH_USER_MODEL, related_name='auth_token',
on_delete=models.CASCADE, verbose_name=_("User")
)
created = models.DateTimeField(_("Created"), auto_now_add=True)
class Meta:
# Work around for a bug in Django:
# https://code.djangoproject.com/ticket/19422
#
# Also see corresponding ticket:
# https://github.com/encode/django-rest-framework/issues/705
abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS
verbose_name = _("Token")
verbose_name_plural = _("Tokens")
def save(self, *args, **kwargs):
if not self.key:
self.key = self.generate_key()
return super().save(*args, **kwargs)
@classmethod
def generate_key(cls):
return binascii.hexlify(os.urandom(20)).decode()
def __str__(self):
return self.key
可以很明显的看出,在这里,我们建立了一个一对一的 Token 模型 指向的是 User 模型,通过元类调用 generate_key 来生产模型中的 key 字段的值,最后由魔术方法 __str__ 来作为返回值。
回到我们的 get_model 函数,通过判断是否存在token表来执行操作:
- 如果已有 token 表(自己定义的或是通过 Token 模型生成的表),返回该表
- 表不存在,其返回的值就是通过 Token 模型建立的 token 表
def get_model(self):
if self.model is not None:
return self.model
from rest_framework.authtoken.models import Token
return Token
既然我们已经明白了 get_model() 的作用,那么我们就从它入手,找到 token验证模块中使用 get_model() 的函数:
def authenticate_credentials(self, key):
model = self.get_model() #这里这里 <<==
try:
token = model.objects.select_related('user').get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed(_('Invalid token.'))
if not token.user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (token.user, token)
先看函数名,作为一个被广泛使用的包,命名肯定是规范的,可读的。
好的,Google 翻译告诉了我们答案,再看它的函数体中的这行代码:
令牌 = model( 也就是 Token ).object.select_related(' user ').get( key= key )
这里等于号后面的 key 是传递进来的参数,那么我们就需要看一下是谁调用了该函数(下图)
def authenticate(self, request):
auth = get_authorization_header(request).split() #这里这里 <<==
if not auth or auth[0].lower() != self.keyword.lower().encode():
return None
if len(auth) == 1:
msg = _('Invalid token header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid token header. Token string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
"""
1.无效的令牌头。没有提供任何凭据。
2.无效的令牌头。此字符串不应包含空格。
"""
try:
token = auth[1].decode() #这里这里 <<==
"""
获取凭证体
"""
except UnicodeError:
msg = _('Invalid token header. Token string should not contain invalid characters.')
# 无效的令牌头。无效字符标记字符串不应包含无效字符。'
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(token) #这里这里 <<==
- 可以看到,传递的是 token ,
- 继续往上面看 :token = auth[1].decode()
- 继续:auth = get_authorization_header(request).split()
- 再向上:(下图)
def get_authorization_header(request):
"""
Return request's 'Authorization:' header, as a bytestring.
返回请求的“Authorization:”头,作为bytestring。
Hide some test client ickyness where the header can be unicode.
在头可以是unicode的地方隐藏一些测试客户端易读性。
"""
auth = request.META.get('HTTP_AUTHORIZATION', b'') #这里这里 <<==
if isinstance(auth, str):
# Work around django test client oddness
# HTTP_HEADER_ENCODING = 'iso-8859-1'
auth = auth.encode(HTTP_HEADER_ENCODING)
return auth
好了,这里大概就算是验证模块的首发站了,看官方给的英文注解(看不懂的看Google 翻译出来的,虽然第二句翻译的有些奇怪...)
在这一函数中,通过 request.META.get() 我们拿到了请求头中的 凭证( Authorization ) 的数据,也就是 凭证头 以及 凭证体,见下图:
在上图中,凭证头是 token ,凭证体是 b571d6300db3c0d86438efa25616179e7f005c0d
之后将 凭证数据 赋值给 auth ,接着我们回到 验证函数中:
def authenticate(self, request):
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != self.keyword.lower().encode():
return None
if len(auth) == 1:
msg = _('Invalid token header. No credentials provided.')
# 无效的令牌头。没有提供任何凭据。
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid token header. Token string should not contain spaces.')
# 无效的令牌头。此字符串不应包含空格。
raise exceptions.AuthenticationFailed(msg)
try:
token = auth[1].decode()
"""
获取凭证体
"""
except UnicodeError:
msg = _('Invalid token header. Token string should not contain invalid characters.')
# 无效的令牌头。无效字符标记字符串不应包含无效字符。'
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(token)
auth = get_authorization_header(request).split()
get_authorization_header(request) 上面已经说过了,返回一个 auth ,在通过 split 将凭证 拆分,之后就是校验部分,很好理解,就不多赘述了。
最后是将凭证体 传递给了 authenticate_credentials,再由 authenticate_credentials 返回 通过 token 认证的用户 以及 token 值
def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.select_related('user').get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed(_('Invalid token.'))
if not token.user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (token.user, token)
Token 验证流程 概述:
- 客户端通过登录模块登录时,对是否是第一次登录进行判断:是,为其在 Token 表中新建一条数据,获取 Token 表中的 token 值;否,直接获取 Token 表中的 token 值。判断语句结束后,服务器将 token 值传递到客户端并由客户端保存在某一存储空间中
- 当我们访问需要 token 验证的 API 接口时,将 token 与数据一同传输到服务器
- 服务器解析客户端传递的数据与请求,当需要 token 用于验证时,在数据表中查找对应的 token 值:如果找到了,那就开始处理请求的其他部分;没找到,那就返回一个 401 错误 ( Unauthorized ),终止操作,等待下一次请求或关闭连接。
好了,关于 TokenAuthentication 的源码部分就到这里了。
#Ps:边看源码边写博客属实掉头发,容我眯一会儿,如果觉得我写的狗屁不通也请不要怪罪嗷。