在传统的用户登录认证中,都是采用session方式。用户登录成功,服务端会保证一个session,当然会给客户端一个sessionId,客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId。cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务会面临的session共享问题,随着用户量的增多,开销就会越大
用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
jwt的认证方式只需要服务端生成token,客户端保存这个token,每次请求携带这个token,服务端认证解析就可。
jwt有三部分构成: header, payload, signature
jwt头部中承载了两个信息, 完整的头部类似与:
{
'typ': 'JWT',
'alg': 'HS256'
}
typ: 类型 alg: 加密算法, 通常为HMAC SHA256
载荷就是存放有效信息的地方, 这些有效信息, 包含三个部分: - 标准中注册的声明 - 公共的声明 - 私有的声明
公共的声明可以任意添加信息, 但是不可以包括敏跟信息, 因为可以被解密. 私有的声明:
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
payload的样式形如:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
签名由base64加密后的header和加密后的payload以'.'链接, 然后顺纯色header中声明的加密方式进行加盐secret
后加密.
客户端根据接口文档, 一般讲jwt加到请求头内, 比如Authorization
中或者自定义字段x-access-token
等方式.后台程序取出对应部分的信息进行验证即可.
if check_password_hash(user.password, auth.password):
payload = {
'public_id': user.public_id,
'exp': datetime.now() + timedelta(minutes=10)
}
token = jwt.encode(payload, app.config['SECRET_KEY'])
return jsonify({'token': token.decode('utf8')})
return make_response(
'Could not verify', 401,
{'WWW-Authencate': 'Basic realm = "Login Required!"'})
客户端请求时将jwt放置在请求头的x-access-token
字段中:
def token_required(func): # decoration
@wraps(func)
def decorated(*args, **kwargs):
token = None
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
if not token:
return jsonify({'message': 'Token is missing!'}), 401
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
corrent_user = User.query.filter_by(public_id=data['public_id']).first()
except:
return jsonify({'message': 'Token is invalid!'})
return func(corrent_user, *args, **kwargs)
return decorated