面试题答案
一键面试1. 设计Token Introspection端点
-
请求格式
- HTTP方法:通常使用POST方法,以保证请求数据的安全性和隐私性。
- 请求头:
Authorization
头:携带认证信息,例如Bearer {access_token}
,用于验证发起请求的客户端身份。Content - Type
头:设置为application/x-www-form-urlencoded
,因为请求体将以URL编码格式发送数据。
- 请求体:
token
:必填字段,要检查的OAuth 2.0访问令牌或刷新令牌。- 可选字段如
token_type_hint
,可提示令牌类型(如access_token
或refresh_token
),有助于服务端更快地处理请求。
-
响应格式
- HTTP状态码:
200 OK
:表示请求成功,令牌有效。响应体将包含令牌的详细信息。400 Bad Request
:请求格式不正确,例如缺少必要参数。401 Unauthorized
:客户端未通过身份验证,例如Authorization
头缺失或无效。403 Forbidden
:客户端没有权限检查该令牌。410 Gone
:令牌已过期。500 Internal Server Error
:服务端内部错误。
- 响应体:
- 成功响应(状态码200):
active
:布尔值,指示令牌是否有效。scope
:字符串,令牌的权限范围,以空格分隔。client_id
:字符串,令牌颁发给的客户端ID。username
:字符串,令牌关联的用户名(如果适用)。exp
:整数,令牌的过期时间,以Unix时间戳表示。iat
:整数,令牌的颁发时间,以Unix时间戳表示。sub
:字符串,令牌主体(通常是用户标识符)。
- 错误响应:
- 除上述状态码对应的标准错误描述外,可在响应体中包含
error
字段,描述具体的错误类型,如invalid_request
、invalid_token
等;error_description
字段,提供更详细的错误说明。
- 除上述状态码对应的标准错误描述外,可在响应体中包含
- 成功响应(状态码200):
- HTTP状态码:
2. 安全措施
- 客户端认证:
- 使用
Authorization
头中的Bearer
令牌验证发起请求的客户端。只有经过授权的客户端才能查询令牌状态。可以通过验证客户端在OAuth 2.0注册过程中获取的密钥来实现。
- 使用
- 数据加密:
- 对请求和响应数据进行加密传输,例如使用TLS协议,确保令牌和相关信息在网络传输过程中不被窃取或篡改。
- 令牌验证:
- 严格验证请求中的令牌格式和签名(如果令牌是JWT格式,需验证签名)。检查令牌是否过期,是否在颁发的有效期内。
- 维护已撤销令牌的列表(如果支持令牌撤销功能),验证时检查令牌是否在撤销列表中。
- 访问控制:
- 确保只有具有适当权限的客户端才能查询特定令牌。例如,只有令牌的颁发者或具有管理权限的客户端可以查询所有令牌,普通客户端只能查询自己颁发的令牌。
- 日志记录与监控:
- 记录所有令牌查询请求和响应,包括客户端IP、请求时间、令牌信息等。通过监控日志,可以及时发现异常活动,如频繁的无效令牌查询或未经授权的访问尝试。
3. 实现示例(以Python Flask和SQLAlchemy为例,简化示例)
from flask import Flask, request, jsonify
from sqlalchemy import create_engine, Column, String, Integer, DateTime
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import jwt
import os
app = Flask(__name__)
engine = create_engine('sqlite:///oauth.db')
Session = sessionmaker(bind = engine)
class Token(db.Model):
__tablename__ = 'tokens'
id = Column(Integer, primary_key = True)
token = Column(String)
client_id = Column(String)
username = Column(String)
scope = Column(String)
exp = Column(DateTime)
iat = Column(DateTime)
sub = Column(String)
@app.route('/introspect', methods=['POST'])
def introspect():
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return jsonify({'error': 'invalid_request', 'error_description': 'Missing or invalid Authorization header'}), 401
client_token = auth_header.split(' ')[1]
# 验证客户端令牌逻辑,此处省略
data = request.form
token_to_check = data.get('token')
if not token_to_check:
return jsonify({'error': 'invalid_request', 'error_description': 'Missing token parameter'}), 400
session = Session()
try:
stored_token = session.query(Token).filter_by(token = token_to_check).first()
if not stored_token:
return jsonify({'active': False}), 200
if stored_token.exp < datetime.utcnow():
return jsonify({'active': False}), 200
response = {
'active': True,
'scope': stored_token.scope,
'client_id': stored_token.client_id,
'username': stored_token.username,
'exp': int(stored_token.exp.timestamp()),
'iat': int(stored_token.iat.timestamp()),
'sub': stored_token.sub
}
return jsonify(response), 200
except Exception as e:
session.rollback()
return jsonify({'error': 'internal_server_error', 'error_description': str(e)}), 500
finally:
session.close()
if __name__ == '__main__':
app.run(ssl_context='adhoc')
以上代码是一个简化的示例,实际应用中还需完善更多安全和业务逻辑,如JWT签名验证、更好的客户端认证等。