先把错误信息贴出来
127.0.0.1 - - [29/Nov/2017 09:13:32] "GET /admin/loginlogs?page_index=1 HTTP/1.1" 500 -
Traceback (most recent call last):
File "C:\Users\jyd\py3work\lib\site-packages\flask\app.py", line 1997, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\jyd\py3work\lib\site-packages\flask\app.py", line 1985, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\jyd\py3work\lib\site-packages\flask_cors\extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "C:\Users\jyd\py3work\lib\site-packages\flask_restful\__init__.py", line 273, in error_router
return original_handler(e)
File "C:\Users\jyd\py3work\lib\site-packages\flask\app.py", line 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\jyd\py3work\lib\site-packages\flask\_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "C:\Users\jyd\py3work\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\jyd\py3work\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\jyd\py3work\lib\site-packages\flask_cors\extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "C:\Users\jyd\py3work\lib\site-packages\flask_restful\__init__.py", line 273, in error_router
return original_handler(e)
File "C:\Users\jyd\py3work\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\jyd\py3work\lib\site-packages\flask\_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "C:\Users\jyd\py3work\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\jyd\py3work\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\jyd\py3work\lib\site-packages\flask_restful\__init__.py", line 480, in wrapper
resp = resource(*args, **kwargs)
File "C:\Users\jyd\py3work\lib\site-packages\flask_httpauth.py", line 89, in decorated
if not self.authenticate(auth, password):
File "C:\Users\jyd\py3work\lib\site-packages\flask_httpauth.py", line 246, in authenticate
return self.verify_token_callback(token)
File "C:\Users\jyd\py3work\lib\site-packages\flask\views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "C:\Users\jyd\py3work\lib\site-packages\flask_restful\__init__.py", line 585, in dispatch_request
assert meth is not None, 'Unimplemented method %r' % request.method
AssertionError: Unimplemented method 'GET'
部分代码
from flask_httpauth import HTTPTokenAuth
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
auth = HTTPTokenAuth(scheme='Bearer')
serializer = Serializer(app.config["SECRET_KEY"], expires_in=1800)
def create_token(data):
"""生成token"""
token = serializer.dumps(data)
return token.decode("utf-8")
@auth.verify_token
def verify_token(token):
"""验证token"""
g.user = User.query.filter_by(token=token).first()
return g.user is not None
class LoginApi(Resource):
"""登录接口"""
def post(self):
parse = reqparse.RequestParser()
parse.add_argument('username', type=str, required=True, location=['json'])
parse.add_argument('password', type=str, required=True, location=['json'])
args = parse.parse_args()
user = User.query.filter_by(username=args.username).first()
if not user or not user.check_pwd(args.password):
res = make_response(jsonify({"code": 1, "msg": "用户或密码错误"}))
return res
else:
token = create_token({"username": user.username, "password": user.password})
user.token = token
db.session.add(user)
db.session.commit()
res = make_response(jsonify({"code": 0, "msg": "", "data": {"username": user.username,
"nickname": user.nickname,
"email": user.email,
"phone": user.phone,
"face": user.face,
"mfa_status": user.mfa_status,
"token": token}}))
return res
class BankCardApi(Resource):
"""添加及修改银行卡接口"""
decorators = [auth.verify_token]
def post(self):
verify = BankCardVerify()
parse = reqparse.RequestParser()
parse.add_argument('name', type=verify.name, required=True, location=['json'])
parse.add_argument('card', type=verify.card, required=True, location=['json'])
args = parse.parse_args()
bankcard = BankCard(name=args.name, card=args.card, user_id=int(session.get("userid")))
db.session.add(bankcard)
db.session.commit()
return jsonify({"code": 0, "msg": "添加成功"})
class LoginLogApi(Resource):
"""获取登录日志接口"""
decorators = [auth.login_required]
resource_fields = {'num_result': fields.Integer,
"objects": fields.List(fields.Nested({
'id': fields.Integer,
'user_id': fields.Integer,
'ip': fields.String,
'mfa_status': fields.Boolean,
'addtime': fields.String})),
"page": fields.Integer,
"total_page": fields.Integer
}
@marshal_with(resource_fields, envelope='loginlog_resource')
def get(self):
parse = reqparse.RequestParser()
parse.add_argument('page_index', type=int, required=True, location=['args'])
args = parse.parse_args()
login = Loginlog.query.filter_by(user_id=g.user.id).order_by(Loginlog.addtime.desc()).paginate(
page=args.page_index, per_page=10)
return {"num_result":login.total, "objects":login.items, "page":login.page, "total_page":login.pages}
restful.add_resource(BankCardApi, '/bankcard/add', endpoint='addbank')
restful.add_resource(LoginApi, '/admin/logins', endpoint='logins')
restful.add_resource(LoginLogApi, '/admin/loginlogs', endpoint='loginlogs')
主要代码已经贴上了,现在看看我怎么遇到那个问题的
我用postman先模拟登录
根据登录获取到的token,去获取该用户的登录日志,但是报错了,详细日志贴在开头
讲真遇到这个问题,不知道怎么解决,搞了好久才找到原因
分析:
用到了token认证,而且flask-restful要增加认证
#token认证
auth = HTTPTokenAuth(scheme='Bearer')
#flask-restful中增加的装饰器
decorators = [auth.login_required]
flask-httpauth部分源代码
def login_required(self, f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if auth is None and 'Authorization' in request.headers:
print("Author")
# Flask/Werkzeug do not recognize any authentication types
# other than Basic or Digest, so here we parse the header by
# hand
try:
auth_type, token = request.headers['Authorization'].split(
None, 1)
auth = Authorization(auth_type, {'token': token})
except ValueError:
# The Authorization header is either empty or has no token
pass
# if the auth type does not match, we act as if there is no auth
# this is better than failing directly, as it allows the callback
# to handle special cases, like supporting multiple auth types
if auth is not None and auth.type.lower() != self.scheme.lower():
auth = None
# Flask normally handles OPTIONS requests on its own, but in the
# case it is configured to forward those to the application, we
# need to ignore authentication headers and let the request through
# to avoid unwanted interactions with CORS.
if request.method != 'OPTIONS': # pragma: no cover
if auth and auth.username:
password = self.get_password_callback(auth.username)
else:
password = None
if not self.authenticate(auth, password):
# Clear TCP receive buffer of any pending data
request.data
return self.auth_error_callback()
return f(*args, **kwargs)
return decorated
class HTTPTokenAuth(HTTPAuth):
def __init__(self, scheme='Bearer', realm=None):
super(HTTPTokenAuth, self).__init__(scheme, realm)
self.verify_token_callback = None
def verify_token(self, f):
self.verify_token_callback = f
return f
def authenticate(self, auth, stored_password):
if auth:
token = auth['token']
else:
token = ""
if self.verify_token_callback:
return self.verify_token_callback(token)
return False
报错中有一部分内容是verify_token_callback
if not self.authenticate(auth, password):
File "C:\Users\jyd\py3work\lib\site-packages\flask_httpauth.py", line 246, in authenticate
return self.verify_token_callback(token)
http://flask-httpauth.readthedocs.io/en/latest/ 查看官方文档
再看看我这边回调的认证token函数,完全符合要求啊,难道函数没有生效
@auth.verify_token
def verify_token(token):
"""验证token"""
g.user = User.query.filter_by(token=token).first()
return g.user is not None
修改源文件进行调试
看到addbank我就觉得非常熟悉,且离找到原因不远了
restful.add_resource(BankCardApi, '/bankcard/add', endpoint='addbank') #addbank不就是在这里吗?
restful.add_resource(LoginApi, '/admin/logins', endpoint='logins')
restful.add_resource(LoginLogApi, '/admin/loginlogs', endpoint='loginlogs')
再看看这个
class BankCardApi(Resource):
"""添加及修改银行卡接口"""
decorators = [auth.verify_token] #这里写错了,不是verify_token,应该是login_required,不然token认证回调函数会被更改掉
def post(self):
verify = BankCardVerify()
parse = reqparse.RequestParser()
parse.add_argument('name', type=verify.name, required=True, location=['json'])
parse.add_argument('card', type=verify.card, required=True, location=['json'])
args = parse.parse_args()
bankcard = BankCard(name=args.name, card=args.card, user_id=int(session.get("userid")))
db.session.add(bankcard)
db.session.commit()
return jsonify({"code": 0, "msg": "添加成功"})
把代码改好后,再试试接口行不行
过期的token
生效的token
代码中用到的库文档:
http://flask-httpauth.readthedocs.io/en/latest/
http://flask-restful.readthedocs.io/en/latest/
http://pythonhosted.org/itsdangerous/