2015-12-11 6 views
19

Uso flask-restful per creare le mie API. Ho usato flask-jwt per abilitare l'autenticazione basata su JWT. Ora ho bisogno di fare l'autorizzazione.Autorizzazione API restrittiva del pallone. Accedi a current_identity all'interno del decoratore

Ho provato a mettere il mio decoratore di autorizzazioni.

test.py (/ prova api)

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' 

In pratica per gestire l'autorizzazione di base, ho bisogno di accedere current_identity e verificare che sia tipo. Quindi, in base al tipo, deciderò se l'utente è autorizzato ad accedere alle API/risorse.

Ma current_identity sembra essere empty in quel decoratore. Quindi, per acquisirlo indirettamente, ho dovuto vedere il codice jwt_handler e fare la cosa lì.

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 

Perché non posso semplicemente accedere current_identity nel mio authorized_api_user_type decoratore? Qual è il modo giusto di fare l'autorizzazione in un pallone riposante?

risposta

11

Ecco la combinazione di quickstart di entrambi Flask-JWT e 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" 
} 

Per localhost:5000/auth e ottenere il access_token in risposta.

quindi ottenere localhost:5000/ con l'intestazione

Authorization: JWT `the access_token value above` 

Si otterrebbe

{ 
    "hello": "user1" 
} 

se si tenta di accedere localhost:5000/ con il token JWT di user2, si otterrebbe 401.

I decoratori sono avvolti in questo modo:

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

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

Così la più un 'nella matrice decoratori arriva a correre in precedenza.

Per maggiori riferimento:

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

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

+0

Hai letto domanda? La domanda riguarda l'accesso a 'current_identity' nel decoratore. –

+0

'current_identity' è accessibile se spostato dal decoratore' jwt_required'. Perché hai bisogno di un decoratore personalizzato? – aGuegu

+0

Decoratore può essere utilizzato per un numero di casi. Ad esempio: per rilevare il ruolo dell'utente. Per evitare di riscrivere il codice all'interno di tutte le tue viste puoi creare un decoratore come "detect_role". Per favore, leggi l'intera domanda dall'OP. –

3

mia soluzione attuale si presenta come:

@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'])) 

Dopo che possiamo accedere identità decoratore via request.identity. E ho rimosso current_identity ovunque dal codice. È ancora un modo disordinato.

-1

Utilizzare questa:

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

Mentre rispondi ai thread è preferibile evitare alcune frasi come "prova questo", "usa questo" ecc. –

Problemi correlati