You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask中如何获取仅单个用户可访问的图片安全URL?

Flask实现单个用户专属的安全图片URL方案

要实现仅单个用户可访问图片、防止URL猜测,同时返回带图片URL的JSON,核心思路是不直接暴露/uploads的静态路径,通过Flask路由做权限校验,再返回图片文件。下面给你两种实用方案:

方案一:基于登录态的权限校验(适合用户登录后访问)

这个方案依赖Flask-Login管理用户登录状态,每次访问图片时验证当前用户是否是图片的所有者。

1. 基础配置与模型

首先准备数据库模型,关联用户和图片:

from flask import Flask, jsonify, send_from_directory, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, login_required, current_user
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-strong-secret-key'  # 替换成实际密钥
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
app.config['UPLOAD_FOLDER'] = os.path.join(app.root_path, 'uploads')  # 图片存储路径
db = SQLAlchemy(app)

# 初始化Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)

# 用户模型
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    # 其他字段:密码哈希、邮箱等

# 图片模型(关联用户ID,文件名直接用图片ID.jpeg)
class Photo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    user = db.relationship('User', backref=db.backref('photos', lazy=True))

# Flask-Login加载用户的回调
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

2. 图片访问路由

创建路由处理图片请求,先校验权限再返回文件:

@app.route('/photos/<int:photo_id>')
@login_required
def serve_photo(photo_id):
    # 查询图片,不存在则返回404
    photo = Photo.query.get_or_404(photo_id)
    # 校验当前用户是否是图片所有者
    if photo.user_id != current_user.id:
        return jsonify({'error': '无权访问该图片'}), 403
    # 返回uploads文件夹里的对应图片
    return send_from_directory(app.config['UPLOAD_FOLDER'], f"{photo_id}.jpeg")

3. 返回带图片URL的JSON

查询条目时,生成安全的图片URL并返回:

# 假设你有一个Item模型关联了Photo
class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(120), nullable=False)
    photo_id = db.Column(db.Integer, db.ForeignKey('photo.id'), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

@app.route('/api/items/<int:item_id>')
@login_required
def get_item(item_id):
    item = Item.query.get_or_404(item_id)
    # 校验条目是否属于当前用户
    if item.user_id != current_user.id:
        return jsonify({'error': '无权访问该条目'}), 403
    # 生成图片的完整URL
    photo_url = url_for('serve_photo', photo_id=item.photo_id, _external=True)
    return jsonify({
        'name': item.name,
        'url': photo_url
    })

方案二:签名式临时URL(适合无需登录的场景,比如临时分享)

如果需要给用户生成可以短期访问的图片链接(不用登录),可以用itsdangerous给图片ID和用户ID签名,生成带有效期的安全URL。

1. 初始化签名工具

在配置里添加:

from itsdangerous import URLSafeSerializer, SignatureExpired

# 初始化URL签名器
signer = URLSafeSerializer(app.config['SECRET_KEY'])

2. 生成签名URL的函数

def generate_signed_photo_url(photo_id, user_id, expires_in=3600):
    # 把图片ID和用户ID打包签名,设置1小时有效期
    token = signer.dumps({'photo_id': photo_id, 'user_id': user_id})
    return url_for('serve_signed_photo', token=token, _external=True)

3. 签名图片访问路由

@app.route('/photos/signed/<token>')
def serve_signed_photo(token):
    try:
        # 解析签名,校验有效期
        data = signer.loads(token, max_age=3600)
    except SignatureExpired:
        return jsonify({'error': '链接已过期'}), 401
    except:
        return jsonify({'error': '无效的链接'}), 400
    
    photo_id = data['photo_id']
    user_id = data['user_id']
    photo = Photo.query.get_or_404(photo_id)
    
    # 校验图片是否属于指定用户
    if photo.user_id != user_id:
        return jsonify({'error': '无权访问该图片'}), 403
    
    return send_from_directory(app.config['UPLOAD_FOLDER'], f"{photo_id}.jpeg")

4. 返回签名URL的JSON

修改之前的get_item接口:

@app.route('/api/items/<int:item_id>')
@login_required
def get_item(item_id):
    item = Item.query.get_or_404(item_id)
    if item.user_id != current_user.id:
        return jsonify({'error': '无权访问该条目'}), 403
    # 生成签名式图片URL
    photo_url = generate_signed_photo_url(item.photo_id, current_user.id)
    return jsonify({
        'name': item.name,
        'url': photo_url
    })

关键注意事项

  • 禁止直接暴露uploads目录:不要把uploads设置为Flask的静态文件夹,否则用户可以直接通过/uploads/1.jpeg访问,绕过权限校验。
  • 上传图片时规范命名:创建Photo记录后,用自增的ID作为文件名保存(比如1.jpeg),确保数据库里的ID和文件名一一对应。
  • 权限校验要严格:无论是登录态还是签名URL,都必须校验用户是否是图片的所有者,防止越权访问。

内容的提问来源于stack exchange,提问作者X XXX X

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:02:42