2015-12-11 3 views
19

Я использую flask-restful для создания своих API. Я использовал flask-jwt для включения аутентификации на основе JWT. Теперь мне нужно сделать авторизацию.Автоклавирование API с флаконами. Access current_identity внутри декоратора

Я пробовал поставить свой авторизованный декоратор.

test.py (/ испытание апи)

from flask_restful import Resource 
from flask_jwt import jwt_required 

from authorization_helper import authorized_api_user_type 


class Test(Resource): 

    decorators = [jwt_required(), authorized_api_user_type()] 

    def get(self): 
     return 'GET OK' 

    def post(self): 
     return 'POST OK' 

В основном для обработки базового разрешения, мне нужно, чтобы получить доступ current_identity и проверить его тип. Затем, основываясь на его типе, я собираюсь решить, имеет ли пользователь право доступа к api/resources.

Но current_identity похоже empty в этом декораторе. Поэтому, чтобы получить это косвенно, я должен был увидеть код jwt_handler и сделать то, что там сделано.

authorization_helper.py

from functools import wraps 
from flask_jwt import _jwt, JWTError 
import jwt 
from models import Teacher, Student 

def authorized_api_user_type(realm=None, user_type='teacher'): 
    def wrapper(fn): 
     @wraps(fn) 
     def decorator(*args, **kwargs): 
      token = _jwt.request_callback() 

      if token is None: 
       raise JWTError('Authorization Required', 'Request does not contain an access token', 
           headers={'WWW-Authenticate': 'JWT realm="%s"' % realm}) 

      try: 
       payload = _jwt.jwt_decode_callback(token) 
      except jwt.InvalidTokenError as e: 
       raise JWTError('Invalid token', str(e)) 

      identity = _jwt.identity_callback(payload) 
      if user_type == 'student' and isinstance(identity, Student): 
       return fn(*args, **kwargs) 
      elif user_type == 'teacher' and isinstance(identity, Teacher): 
       return fn(*args, **kwargs) 
      # NOTE - By default JWTError throws 401. We needed 404. Hence status_code=404 
      raise JWTError('Unauthorized', 
          'You are unauthorized to request the api or access the resource', 
          status_code=404) 
     return decorator 
    return wrapper 

Почему я не могу просто получить доступ к current_identity в моем authorized_api_user_type декоратора? Каков ПРАВЫЙ способ сделать авторизацию в колбе-успокоительной?

ответ

11

Описание Вот комбинация быстрых стартов как Flask-JWT, так и Flask-Restful.

from flask import Flask 
from flask_restful import Resource, Api, abort 
from functools import wraps 

app = Flask(__name__) 
api = Api(app) 

from flask_jwt import JWT, jwt_required, current_identity 
from werkzeug.security import safe_str_cmp 

class User(object): 
    def __init__(self, id, username, password): 
     self.id = id 
     self.username = username 
     self.password = password 

    def __str__(self): 
     return "User(id='%s')" % self.id 

users = [ 
    User(1, 'user1', 'abcxyz'), 
    User(2, 'user2', 'abcxyz'), 
] 

username_table = {u.username: u for u in users} 
userid_table = {u.id: u for u in users} 

def authenticate(username, password): 
    user = username_table.get(username, None) 
    if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')): 
     return user 

def identity(payload): 
    user_id = payload['identity'] 
    return userid_table.get(user_id, None) 

app.config['SECRET_KEY'] = 'super-secret' 

jwt = JWT(app, authenticate, identity) 


def checkuser(func): 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
     if current_identity.username == 'user1': 
      return func(*args, **kwargs) 
     return abort(401) 
    return wrapper 

class HelloWorld(Resource): 
    decorators = [checkuser, jwt_required()] 
    def get(self): 
     return {'hello': current_identity.username} 

api.add_resource(HelloWorld, '/') 

if __name__ == '__main__': 
    app.run(debug=True) 

POST

{ 
    "username": "user1", 
    "password": "abcxyz" 
} 

Чтобы localhost:5000/auth и получить access_token в ответ.

Затем GET localhost:5000/ с заголовком

Authorization: JWT `the access_token value above` 

Вы бы

{ 
    "hello": "user1" 
} 

, если вы пытаетесь получить доступ к localhost:5000/ с жетоном JWT из user2, вы получите 401.

декораторов заворачивают таким образом:

for decorator in self.decorators: 
    resource_func = decorator(resource_func) 

https://github.com/flask-restful/flask-restful/blob/master/flask_restful/init.py#L445

Таким образом, спустя один в массиве Декораторы получает запустить ранее.

Для получения дополнительной справки:

https://github.com/rchampa/timetable/blob/master/restful/users.py

https://github.com/mattupstate/flask-jwt/issues/37

+0

Читали вопрос? Вопрос о доступе к 'current_identity' в декораторе. –

+0

'current_identity' доступен, если он завернут декором' jwt_required'. Зачем вам нужен индивидуальный декоратор? – aGuegu

+0

Декоратор может использоваться для нескольких случаев. Например - для определения роли пользователя. Чтобы избежать перезаписи кода во всех ваших представлениях, вы можете создать декоратор, например, «detect_role». Пожалуйста, прочитайте весь вопрос от OP. –

3

Мое текущее решение выглядит следующим образом:

@app.before_request 
def detect_something(): 
    header = request.headers.get('Authorization') 
    if header: 
     _, token = header.split() 
     request.identity = identity(jwt.decode(token, 
               app.config['SECRET_KEY'])) 

После этого мы можем получить доступ к идентичности в декоратора через request.identity. И я удалил current_identity всюду из кода. Это все еще грязный путь.

-1

Используйте это:

from flask_jwt import current_identity 
@jwt_required() 
def get(self): 
    return {'current_identity': current_identity.json()} 
+0

При ответе на темы было бы предпочтительнее избегать определенных фраз, таких как «попробуйте это», «используйте это» и т. Д. –