2018-8-16JWTtoken用户登录认证思路分析9502751
2018-8-16JWTtoken用户登录认证思路分析9502751
JWT token在商城中的实现
class UserView(CreateAPIView):
serializer_class = CreateUserSerializer
- 创建用户对象的视图函数,调用序列化器中的create方法来实现用户的保存
class CreateUserSerializer(serializers.ModelSerializer):
"""
创建用户序列化器具
"""
password2 = serializers.CharField(label="确认密码", required=True, allow_null=False, allow_blank=False, write_only=True)
sms_code = serializers.CharField(label="短信验证码", required=True, allow_null=False, allow_blank=False, write_only=True)
allow = serializers.CharField(label="同意协议", required=True, allow_null=False, allow_blank=False, write_only=True)
token = serializers.CharField(label='登录状态token', read_only=True) # 增加token字段
def validate_mobile(self, value):
if not re.match(r"^1[3456789]\d{9}$", value):
# if not re.match(r'^1[345789]\d{9}$', value):
raise serializers.ValidationError("手机号码错误")
return value
def validate_allow(self, value):
if value != "true":
raise serializers.ValidationError('请同意用户协议')
return value
def validate(self, attrs):
if attrs["password"] != attrs["password2"]:
raise serializers.ValidationError("两次密码输入不一致")
redis_conn = get_redis_connection("verify_codes")
mobile = attrs["mobile"]
real_sms_code = redis_conn.get("sms_code_%s" % mobile)
if real_sms_code is None:
raise serializers.ValidationError("无效的短信验证码")
if real_sms_code.decode() != attrs['sms_code']:
raise serializers.ValidationError('短信验证码输入错误')
return attrs
def create(self, validated_data):
del validated_data["password2"]
del validated_data["sms_code"]
del validated_data["allow"]
user = super().create(validated_data)
user.set_password(validated_data["password"])
user.save()
# 为用户设置jwt验证字符串
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
user.token = token
return user
class Meta:
model = User
fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow', 'token')
extra_kwargs = {
'id': {"read_only": True},
"username": {
"max_length": 20,
"min_length": 5,
"error_messages": {
"max_length": "仅允许5-20个字符的用户名",
"min_length": '仅允许5-20个字符的用户名',
}
},
'password': {
'write_only': True,
'min_length': 8,
'max_length': 20,
'error_messages': {
'min_length': '仅允许8-20个字符的密码',
'max_length': '仅允许8-20个字符的密码',
}
}
}
- 序列化器中对应的model模型中没有的字段进行补充
- 增加用户名的属性为只读
- 对用户的密码进行限制
- 对手机号码进行验证
- 对协议进行验证
- 对密码和短信验证码进行验证
- 创建用户的对象保存进到数据库中
- 将保存的用户对象返回给前端
- 为用户设置jwt验证字符串
- 在返回的用户对象中增加一个额外的字段token(此token字段是中载荷为user对象)
用户登录
from rest_framework_jwt.views import obtain_jwt_token
url(r'^authorizations/$', obtain_jwt_token, name='authorizations'),
-
原生的这个登录的方法中,默认返回到回来的数据是token
-
但是在前端页面中我们需要在用户信息中展示用户的基本信息username,user对象
-
对源码的分析如下所示:
- 用户发送过来的登录请求是post请求
- 默认回去post方法中执行
- 执行的流程中会去将数据库中的token与用户发送过来的token做对比返回
- 默认返回的数据由JWT_RESPONSE_PAYLOAD_HANDLER来决定
- 重写JWT_RESPONSE_PAYLOAD_HANDLER并在配置文件中重新配置接可以返回我们想要的数据
-
源代码的执行流程如下
obtain_jwt_token = ObtainJSONWebToken.as_view()
class JSONWebTokenAPIView(APIView):
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
user = serializer.object.get('user') or request.user
token = serializer.object.get('token')
response_data = jwt_response_payload_handler(token, user, request)
response = Response(response_data)
if api_settings.JWT_AUTH_COOKIE:
expiration = (datetime.utcnow() +
api_settings.JWT_EXPIRATION_DELTA)
response.set_cookie(api_settings.JWT_AUTH_COOKIE,
token,
expires=expiration,
httponly=True)
return response
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ObtainJSONWebToken(JSONWebTokenAPIView):
"""
API View that receives a POST with a user's username and password.
Returns a JSON Web Token that can be used for authenticated requests.
"""
serializer_class = JSONWebTokenSerializer
# 默认情况下会去api_settings中加载这个函数
jwt_response_payload_handler = api_settings.JWT_RESPONSE_PAYLOAD_HANDLER
- 重写的代码
def jwt_response_payload_handler(token, user=None, request=None):
"""
自定义jwt认证成功返回数据
"""
return {
'token': token,
'user_id': user.id,
'username': user.username
}
- 在配置文件中进行配置
# JWT
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler',
}