ubuntu16.04安装django,mongoengine,微信小程序登录认证

ubuntu16.04安装django,mongoengine,微信小程序登录认证

一、django + mongo

1.安装当前最新的django版本django2.2.3

  我们使用django2.2版本,对应的python版本不能使用系统默认的python2.7

$ sudo apt install python3-pip
$ sudo pip3 install django==2.2.3

可选安装svn客户端:

 安装svn,并从svn服务器下载代码 

$ sudo apt install subversion
$ mkdir projects && cd projects
$ svn co svn://192.168.2.249/repos/apps/wechat_test

2.安装mongoengine

$ sudo pip3 install mongoengine

  为了使用mongoengine,在settings.py中添加以下代码

import mongoengine
conn = mongoengine.connect("parking_test")#本地mongo数据库  

3.安装pymysql

  由于django默认使用的sqlite3存在高并发情况下被锁住导致部分请求失败的情况,即使我们的业务内容完全不需要数据库的情况,django的认证等还是要用到数据库,将数据库置为空是不行的。而django支持的数据库只有4个sqlite、mysql、postgresql、oracle,使用其次较多的应该就是mysql了,mysql对并发支持相比之下就好很多。

$ sudo pip3 install pymysql

  修改settings.py中的DATABASES项

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangodb',
        'USER': 'root',
        'PASSWORD': '123',
        'HOST':'',#本地mysql
        'PORT':'',#默认端口
    },
}

  然后不出意外的话运行django项目会抛出异常

django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.

  异常提示mysqlclient需要1.3.13或更高版本,而实际上github上最高的版本目前就0.9.3,没有1.3.13。搜索以上问题修改2个文件能解决问题,分别是:(注意自己的python版本,根据抛出的异常堆栈找到相应的文件)

/usr/local/lib/python3.5/dist-packages/django/db/backends/mysql/base.py
/usr/local/lib/python3.5/dist-packages/django/db/backends/mysql/operations.py

  打开文件/usr/local/lib/python3.5/dist-packages/django/db/backends/mysql/base.py 并搜索version = Database.version_info,将这行下面的if version < (1, 3, 13):抛出异常注释掉

version = Database.version_info
#if version < (1, 3, 13):
#    raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)

  再次运行django项目,还有一个异常

AttributeError: 'str' object has no attribute 'decode'

  从异常堆栈看到是/usr/local/lib/python3.5/dist-packages/django/db/backends/mysql/operations.py中抛出的,打开文件并搜索query = query.decode(errors='replace'),修改这行代码.将decode改为encode

#query = query.decode(errors='replace')
query = query.encode(errors='replace')

4.时区

  settings.py关于时区的设定默认是UTC,在linux下它对应的时区是美国/芝加哥,在中国当然要用中国的时区,修改配置很简单

TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False

  可选安装:

    silk性能监测工具

$ sudo pip3 install django-silk

    settings.py中添加配置silk

INSTALLED_APPS = (
    ...
    'silk'
)

MIDDLEWARE = [
    ...
    'silk.middleware.SilkyMiddleware',
    ...
]
STATIC_ROOT = os.path.join(BASE_DIR,'static')#BASE_DIR是项目文件夹路径

  在主urls.py中添加silk

from django.urls import include
import silk urlpatterns
= [ ... path('silk/', include('silk.urls', namespace='silk')), ]

  然后执行

$ python3 manage.py migrate
$ python3 manage.py collectstatic

   再以默认ip和端口运行项目,在浏览器中输入localhsot:8000/silk就能看到silk的监测情况

最后的settings.py:

  其中silk 是django可视化性能监测工具包,留意 ALLOWED_HOSTS = ['*']

"""
Django settings for wechat_test project.

Generated by 'django-admin startproject' using Django 2.2.3.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '+-i(^67u0d2_f0=!@8s)a*x4=ao4&k1@hc$qi3lrc=xh5g+r5!'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #'park_space',
    #'coupon',
    #'feedback',
    #'order',
    'silk',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'silk.middleware.SilkyMiddleware',
]

ROOT_URLCONF = 'wechat_test.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'wechat_test.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#         'OPTIONS':{
#             'timeout':20,
#         }
#     }
# }

import mongoengine
conn = mongoengine.connect("parking_test")

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangodb',
        'USER': 'root',
        'PASSWORD': '123',
        'HOST':'',
        'PORT':'',
    },
}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

#TIME_ZONE = 'UTC'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'

STATIC_ROOT = os.path.join(BASE_DIR,'static')

二、nginx + uwsgi +django

  django的配置沿用前面的django+mongo就行,假设我们新建的django项目wechat_test(绝对路径/home/dyan/projects/wechat_test)而不是从svn下载的

1.安装uwsgi

$ sudo pip3 install uwsgi

  进入项目目录执行,以下命令,浏览器访问http://localhost:8000/silk(因为还没有定义其他http请求,或者http://localhost:8000/admin)

$ uwsgi --http :8000 --chdir . --module wechat_test.wsgi

  使用脚步启动,在uwsgi_scripts下新建一个文件uwsgi.ini

$ cd uwsgi_scripts
$ touch uwsgi.ini 

   打开uwsgi.ini填写配置

[uwsgi]
# 项目目录绝对连接
chdir=/home/hlfpark/projects/wechat_test
# 指定项目的application
module=wechat_test.wsgi:application  
# 指定sock的文件路径       
socket=uwsgi_script/uwsgi.sock  #这个后面nginx代理需要用到,放到运行nginx的用户目录下,以免namespace无权访问。
# 进程个数       
workers=4
pidfile=uwsgi_script/uwsgi.pid
# 指定IP端口       
http=192.168.2.244:8080   #这里可以用:8080代替,但是不能用localhost:8080,这样只能本机访问。后面用nginx代理的话就无所谓
# 指定静态文件
static-map=/static=/home/hlfpark/projects/wechat_test/static  #我们在settings.py中设置的STATIC_ROOT路径
# 启动uwsgi的用户名和用户组
uid=hlfpark
gid=root
# 启用主进程
master=true
# 自动移除unix Socket和pid文件当服务停止的时候
vacuum=true
# 序列化接受的内容,如果可能的话
thunder-lock=true
# 启用线程(python GIL线程对于高并发应该没什么用)
#enable-threads=true
# 设置自中断时间
harakiri=30
# 设置缓冲
post-buffering=4096
# 设置日志路径
daemonize=uwsgi_script/uwsgi.log
#日志大小配置500M
log-maxsize = 500000000 

   然后就可以用uwsgi启动django项目了

$ uwsgi -ini uwsgi.ini

2.使用supervisor管理uwsgi

  虽然我们使用uwsgi启动了几个工作进程,但是不好管理,使用supervisor让我们的django项目进程便于管理。

3.配置nginx

  

  location / {
        # 导入一个Nginx模块他是用来和uWSGI进行通讯的
        include  uwsgi_params;    
     #这个.sock文件就是之前在uwsgi.ini中配置的路径 uwsgi_pass unix:
/home/hlfpark/projects/wechat_test/uwsgi_script/uwsgi.sock; uwsgi_connect_timeout 30; } # 指定静态文件路径 location /static/{ alias /home/hlfpark/projects/wechat_test/static; index index.html index.htm; }

三、django+ rest_framework_jwt

  微信小程序登录,后台从微信服务器拿到openid和session_key,然后用rest_framework_jwt生成用户登录态发送给用户,用户保存登录态,之后与后台的通信都带上用户登录态做用户认证。

  rest_framework_jwt是一种类似数字签名的东西。数字签名是明文和密钥用加密算法算出一段定长的字符串,将得出的字符串附加到明文后面,以此做用户认证。rest_framework_jwt为了适用url传递在明文部分增加了加密方式,对数据还进行的base64url编码。默认的密钥是settings.py的SECRET_KEY

  首先安装rest_framework_jwt

$ pip install djangorestframework-jwt
$ pip install djangorestframework

  然后配置settings.py,参考:http://jpadilla.github.io/django-rest-framework-jwt/

  JWT_AUTH配置项如果没有全部使用默认配置

#INSTALLED_APPS配置项增加一项rest_framework
INSTALLED_APPS = [
  ......
  'rest_framework',
]

REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ), 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # <------- 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ), } JWT_AUTH = { 'JWT_ENCODE_HANDLER': 'rest_framework_jwt.utils.jwt_encode_handler', 'JWT_DECODE_HANDLER': 'rest_framework_jwt.utils.jwt_decode_handler', #指定自定义函数以生成令牌有效内容 'JWT_PAYLOAD_HANDLER': 'rest_framework_jwt.utils.jwt_payload_handler', #如果以不同于默认有效负载处理程序的方式存储user_id,请实现此函数以从有效负载中获取user_id。注意:将弃用JWT_PAYLOAD_GET_USERNAME_HANDLER。 'JWT_PAYLOAD_GET_USER_ID_HANDLER': 'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler', #负责控制登录或刷新后返回的响应数据。覆盖以返回自定义响应,例如包括用户的序列化表示。默认返回JWT令牌。 'JWT_RESPONSE_PAYLOAD_HANDLER': 'rest_framework_jwt.utils.jwt_response_payload_handler', 'JWT_SECRET_KEY': SECRET_KEY,#这是用于签署JWT的密钥。确保这是安全的,不共享或公开。默认是项目的settings.py中的SECRET_KEY。 'JWT_GET_USER_SECRET_KEY': None,#这是JWT_SECRET_KEY的更强大版本。它是根据用户定义的,因此如果令牌被泄露,可以由所有者轻松更改。更改此值将使给定用户的所有令牌都无法使用。值应该是一个函数,接受用户作为唯一参数并返回它的密钥。默认值为“None”。 'JWT_PUBLIC_KEY': None, #这是cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey类型的对象。它将用于验证传入JWT的签名。设置时将覆盖JWT_SECRET_KEY。阅读文档以获取更多详细信息。请注意,JWT_ALGORITHM必须设置为RS256,RS384或RS512之一。默认值为“None”。 'JWT_PRIVATE_KEY': None,#这是cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey类型的对象。它将用于签署JWT的签名组件。设置时将覆盖JWT_SECRET_KEY。阅读文档以获取更多详细信息。请注意,JWT_ALGORITHM必须设置为RS256,RS384或RS512之一。默认值为“None”。 'JWT_ALGORITHM': 'HS256',#可能的值是PyJWT中用于加密签名的任何支持算法.默认值为“HS256”。 'JWT_VERIFY': True,#如果密钥是错误的,它会引发一个jwt.DecodeError。您仍然可以通过将JWT_VERIFY设置为False来获取有效负载 'JWT_VERIFY_EXPIRATION': True,#您可以通过将JWT_VERIFY_EXPIRATION设置为False来关闭到期时间验证。如果没有过期验证,JWT将永远存在,意味着攻击者可以无限期地使用泄露的令牌。默认为True。 'JWT_LEEWAY': 0,#这允许您验证过去但不是很远的过期时间。例如,如果您有一个JWT有效负载,其过期时间设置为创建后30秒,但您知道有时您将在30秒后处理它,您可以设置10秒的余地以获得一些余量。 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=30),#设置过期时间。默认值为datetime.timedelta(seconds= 300)(5分钟)。 'JWT_AUDIENCE': None,#这是一个字符串,将根据令牌的aud字段进行检查(如果存在)。默认值None(如果JWT上有aud,则会失败) 'JWT_ISSUER': None,#这是一个字符串,将根据令牌的iss字段进行检查。默认值为None(不检查JWT上的iss)。 'JWT_ALLOW_REFRESH': False,#启用令牌刷新功能。从rest_framework_jwt.views.obtain_jwt_token发出的令牌将具有orig_iat字段。默认值为False 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=365),#限制令牌刷新,是一个datetime.timedelta实例。这是在原始令牌之后可以刷新未来令牌的一段时间。默认值为datetime.timedelta(days = 7)(7天)。 'JWT_AUTH_HEADER_PREFIX': 'JWT',#您可以修改需要与令牌一起发送的Authorization标头值前缀。默认值为JWT。此决定是在PR#4中引入的,以允许在DRF中同时使用此程序包和OAuth2。用于令牌和授权标头的另一个常见值是Bearer。默认是JWT。 'JWT_AUTH_COOKIE': None,#如果除了Authorization标头之外还要使用http cookie作为令牌的有效传输,则可以将其设置为字符串。您在此处设置的字符串将用作cookie名称,该名称将在请求令牌时在响应标头中设置。如果设置,令牌验证过程也将查看此cookie。如果请求中存在标头和cookie,则“授权”标头优先。默认值为“None”,在创建令牌时未设置cookie,验证时也不接受。 }

  之后手动签发jwt,上面的链接仍然有示例。由于我们使用mongodb,需要注意的一点是在用jwt_payload_handler(user)将数据库对象转为python字典对象后user_id字段的值是一个ObjectID对象,不能直接调用jwt_encode_handler(),需要再将ObjectID转成python的原生数据类型。

  看一下jwt_payload_handler(user)的源码,从数据库出来的数据基本上只用到了pk(可能是id或user_id,根据数据库),username,email这三个字段(也可以没有email字段),而其他的exp,orig_iat,aud,iss都是jwt配置文件相关。所以这里不准备使用jwt_payload_handler函数,而是自己弄一个python字典类型,因为openid唯一,我们只需要openid外加一个过期时间戳exp就够了,session_key貌似用不上而且密码也不能随便发出去吧。当然如果需要也可以附带其他数据。下划线内容替换删除线内容:

    import datetime
# 判断用户是否第一次登录 try: user
= User.objects.get(openid=openid) except Exception: # 微信用户第一次登陆,新建用户 user = User.objects.create(openid=openid, sex=1,username ='username') # 手动签发jwt jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) payload={} payload['openid']=openid
#虽然解码token出来是时间戳,但是这里不能用时间戳,要用datetime.datetime.utcnow()加设定的过期时间。这里需要注意time和datetime库的默认时区都不是中国时区+8小时才是北京时间。
#但只是用来认证用户登录过期,不用存入数据库倒是没有关系。如果确实需要可以加上datetime.timedelta(hours=8),这样后面使用time.time()获得时间戳也要加8小时
payload['exp']=datetime.datetime.utcnow()+api_settings.JWT_EXPIRATION_DELTA #+ datetime.timedelta(hours=8) token
= jwt_encode_handler(payload)

  生成的token传给用户,用户向后台发起请求时附带这个token就行了,后台解码token就能进行用户认证了。解出的结果中exp字段是token过期时间的时间戳,openid字段即我们需要的认证的字段,这里需要检测异常,密钥不对的话会抛出异常(除非配置的JWT_VERIFY字段为False,但是这样谁都可以伪造用户发起请求,签名认证就没有意义了

    import jwt
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER try:
user_dict
= jwt_decode_handler(token)
except(jwt.DecodeError):
#do something
pass
else:
pass

  

  

oracle
[ˈôrəkəl]
名词 神谕
动词 预言; 预报; 预示

猜你喜欢

转载自www.cnblogs.com/dyan1024/p/11251745.html
今日推荐