5.1 Token
1. Summary of Token
Here are the differences between a website login and an API login
Different from website login, the login information is written into the cookie and stored in the browser, while the API is only responsible for generating the token and sending it to the client, and the client can decide how to store it.
- Token has a validity period
- Token can indicate user identity, such as storage user ID
2. Obtain the Token
Password validation –models/user.py
@staticmethod
def verify(email, password):
user = User.query.filter_by(email=email).first()
if not user:
raise NotFound('user not found')
if not user.check_password(password):
raise AuthFailed()
return {'uid': user.id}
def check_password(self, raw):
if not self._password:
return False
return check_password_hash(self._password, raw)
Copy the code
Return token attempt function, break REST rules slightly, because login operation password security is higher, using GET will leak
@api.route('', methods=['POST'])
def get_token(a):
form = ClientForm(request).validate_for_api()
promise = {
ClientTypeEnum.USER_EMAIL: User.verify,
}
identity = promise[form.type.data](
form.account.data,
form.secret.data
)
expiration = current_app.config['TOKEN_EXPIRATION']
token = generator_auth_token(identity['uid'],
form.type.data,
None,
expiration=expiration)
t = {
'token': token.decode('utf-8')}return jsonify(t), 201
def generator_auth_token(uid, ac_type, scope=None,
expiration=7200):
""" Generate token """
s = Serializer(current_app.config['SECRET_KEY'],
expires_in=expiration)
return s.dumps({
'uid': uid,
'type': ac_type.value
})
Copy the code
3. The use of Token
It is impossible for every user to access our interface for retrieving user data. This must be controlled, meaning that only identified users can access our interface.
How do you protect this interface?
When the user accesses the interface, we need to obtain the token sent by the user and parse and verify. Only when the token is valid and has not expired, we allow the access.
Because every need authentication token trying to function needs the business logic of the above, so we can write a decorator, unified handling, in the form of aspect oriented. Write a function authentication token, if the verification through, we will continue to function method, if not through, we will return a custom exception.
libs/token_auth.py
from flask_httpauth import HTTPBasicAuth
__author__ = "gaowenfeng"
auth = HTTPBasicAuth()
@auth.verify_password
def verify_password(account, password):
return False
Copy the code
@api.route('/get')
@auth.login_required
def get_user(a):
return 'i am gwf'
Copy the code
5.2 HTTPBasicAuth
1. Basic principles of HTTPBasicAuth
In addition to sending custom accounts and passwords, the HTTP protocol itself has various specifications that allow us to send accounts and passwords. One of them is HTTPBasic
HTTPBasic: set a fixed key/value pair in the header of the HTTP request key=Authorization,value= BASIC Base64 (Account: PSD)
2. Send the token in BasicAuth mode
We can use the token as the account mentioned above, and the password PSD passes a null value
5.3 Token Sending and Authentication
1. The authentication token
auth = HTTPBasicAuth()
User = namedtuple('User'['uid'.'ac_type'.'scope'])
@auth.verify_password
def verify_password(token, password):
user_info = verify_auth_token(token)
if not user_info:
return False
else:
g.user = user_info
return True
def verify_auth_token(token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
# token An exception that was thrown illegally
except BadSignature:
raise AuthFailed(msg='token is valid', error_code=1002)
# Token expired exception thrown
except SignatureExpired:
raise AuthFailed(msg='token is expired', error_code=1003)
uid = data['uid']
ac_type = data['type']
return User(uid, ac_type, ' ')
Copy the code
2. Compilation of view functions
@api.route('/<int:uid>', methods=['GET'])
@auth.login_required
def get_user(uid):
user = User.query.get_or_404(uid)
r = {
'nickname': user.nickname,
'email': user.email
}
return jsonify(r), 200
Copy the code
3. Rewrite get_OR_404 to throw custom exceptions
def get_or_404(self, ident):
rv = self.get(ident)
if not rv:
raise NotFound()
return rv
def first_or_404(self):
rv = self.first()
if not rv:
raise NotFound()
return rv
Copy the code
4. Obtain token information
@api.route('/secret', methods=['POST'])
def get_token_info(a):
""" Get token information """
form = TokenForm().validate_for_api()
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(form.token.data, return_header=True)
except SignatureExpired:
raise AuthFailed(msg='token is expired', error_code=1003)
except BadSignature:
raise AuthFailed(msg='token is invalid', error_code=1002)
r = {
'scope': data[0] ['scope'].'create_at': data[1] ['iat'].'expire_in': data[1] ['exp'].'uid': data[0] ['uid']}return jsonify(r)
Copy the code