JWT的含义

Json Web Token认证机制

JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

详细信息自行谷歌.

库的选择(django-rest-framework django-rest-framework-jwt)

使用django-rest-framework开发api并使用json web token进行身份验证
在这里使用django-rest-framework-jwt这个库来帮助我们简单的使用jwt进行身份验证
并解决一些前后端分离而产生的跨域问题

安装django-rest-framework

现在接口一般都是restful风格,所以我们直接使用这个框架

在终端输入以下命令安装

1
pip install djangorestframe

在settings.py的INSTALLED_APPS中加入

1
2
3
4
5
6
INSTALLED_APPS = [
...
'rest_framework',
'rest_framework.authtoken', # 设置token
...
]

安装django-rest-framework-jwt

安装jwt库,简单快速的生成我们所需要的token

在终端输入以下命令安装

1
pip install djangorestframework-jwt

在你的settings.py,添加JSONWebTokenAuthentication到Django REST框架DEFAULT_AUTHENTICATION_CLASSES

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# django-rest-framework设置
REST_FRAMEWORK = {
'PAGE_SIZE': 10,

# 设置所有接口都需要被验证
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}

安装django-cors-headers

解决api跨域请求有好几种方法,比如(jsonp,在apache或nginx中设置,在请求头里设置),我们这里使用这个包来方便的跨域

在终端输入如下命令:

1
pip install django-cors-headers

配置settings.py文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
INSTALLED_APPS = [
...
'corsheaders'
...
]

MIDDLEWARE_CLASSES = (
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware', # 注意顺序
...
)

CORS_ORIGIN_WHITELIST = (
#'*'
'127.0.0.1:8080',# 请求的域名
'localhost:8080',
'localhost',
)

后端配置

在setting里设置token的过期时间

1
2
3
4
import datetime
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
}

当然还有很多其他相关设置,可以自己翻阅文档

修改使用jwt验证的URL

1
2
3
from rest_framework_jwt.views import obtain_jwt_token

url(r'^api-token-auth/', obtain_jwt_token),

配置页面访问权限

按需设置访问权限,加上token之后基本上是不经过认证是不能查看或修改数据的

前端配置

(在前端我们使用jQuery封装的ajax来操作get和post)

使用post方法获取token并存入html的localStorage中

1
2
3
4
5
6
7
8
9
10
11
12
13
<script type="text/javascript">
function post_test() {
$.post("http://10.127.48.204:8000/api-token-auth/",{
'username':'earthchen',
'password':'xxxxxxxx'
},
function(result){
if(result){
localStorage.token=result.token; 存入数据
}
})
}
</script>

在请求数据时需要在头部添加token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="text/javascript">
function test(){
$.ajax({
headers:{
'Authorization':'JWT '+localStorage.token //注意:jwt后面有个空格
},
type:"get",
url:"http://10.127.48.204:8000/snippets/1/",
success:function(result){
document.write(result.style);
}
})
}
<script>

相关demo

新建一个app->authority oauth叫什么都可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# views

from django.shortcuts import render
from django.views import View
from django.http import HttpResponse, JsonResponse
import json
from utils.utils import get_token
from apps.authority.models import User
from rest_framework.views import APIView
from rest_framework.response import Response

class loginView(View):
def post(self,request):
req = json.loads(request.body)
username = req.get('username')
password = req.get('password')
print(username,password)
user = User.objects.get(username=username, password=password)
print(username, password)
get_token(user)

return JsonResponse({
'name': username,
'password': password
})


#如果头部携带token则自动验证
class checkTokenView(APIView):
# permission_classes = [IsAuthenticated] #当前类需要验证 IsAdminUser
def post(self, request):
print(request.user)

return Response({
'status': 200
})

根目录建文件夹/包,utils
users.py
utils.py
constants.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# utils.py
from rest_framework_jwt.settings import api_settings


def verifyblank(str1):
"""验证字符串是否含空格,False为含空格,True为不含空格"""
str2 = "".join(str1.split(" "))
return str1 == str2

def get_token(user):
"""获取token"""
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
# 1. jwt_paylaod_handler 我们需要将用户的信息传递给这个函数,函数自己会获取user中的数据
# 获取了之后,会生成一个二进制
payload = jwt_payload_handler(user)
# 2. 需要对 payload 进行编码,编码之后的才是token
token = jwt_encode_handler(payload)
return token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# users.py
import re
from apps.authority.models import User
from django.contrib.auth.backends import ModelBackend
from utils.constants import username_min, username_auth_max
from utils.utils import verifyblank


def jwt_response_payload_handler(token, user=None, request=None):
"""自定义jwt认证成功返回数据"""
return {
'token': token,
'user_id': user.id,
'username': user.username,
"mobile": user.mobile,
"icon": user.icon,
"province": user.province,
"city": user.city,
"gender": user.gender,
"tag": [str(user.id), "staff"] if user.is_staff else [str(user.id)],
}


def get_user_by_account(account):
try:
if account.isdigit():
# 用户名使用纯数字时判断手机号
if (len(account) == 11) and re.match(r'1[3456789]\d{9}', account):
user = User.objects.get(mobile=account)
else:
user = None
else:
# 用户名不使用纯数字时判断用户名
if (username_min < len(account) < username_auth_max) and verifyblank(account):
# 用户名检测长度和不能有空格
user = User.objects.get(username=account)
else:
user = None
except User.DoesNotExist:
user = None
return user


class UsernameMobileAuthBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
"""自定义手机号和用户名认证"""
# 登陆密码不能是纯数字
if password.isdigit():
return None
user = get_user_by_account(username)
if user is not None and user.check_password(password):
return user
return None
1
2
3
4
5
6
7
# constants.py

# 这里存放常量参数

username_min = 1
username_max = 16
username_auth_max = 21