Python单独接口实现APIKEY

640 阅读4分钟

APIKEY 的设计与使用

cc6619482f68483bd738c264112c3721

cc6619482f68483bd738c264112c3721

一、APIKEY 的生成与存储

  1. 随机生成 APIKEY:常用的方法是使用随机字符串生成器或数据加密方法生成字符串,确保每个 APIKEY 都是唯一的。
  2. 存储:APIKEY 可以存储在数据库中,但为了安全起见,通常会使用加密的方式存储。

二、APIKEY 的传递

  1. 在请求头中传递:大多数情况下,APIKEY 会作为请求头的一部分发送。
  2. 请求参数:在某些情况下,APIKEY 也可以作为请求参数的一部分。但这种方法不安全,因为请求参数容易被记录或查看。

三、APIKEY 的使用限制

  1. 限制 IP 地址:可以为 APIKEY 设置允许的 IP 地址范围,增加安全性。(在模型中设置字段,访问接口做验证)
  2. 限制使用频率:可以设置 API 请求的频率限制,防止被滥用。(设置频率字段,接口验证频率次数)
  3. 有效期限制:为 APIKEY 设置有效期,过期后需要重新生成。(设置过期时间)

四、APIKEY 的作用

  1. 身份验证:APIKEY 可用于验证请求者的身份。
  2. 授权控制:基于 APIKEY 可以控制哪些用户或系统可以访问特定的 API。
  3. 跟踪和分析:APIKEY 可以用于分析请求的数据,例如流量、使用情况等。
  4. 安全保护:APIKEY 增加了非法访问的难度,减少了未经授权的访问。

五、注意事项

  1. 不要在客户端代码中硬编码 APIKEY,因为这样会导致安全风险。
  2. 定期审查和更新 APIKEY,确保其安全性和有效性。
  3. 对于不再使用的 APIKEY,应及时删除或使其失效。
  4. 使用 HTTPS 协议来传输 API 请求和数据,确保数据的安全性。

在 Django 框架中如何实现单独设置 APIKEY

新建 APIKEY 模型

class ApiKeyToken(models.Model):
    key = models.CharField(max_length=40, primary_key=True, verbose_name='key')
    platform = models.CharField(max_length=40, verbose_name='api-key运用平台', default='netops')
    is_active = models.BooleanField(default=True, verbose_name='状态')
    lastGetTime = models.DateTimeField(blank=True, auto_now=True, null=True, verbose_name='最近查询时间')
    expireTime = models.DateTimeField(blank=True, auto_now=True, null=True, verbose_name='key过期时间')
    countTimes = models.IntegerField(blank=True, null=True, verbose_name='key使用次数')
    serverIp = models.CharField(blank=True, null=True, verbose_name='key提供服务的IP或IP范围',max_length=100)

    def save(self, *args, **kwargs):
        if not self.key:
            self.key = self.generate_key()
        return super(ApiKeyToken, self).save(*args, **kwargs)

    def generate_key(self):
        return binascii.hexlify(os.urandom(20)).decode()

    def __unicode__(self):
        return self.key

    class Meta:
        # abstract = True
        db_table = 'apikey_model'  # 通过db_table自定义数据表名
        verbose_name = _('ApiKey')
        verbose_name_plural = _('ApiKey')

执行数据迁移

python manage.py makemigrations

python manage.py migrate

image

image

使用 APIKEY 模型

生成APIKEY


api_key_token = ApiKeyToken()
api_key_token.key = api_key_token.generate_key()
api_key_token.save()

使用APKEY

新建验证类 auth.py

from rest_framework.authentication import TokenAuthentication, get_authorization_header
from rest_framework.exceptions import AuthenticationFailed

from open_ipam.models import ApiKeyToken


class ApiKeyAuthentication(TokenAuthentication):

    def get_token_from_auth_header(self, auth):
        auth = auth.split()
        # print(auth)
        if not auth or auth[0].lower() != b'api-key':
            return AuthenticationFailed('Invalid api-key header. No credentials provided.')

        if len(auth) == 1:
            raise AuthenticationFailed('Invalid api-key header. No credentials provided.')
        elif len(auth) > 2:
            raise AuthenticationFailed('Invalid api-key header. Token string should not contain spaces.')

        try:
            return auth[1].decode()
        except UnicodeError:
            raise AuthenticationFailed('Invalid api-key header. Token string should not contain invalid characters.')

    def authenticate(self, request):
        auth = get_authorization_header(request)
        token = self.get_token_from_auth_header(auth)

        if not token:
            token = request.GET.get('api-key', request.POST.get('api-key'None))

        if token:
            return self.authenticate_credentials(token)

    def authenticate_credentials(self, key):
        try:
            token = ApiKeyToken.objects.get(key=key)
        except ApiKeyToken.DoesNotExist:
            raise AuthenticationFailed('Invalid Api key.')

        if not token.is_active:
            raise AuthenticationFailed('Api key inactive or deleted.')

        # user = token.company.users.first()  # what ever you want here
        return token

新建装饰器

def check_api_key(func):
    auth_check = ApiKeyAuthentication()

    def wrap(view, request):
        if request.headers['Authorization']:
            if 'api-key' in request.headers['Authorization']:
                auth_check.authenticate(request)
                return func(view, request)
            else:
                raise AuthenticationFailed('Invalid api-key header. No credentials provided.')
        raise "Invalid Api key"

    return wrap

使用装饰器

class IpamOpenAPI(APIView):
    @check_api_key
    def post(self, request):
        pass

如此用户请求当前View时候,则会先经过装饰器进行APIKEY的校验。在视图函数中,我们首先从请求头中获取APIKEY,然后查询数据库验证APIKEY的有效性。如果APIKEY无效或缺失,我们返回相应的错误响应。如果APIKEY有效,则继续处理请求。

目前实现的APIKEY校验方式仅仅是一种类型,需要注意的是,在实际应用中,应该使用更加安全的方式来存储和验证APIKEY,例如使用加密的存储方式、引入更强大的身份验证机制等。此外,还应该考虑其他的安全措施,例如限制API的访问频率、限制IP地址等