amazon s3和django - 仅允许来自我网站的用户而不是匿名用户

时间:2015-02-06 11:37:47

标签: django amazon-web-services amazon-s3 boto

我使用amazon s3存储上传的用户图片。我的问题是:

  • 如果我允许或授权我,我无法上传或下载内容。
  • 如果我允许或授予所有人,所有用户和(特别是)匿名用户将能够看到我不想要的内容。

所以,我的问题是,我该怎么办才能让我网站的用户上传,下载和删除内容?

因为我有条件:

  1. 仅关注用户的用户(user1,user2,user3,...) (user0)可以下载/查看内容吗?
  2. 只有上传视图的用户才能删除该内容。
  3. models.py:

    def get_upload_file_name(instance, filename):
        return "uploaded_files/%s_%s" %(str(time()).replace('.','_'), filename)
    
    PRIVACY = (
        ('H','Hide'),
        ('F','Followers'),
        ('A','All'),
    )
    
    class Status(models.Model):
        body = models.TextField(max_length=200)
        image = models.ImageField(blank=True, null=True, upload_to=get_upload_file_name)
        privacy = models.CharField(max_length=1,choices=PRIVACY, default='F')
        pub_date = models.DateTimeField(auto_now_add=True, auto_now=False)
        user = models.ForeignKey(User)
    

    settings.py:

    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
    
    AWS_ACCESS_KEY_ID = 'FAKEAMAZONKEY'
    
    AWS_SECRET_ACCESS_KEY = 'FAKEAMAZONSECRETKEY'
    
    AWS_STORAGE_BUCKET_NAME = 'fakebucketname'
    

    更新

    用户之间关系模型

    class Person(models.Model):
        user = models.OneToOneField(User)
        relationships = models.ManyToManyField('self', through='Relationship', 
                                               symmetrical=False, 
                                               related_name='related_to')
    
        def __unicode__(self):
            return self.user.username
    
        def add_relationship(self, person, status):
            relationship, created = Relationship.objects.get_or_create(
                from_person=self,
                to_person=person,
                status=status)
            return relationship
    
        def remove_relationship(self, person, status):
            Relationship.objects.filter(
                from_person=self, 
                to_person=person,
                status=status).delete()
            return
    
        def get_relationships(self, status):
            return self.relationships.filter(
                to_people__status=status, 
                to_people__from_person=self)
    
        def get_related_to(self, status):
            return self.related_to.filter(
                from_people__status=status, 
                from_people__to_person=self)
    
        def get_following(self):
            return self.get_relationships(RELATIONSHIP_FOLLOWING)
    
        def get_followers(self):
            return self.get_related_to(RELATIONSHIP_FOLLOWING)
    
        def get_friends(self):
            return self.relationships.filter(
                to_people__status=RELATIONSHIP_FOLLOWING, 
                to_people__from_person=self,
                from_people__status=RELATIONSHIP_FOLLOWING, 
                from_people__to_person=self)
    
    
    RELATIONSHIP_FOLLOWING = 1
    RELATIONSHIP_BLOCKED = 2
    RELATIONSHIP_STATUSES = (
        (RELATIONSHIP_FOLLOWING, 'Following'),
        (RELATIONSHIP_BLOCKED, 'Blocked'),
    )
    
    class Relationship(models.Model):
        from_person = models.ForeignKey(Person, related_name='from_people')
        to_person = models.ForeignKey(Person, related_name='to_people')
        status = models.IntegerField(choices=RELATIONSHIP_STATUSES)
    
        def __unicode__(self):
            return "%s %s %s" % (self.from_person, self.get_status_display(), self.to_person)
    
    
    class Activity(models.Model):
        actor = models.ForeignKey(User)
        action = models.CharField(max_length=100)
        content_type = models.ForeignKey(ContentType, related_name="content_type")
        object_id = models.PositiveIntegerField()
        content_object = generic.GenericForeignKey('content_type', 'object_id')
        element_type = models.ForeignKey(ContentType, related_name="element_type", blank=True, null=True)
        element_id = models.PositiveIntegerField(blank=True, null=True)
        element_object = generic.GenericForeignKey('element_type', 'element_id')
        pub_date = models.DateTimeField(auto_now_add=True, auto_now=False)
    
        class Meta:
            verbose_name = 'Activity'
            verbose_name_plural = 'Activities'
            ordering = ['-pub_date']
    
        def __unicode__(self):
            return ("%s %s") % (self.actor.username, self.action)
    
        def get_rendered_html(self, user=None):
            if self.element_type:
                template_name = '%s_activity.html' %(self.element_type.name)
            else:
                template_name = '%s_activity.html' %(self.content_type.name)
    
            return render_to_string(template_name, {
                'object':self.content_object,
                'actor':self.actor,
                'action':self.action,
                'element_object':self.element_object,
                'user':user,
                'pub_date':self.pub_date
                })
    

5 个答案:

答案 0 :(得分:5)

直接加载文件时,您可以使用Amazon's Query String Authentication,其中必须在URL中包含签名,以验证您是否可以获取文件。 您的应用可以使用boto' Key.generate_url method来创建此类网址。您还应该添加一个到期时间,之后链接将不再有效。

编辑:关于如何使用boto ,,,

进行更详细的description

答案 1 :(得分:2)

正如乔治所说,你需要采取两个步骤来完成这项工作,虽然不是授予对ec2实例的访问权限(从未使用过该方法),我建议只授予你特定密钥的权限(广泛使用这种方法)< / p>

  1. 允许对您希望的用户的存储区资源进行完全控制。
  2. 以下是关于权限如何工作的亚马逊。它们并不简单,因此您需要了解主要帐户是谁,以及您是否使用其密钥或IAM用户的密钥。无论如何,你的密钥应该可以完全访问资源(希望它是你的桶,而不是其他人,因为事情稍微复杂一些) http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html

    1. 实施业务逻辑:
      • 允许您的应用用户创建S3文件
      • 允许关注用户X的用户查看/下载用户X创建的文件
      • 仅允许用户X删除他创建的文件
    2. 用于创建资源:    - 如果您提供的后端有效,并且您已经很好地配置了S3权限,那么您不应该做任何事情。但是,要检查您的密钥不是问题,请执行以下操作:

      from boto.s3.connection import S3Connection as s3c
      connection = s3c('your-aws-access-key', 'your-aws-secret-key')
      bucket = connection.get_bucket('your-bucket-name')
      
      new_key = bucket.new_key('your-key-name')  #meaning the name of the file
      # https://github.com/boto/boto/blob/develop/boto/s3/bucket.py#L611
      
      new_key.send_file(file_object)  #this will upload the file
      # https://github.com/boto/boto/blob/develop/boto/s3/key.py#L709
      

      如果一切正常,您应该能够在S3浏览器中看到您的文件 - 如果没有,您将不得不返回有关S3访问权限的文档。

      仅允许关注用户X的用户访问用户上传的文件: 您的models.py文件并未说明您是如何实施follow逻辑的。我无法告诉谁是谁。您有自己的自定义User型号吗?你使用Django,但有扩展吗?你有自己的模型链接用户吗?分享有关用户如何遵循&#39;的更多信息。另一个,然后我甚至可以共享代码片段。

      允许用户X删除自己的文件: 我猜测Status.user字段已经包含对创建图像的用户的引用。如果是这样:将其放在您的视图中:

      def picture_deletion_view(request, status_identifier ...):
          try:
              status = Status.objects.filter(id_or_some_identifier=status_identifier)
          except Status.DoesNotExist:
              return SomeHttpResponse()
      
          if request.user.id == status.user.id:
              # you can delete the picture and redirect... to somewhere
          else:
              # you can't delete! redirect...or something
      

答案 2 :(得分:1)

这是由视图和模板处理的逻辑。 例如,在模板中,您可以使用带有uploadform的此{% if user.is_authenticated %}块,并且在视图中您还可以检查用户是否经过身份验证,然后才将您的内容加载到s3

答案 3 :(得分:1)

所以我不能和aws.s3说话,但对于django来说,限制谁有权访问视图的最佳方法是要求通过django的login_required装饰者登录:

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...

或者,如果你想公开所有的视图,而不是公开页面的部分(链接等),你可以把这些信息放到模板中,就像@t_io所说的那样,但django docs强烈建议不要放置模板中的处理逻辑很多,因为它会减慢网站的速度。将这些信息放在视图中会更好:

@login_required
def image_view(request):
    user = request.user

    # this list has the user's own images
    mine = []
    for status in user.status_set.all():
        mine.append(status.image)

    # this list has the images the user can see (relationship-based)
    following = []
    friends = []

    # you can get the person from the user
    person = user.person

    for status in person.get_friends().all():
        friends.append(status.image)

    for status in person.get_following().all():
        following.append(status.image)

    ctx = dict(user=request.user, friends=friends, following=following, mine=mine)
    return render("template.html", ctx)

并在模板中,您可以遍历列表

{% for img in mine %}
    <li><a href=...></a></li>
{% endfor %}

{% for img in following %}
    <li><a href=...></a></li>
{% endfor %}

......你明白了

为了防止人们直接导航到媒体网址,你可以使用sendfile(python库)或apache / nginx等价物。所以你需要另一个观点:

import sendfile
from django.conf import settings
@login_required
def handle_media(request, path=None):
    try:
        requested_file = os.path.join(settings.MEDIA_ROOT, path)
        return sendfile.sendfile(request, requested_file)
    except:
        pass

    return HttpResponseNotFound('<h1>Page not found {}</h1>'.format(path))

然后你需要一个额外的网址(urls.py):

urlpatterns = patterns('',
    url(r'/media/(?P<path>[a-zA-Z0-9_- /]+)$', views.handle_media),
)

请参阅django-sendfile以了解sendfile

答案 4 :(得分:0)

由于boto3退出,我们可以使用: https://github.com/boto/boto3/blob/develop/docs/source/guide/s3-presigned-urls.rst

ELISP> (replace-regexp "war" "negotiation" "trade war")
nil
ELISP> 

我们也可以使用类似的方法在同一引用链接中上传对象。