如何通过 OAuth2.0 的 access_token 限制接口访问权限?

有个关于 oauth2.0 的问题,我有一个 A公司的 app,里面嵌套一个 B公司 h5 页面,其中 h5 页面要用到A公司 app的用户信息,经过 oauth2.0 拿到App 的 access_token 后,理论上他能调用 A公司app 的全部接口(对于A公司后端来说就是一个普通的用户的token),这个可以预防吗,要怎么限制做到颁发给 A公司的 access_token 只能访问特定的接口,而不能访问系统的其他接口呢

有考虑过 scope,但是 scope 我感觉上只是用户是否赋予 h5 页面 App 的可供调用接口里面的接口是否都授权。打个比喻 APP 提供给第三方公司调用的就三个接口,【获取手机号,获取用户真实姓名,获取用户身份证】其他接口都不可以调用,其中这三个接口 h5 都不是随便可以调用,而是要经过用户进行确认,确认后才可以调用,是做这个功能。但是关于 APP 如何控制第三方公司其他接口都不可以调用,只能调用【获取手机号,获取用户真实姓名,获取用户身份证】接口,是不是要另外做逻辑进行隔离。即控制h5能调用多少个 APP 接口是一回事,而用户是否授权其中某几个接口给 h5 调用,又是另外一回事了。我的理解是否正确呢

阅读 2.1k
avatarAI BotBETA

您对于 OAuth2.0 和 scope 的理解是正确的。在 OAuth2.0 中,scope 是用来定义客户端应用可以访问的资源或权限范围的。当用户授权一个应用时,他们会同意应用访问某些特定范围的数据或接口。

为了确保 A 公司的 access_token 只能访问特定的接口,而不是整个系统的接口,你应该采取以下措施:

  1. 定义精细化的 Scope

    • 为每一个接口或接口集合定义一个唯一的 scope。例如,phone_numberreal_nameid_card
    • 在授权过程中,只授予 B 公司 h5 页面需要的 scope。
  2. 接口级别的权限检查

    • 在 A 公司的后端接口中,检查每个请求中的 access_token 对应的 scope 是否包含该接口所需的 scope。
    • 如果不包含,则拒绝访问并返回相应的错误消息。
  3. 动态权限管理

    • 除了静态的 scope 定义,你还可以实现动态的权限管理系统,允许管理员为特定的客户端或用户设置更细粒度的权限。
  4. 使用中间件或拦截器

    • 在你的后端框架中,使用中间件或拦截器来自动执行权限检查,而不是在每个接口中手动检查。
  5. 文档和沟通

    • 确保与 B 公司明确沟通哪些接口是开放的,哪些不是,以及每个接口需要的 scope。
  6. 审计和监控

    • 实施审计和监控机制,定期检查哪些接口被哪些客户端访问,以及是否有未经授权的访问尝试。
  7. 考虑使用 API Gateway

    • API Gateway 可以作为一个中央入口点,用于管理所有 API 请求的权限和流量。它可以在请求到达后端服务之前进行权限检查和其他逻辑处理。
  8. 刷新 Token 和撤销机制

    • 如果需要,实现 token 刷新和撤销机制,以便在必要时撤销对某个客户端的访问权限。

通过这些措施,你可以确保即使 B 公司的 h5 页面获得了 A 公司的 access_token,它也只能访问被明确授权的接口,而不能访问其他未授权的接口。这有助于保护你的 API 和用户数据的安全。

3 个回答

可以用scope 来做的,请求的时候scope 不同,如果用户一点同意了这些scope 的请求。那么就会发放不同权限的access_token。这时候,client 使用这个用户的access_token 就能请求到资源服务器的各种接口,资源服务器依据access_token 中的不容权限选择放行与否。

OAuth 本身就是个身份校验协议,如果你希望限制访问权限,就得自行实现。

没记错的话 scope 只是其中一个字段,用来方便携带一些信息,真正要实现功能还得你自己来。

发 Token:

import jwt
import datetime

def generate_token(user_id, scopes):
    payload = {
        'user_id': user_id,
        'scopes': scopes,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)  # token 过期时间
    }
    token = jwt.encode(payload, 'your_secret_key', algorithm='HS256')
    return token

验证 Token 和权限:

from flask import Flask, request, jsonify
import jwt

app = Flask(__name__)

def verify_token(token, required_scope):
    try:
        payload = jwt.decode(token, 'your_secret_key', algorithms=['HS256'])
        if required_scope in payload['scopes']:
            return True
        else:
            return False
    except jwt.ExpiredSignatureError:
        return False
    except jwt.InvalidTokenError:
        return False

@app.route('/api/phone_number', methods=['GET'])
def get_phone_number():
    token = request.headers.get('Authorization').split()[1]
    if verify_token(token, 'read_phone_number'):
        return jsonify({"phone_number": "123-456-7890"})
    else:
        return jsonify({"error": "Unauthorized"}), 403

@app.route('/api/real_name', methods=['GET'])
def get_real_name():
    token = request.headers.get('Authorization').split()[1]
    if verify_token(token, 'read_real_name'):
        return jsonify({"real_name": "John Doe"})
    else:
        return jsonify({"error": "Unauthorized"}), 403

@app.route('/api/id_number', methods=['GET'])
def get_id_number():
    token = request.headers.get('Authorization').split()[1]
    if verify_token(token, 'read_id_number'):
        return jsonify({"id_number": "987654321"})
    else:
        return jsonify({"error": "Unauthorized"}), 403

if __name__ == '__main__':
    app.run(debug=True)
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏