实时 Django 第 2 部分:使用 django、RabbitMQ 和 Vue.js 构建聊天应用程序

(身份验证和用户管理)

我们将通过实施用户管理和身份验证来启动 chatire,以便用户可以创建帐户并登录。

感谢 Django 优秀且充满活力的社区,大部分工作已经为我们完成。因此我们将使用一个名为djoser的第三方 django 库

让我们从pypi

pip install djangorestframework
pip install djoser

Djoser 是 Django 内置身份验证系统的 REST 实现。因此,它不是返回 html 的表单和视图,而是为我们提供了用于用户注册、令牌创建、用户管理等的 REST 端点。

配置djoser

我们将对 djoser 进行尽可能简单的设置。将以下内容包含在您的INSTALLED_APPS

INSTALLED_APPS = (
    'django.contrib.auth',
    ...,
    'rest_framework',
    'rest_framework.authtoken',
    'djoser',
)

将 djoser 的 url 添加到urls.py

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    ...,
    path('auth/', include('djoser.urls')),
    path('auth/', include('djoser.urls.authtoken')),
]

包含rest_framework.authentication.TokenAuthenticationdjango Rest 框架的身份验证类:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        (...)
    ),
}

最后,运行数据库迁移python manage.py migrate将创建djsoer.

就是这样!身份验证端点现在可供使用。让我们创建一个新用户:

curl -X POST http://127.0.0.1:8000/auth/users/create/ --data 'username=danidee&password=mypassword'

{"email":"","username":"danidee","id":1}

瞧!我们有一个新用户。查看 djoser 文档以获取可用端点列表以及如何使用它们 http://djoser.readthedocs.io/en/latest/base_endpoints.htmlhttp :// djoser . 阅读文档。io / en / latest / base_端点。_ html

Vue.js

Vue 是一个用于构建响应式界面的 JavaScript 框架。尽管我是 React 的忠实粉丝(由于 React-native),但我仍然更喜欢使用 Vue 来开发 Web 应用程序。

原因之一是它的学习曲线平缓,它非常容易上手,并且与 React 不同,您不需要构建管道(使用 Webpack 和 co)来构建生产就绪的应用程序。您可以<script>像使用 一样包含外部标签JQuery

它还有一个充满活力的社区,网上有很多插件和教程。

我们将使用它vue-cli来快速创建一个 Vue 应用程序(而不是<script>tag 方法)。ES6+这种方法使我们能够充分利用单文件 Vue 组件的全部功能。

vue-cli从 npm安装:

npm install -g vue-cli

让我们基于 webpack 模板构建一个新项目vue-cli

vue init webpack chatire-frontend

注意:确保选择“安装 vue-router”选项

这可能是喝杯咖啡或吃点快餐的最佳时机,因为这可能需要一些时间,具体取决于您的网络速度。我们尼日利亚的 ISP 真的很糟糕。花了10多分钟。

之后导航到新的 vue 应用程序并使用以下命令运行开发服务器:

npm run dev

当你访问时你应该会看到这个localhost:8080

实时 Django 2.1

让我们稍微讨论一下文件夹结构:

.
├── build
│   ├── build.js
│   ├── check-versions.js
│   ├── logo.png
│   ├── utils.js
│   ├── vue-loader.conf.js
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── config
│   ├── dev.env.js
│   ├── index.js
│   ├── prod.env.js
│   └── test.env.js
├── index.html
├── node_modules
├── package.json
├── package-lock.json
├── README.md
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   ├── components
│   ├── main.js
│   └── router
│       └── index.js
├── static
└── test
    ├── e2e
    │   ├── custom-assertions
    │   │   └── elementCount.js
    │   ├── nightwatch.conf.js
    │   ├── runner.js
    │   └── specs
    │       └── test.js
    └── unit
        ├── jest.conf.js
        ├── setup.js
        └── specs
            └── HelloWorld.spec.js
  • build:此目录包含用于运行 webpack 开发服务器或在准备部署到生产时捆绑应用程序的脚本。例如,npm run dev该命令实际上运行:
    webpack-dev-server --inline --progress --config build/webpack.dev.conf.js 该--inline选项将生成的静态文件注入到我们的index.html页面中。
  • config:正如其名称所示,您应该存储用于开发、测试和生产的配置值
  • src:这是我们编写大部分代码的地方,它包含应用程序不同方面的子文件夹。 我们的单个文件组件将放置在该文件夹中。那里已经有一个默认HelloWorld.vue组件。 router文件夹中的文件index.js包含vue-router的配置
  • static:静态文件(HTML、CSS 和 JavaScript)应存储在此文件夹中。
  • test:最后, webpack 模板通过生成使用Nightwatch运行的端到端测试 (e2e)和使用Jestvue-cli运行的单元测试,可以轻松测试我们的应用程序。 测试可以与npm run unit(对于单元测试)和npm run e2e端到端测试一起运行。

vue-cli还为我们设置了热重载,这确实改善了开发者的体验。编辑组件后点击“保存”后,更改会立即反映在浏览器中。

配置Vue路由器

在文件夹中创建两个组件components。一个用于主聊天屏幕Chat.vue,另一个用于用户身份验证和注册,我们将其称为UserAuth.vue

理想情况下,我们想要的是根据用户的登录状态有条件地显示组件。如果用户已通过身份验证,我们希望显示聊天组件,否则我们希望他们注册或登录,这意味着我们将显示该UserAuth组件。

https://googleads.g.doubleclick.net/pagead/ads?client=ca-pub-8618431079416074&output=html&h=200&slotname=8239403128&adk=2243974991&adf=1373591178&pi=t.ma~as.8239403128&w=867&fwrn=4&lmt=1616559018&rafmt=11&format=867×200&url=https%3A%2F%2Fdanidee10.github.io%2F2018%2F01%2F03%2Frealtime-django-2.html&wgl=1&uach=WyJXaW5kb3dzIiwiMTUuMC4wIiwieDg2IiwiIiwiMTE4LjAuNTk5My4xMjAiLG51bGwsMCxudWxsLCI2NCIsW1siQ2hyb21pdW0iLCIxMTguMC41OTkzLjEyMCJdLFsiR29vZ2xlIENocm9tZSIsIjExOC4wLjU5OTMuMTIwIl0sWyJOb3Q9QT9CcmFuZCIsIjk5LjAuMC4wIl1dLDBd&dt=1699282500698&bpp=1&bdt=234&idt=105&shv=r20231101&mjsv=m202311010101&ptt=9&saldr=aa&abxe=1&prev_fmts=0x0%2C922x280&nras=1&correlator=6473486370751&frm=20&pv=1&ga_vid=1624410613.1699282327&ga_sid=1699282501&ga_hid=1782970992&ga_fc=1&rplot=4&u_tz=480&u_his=3&u_h=720&u_w=1280&u_ah=672&u_aw=1280&u_cd=24&u_sd=1.5&dmc=8&adx=198&ady=5298&biw=1263&bih=595&scr_x=0&scr_y=2925&eid=44759875%2C44759926%2C44759837%2C31079307%2C31079404%2C44807048%2C44807334%2C44807454%2C44807461%2C31078301%2C31079356%2C31079382%2C31078663%2C31078665%2C31078668%2C31078670&oid=2&pvsid=2525487488543578&tmod=2054120635&uas=1&nvt=1&ref=https%3A%2F%2Fdanidee10.github.io%2F2018%2F01%2F01%2Frealtime-django-1.html&fc=1920&brdim=0%2C0%2C0%2C0%2C1280%2C0%2C1280%2C672%2C1280%2C595&vis=1&rsz=%7C%7CpEebr%7C&abl=CS&pfx=0&fu=128&bc=31&td=1&psd=W251bGwsbnVsbCxudWxsLDNd&nt=1&ifi=3&uci=a!3&btvi=1&fsb=1&xpc=bVHKrMma41&p=https%3A//danidee10.github.io&dtd=38186

我们可以通过创建一个实现全局导航来做到这一点。编辑路由器的index.js文件以包含以下内容

import Vue from 'vue'
import Router from 'vue-router'
import Chat from '@/components/Chat'
import UserAuth from '@/components/UserAuth'

Vue.use(Router)

const router = new Router({
  routes: [
    {
      path: '/chats',
      name: 'Chat',
      component: Chat
    },

    {
      path: '/auth',
      name: 'UserAuth',
      component: UserAuth
    }
  ]
})

router.beforeEach((to, from, next) => {
  if (sessionStorage.getItem('authToken') !== null || to.path === '/auth') {
    next()
  }
  } else {
    next('/auth')
  }
})

export default router

beforeEach在导航到我们应用程序中的任何路线之前都会调用守卫。

如果令牌存储在中,sessionStorage我们允许通过调用继续导航next(),否则我们重定向到身份验证组件。

无论用户在我们的应用程序中导航到哪个路线,该函数都会检查用户是否具有身份验证令牌并适当地重定向它们。

登录/注册页面

我用 Bootstrap 4 选项卡构建了一个简单的登录/注册页面,内容如下UserAuth.vue

<template>
  <div class="container">
    <h1 class="text-center">Welcome to Chatire!</h1>
    <div id="auth-container" class="row">
      <div class="col-sm-4 offset-sm-4">
        <ul class="nav nav-tabs nav-justified" id="myTab" role="tablist">
          <li class="nav-item">
            <a class="nav-link active" id="signup-tab" data-toggle="tab" href="#signup" role="tab" aria-controls="signup" aria-selected="true">Sign Up</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" id="signin-tab" data-toggle="tab" href="#signin" role="tab" aria-controls="signin" aria-selected="false">Sign In</a>
          </li>
        </ul>

        <div class="tab-content" id="myTabContent">

          <div class="tab-pane fade show active" id="signup" role="tabpanel" aria-labelledby="signin-tab">
            <form @submit.prevent="signUp">
              <div class="form-group">
                <input v-model="email" type="email" class="form-control" id="email" placeholder="Email Address" required>
              </div>
              <div class="form-row">
                <div class="form-group col-md-6">
                  <input v-model="username" type="text" class="form-control" id="username" placeholder="Username" required>
                </div>
                <div class="form-group col-md-6">
                  <input v-model="password" type="password" class="form-control" id="password" placeholder="Password" required>
                </div>
              </div>
              <div class="form-group">
                <div class="form-check">
                  <input class="form-check-input" type="checkbox" id="toc" required>
                  <label class="form-check-label" for="gridCheck">
                    Accept terms and Conditions
                  </label>
                </div>
              </div>
              <button type="submit" class="btn btn-block btn-primary">Sign up</button>
            </form>
          </div>

          <div class="tab-pane fade" id="signin" role="tabpanel" aria-labelledby="signin-tab">
            <form @submit.prevent="signIn">
              <div class="form-group">
                <input v-model="username" type="text" class="form-control" id="username" placeholder="Username" required>
              </div>
              <div class="form-group">
                <input v-model="password" type="password" class="form-control" id="password" placeholder="Password" required>
              </div>
              <button type="submit" class="btn btn-block btn-primary">Sign in</button>
            </form>
          </div>
          
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  const $ = window.jQuery // JQuery

  export default {

    data () {
      return {
        email: '', username: '', password: ''
      }
    }

  }
</script>

<style scoped>
  #auth-container {
    margin-top: 50px;
  }

  .tab-content {
    padding-top: 20px;
  }
</style>

在上面的代码片段中,v-model用于所有输入字段上的双向数据绑定。这意味着在这些字段中输入的任何内容都可以在 JavaScript 端使用this.field_name.

我们还在两种表单上创建了事件侦听器,使用@submit.prevent它将侦听每个表单的表单提交事件并调用指定的方法。我们还没有实现这些方法。

由于我们正在使用Bootstrap,而不是jQuery从安装,我们定义了一个指向全局注册的npm变量。$window.jQuery

我们将使用jQuery的 ajax 方法与 django 服务器进行通信。如果您想将应用程序jQuery. 它在Vue用户中很受欢迎。

不要忘记在主页中包含 bootstrap 的 CSS 和 JavaScript index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css" integrity="sha384-Zug+QiDoJOrZ5t4lssLdxGhVrurbmBWopoEl+M6BdEfwnCJZtKxi1KgxUyJq13dy" crossorigin="anonymous">

    <style>
      .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active {
        outline: none;
      }
    </style>

    <title>chatire-frontend</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->

    <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/js/bootstrap.min.js" integrity="sha384-a5N7Y/aK3qNeh15eJKGWxsqtnX/wWdSZSKp+81YjTmS15nvnvxKHuzaWwXHDli+4" crossorigin="anonymous"></script>
  </body>
</html>

该页面应如下所示:

实时 Django 1.2

相当不错啊?

让我们获取我们的身份验证令牌

我们想要注册用户,然后将他们重定向到聊天路由。

为了实现这一点,我们必须实现之前指定的signUp和方法:signIn

methods: {
  signUp () {
    $.post('http://localhost:8000/auth/users/create/', this.$data, (data) => {
      alert("Your account has been created. You will be signed in automatically")
      this.signIn()
    })
    .fail((response) => {
      alert(response.responseText)
    })
  },

  signIn () {
    const credentials = {username: this.username, password: this.password}

    $.post('http://localhost:8000/auth/token/create/', credentials, (data) => {
      sessionStorage.setItem('authToken', data.auth_token)
      sessionStorage.setItem('username', this.username)
      this.$router.push('/chats')
    })
    .fail((response) => {
      alert(response.responseText)
    })
  }
}

现在尝试提交表单。哎呀!它失败了:

跨源请求被阻止:同源策略不允许读取 http://localhost:8000/auth/users/create 处的远程资源。(原因:CORS 标头“Access-Control-Allow-Origin”丢失)。http :// localhost : 8000 / auth / users / create。(原因:CORS 标头“Access-Control-Allow-Origin”丢失)。

跨域资源共享

引用 mozilla 开发者网站:

跨源资源共享 (CORS) 是一种机制,它使用附加的 HTTP 标头来让用户代理获得从与当前使用的站点不同的源(域)上的服务器访问所选资源的权限。当用户代理从与当前文档来源不同的域、协议或端口请求资源时,它会发出跨源 HTTP 请求。

本质上是一种颠覆同源政策的机制。同源策略可以防止不同域上的网站XmlHttpRequest向另一个网站/Web 服务创建 (Ajax)。您可以使用 CORS 稍微削弱安全机制,并告诉 Web 服务器允许来自特定域的 Ajax 请求是安全的。

在我们的例子中,即使两个网络服务器都在本地主机上运行,​​由于它们位于不同的端口(8080 和 8000),因此它们被视为不同的域。

http对于与方案 (或)匹配的域https,主机名 ( localhost) 和端口必须匹配。

那么我们如何在 django 应用程序中启用 CORS?我们可以安装第三方应用程序来执行此操作,称为django-cors-headers.

pip install django-cors-headers

将其添加到您的INSTALLED_APPS

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Custom Apps     'rest_framework',
    'rest_framework.authtoken',
    'corsheaders',
    'djoser'
]

包括中间件,(确保它位于之前django.middleware.common.CommonMiddleware

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',

    'corsheaders.middleware.CorsMiddleware',

    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

终于定了CORS_ORIGIN_ALLOW_ALL = True

请注意,这将为所有域启用 CORS。这对于开发来说很好,但是当您在生产中时,您只想允许某些域,这可以通过以下方式控制:

CORS_ORIGIN_WHITELIST

阅读 django-cors-header文档以了解其他选项。

完成此操作后,您应该能够创建帐户并立即登录。

在幕后,django-cors-headers使用中间件向每个请求添加适当的标头,告诉 Django 该请求是安全的并且应该被允许。

登出

因为我们用来sessionStorage存储身份验证令牌,所以我们可以通过打开新的浏览器选项卡来启动新会话。

要在浏览器重新启动/新选项卡之间实际保留令牌,您可以切换到localStorage. 它与 具有相同的 api,sessionStorage因此您只需更改sessionlocal.

然后,您可以创建一个函数,通过调用从您决定保留的存储中删除令牌removeItem。这就是我们要做的localStorage

localStorage.removeItem('authToken')

回顾

这就是这部分的全部内容,我们的目标是构建一个简单的用户管理和身份验证系统。我们首先安装 djoser,它是一个优秀的第三方 django 应用程序,提供REST身份验证端点。

我们还了解了如何使用jQuery的 ajax 方法从 Vue 调用这些端点。

在通往胜利的路上,我们被拦住了Same origin policy,我们简短地谈论了它为什么存在。最终我们学会了如何使用CORSthrough允许从 Vue 应用程序到 django 后端的 Ajax 请求django-cors-headers

在下一部分中,我们将为聊天应用程序构建 django 模型和 API。

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注