이번에는 koa2에서 등록 및 로그인 기능을 구현하는 방법을 보여드리고, koa2에서 등록 및 로그인 기능을 구현하기 위한 주의사항은 무엇인지 살펴보겠습니다.
머리말
얼마 전 회사에서 기술에서 제품으로 전환한 동료와 진로에 대해 논의하던 중, 그 친구가 제가 깊이 믿었던 말을 했습니다.
"자신을 가두지 마세요. 특정 분야에 기술이 오면 제품의 변화는 우선 생각의 변화입니다. 프론트엔드에서 일해본 사람이라면 데이터를 입력하는 방법만 알고 데이터를 얻는 방법을 모릅니다.
마치 Vue를 배우는 것과 같습니다. 등록 및 로그인 프로젝트를 보고 간단히 Vue 프로젝트를 시작했는데, 클라이언트 제출의 전 과정을 구현하기 위해 koa와 mongodb를 도입했습니다. 수신 및 반환 - 데이터베이스 입력.
이 프로젝트는 vue-cli를 기반으로 구축되었으며 토큰 방식을 사용하여 사용자 로그인을 확인하고 데이터베이스 등록, 사용자 읽기, 사용자 삭제 등의 기능을 구현합니다. 이 글에서는 독자들이 node와 vue에 대해 어느 정도 기초를 갖고 있다고 가정하고 있으므로 기본적인 부분은 자세히 설명하지 않겠습니다.
시스템 환경: MacOS 10.13.3
느리거나 실패한 npm 설치에 대해
Taobao 미러를 사용하여 설치
1 | $ npm install -g cnpm --registry=https:
|
로그인 후 복사
그러면 모든 npm 설치
가 로 변경됩니다. cnpm install
npm install
改为 cnpm install
项目流程图
为了让项目思路和所选技术更加清晰明了,画了一个图方便理解。
项目启动
0.项目地址
github: https://github.com/stzhongjie/vue-login
1.初始化项目
$ npm install
2.启动项目
$ npm run dev
3.启动MongoDB
$ mongod --dbpath XXX
xxx是项目里 data 文件夹(也可以另行新建,数据库用于存放数据)的路径,也可直接拖入终端。
4.启动服务端
$ node server.js
前端UI
vue的首选UI库我是选择了饿了么的Element-UI了,其他诸如 iview 、 vue-strap 好像没有ele全面。
安装Element-UI
$ npm i element-ui -S
引入Element-UI
1 2 3 4 | import ElementUI from 'element-ui' ;
import 'element-ui/lib/theme-chalk/index.css' ;
Vue. use (ElementUI);
|
로그인 후 복사
利用UI里面的选项卡切换做注册和登录界面的切换,以login组件作为整个登录系统的主界面,register组件作为独立组件切入。Element-UI的组成方式,表单验证等API请查阅官网。
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | <template>
<p class = "login" >
<el-tabs v-model= "activeName" @tab-click= "handleClick" >
<el-tab-pane label= "登录" name= "first" >
<el-form :model= "ruleForm" :rules= "rules" ref= "ruleForm" label-width= "100px" class = "demo-ruleForm" >
<el-form-item label= "名称" prop= "name" >
<el-input v-model= "ruleForm.name" ></el-input>
</el-form-item>
<el-form-item label= "密码" prop= "pass" >
<el-input type= "password" v-model= "ruleForm.pass" auto-complete= "off" ></el-input>
</el-form-item>
<el-form-item>
<el-button type= "primary" @click= "submitForm('ruleForm')" >登录</el-button>
<el-button @click= "resetForm('ruleForm')" >重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label= "注册" name= "second" >
<register></register>
</el-tab-pane>
</el-tabs>
</p>
</template>
<script>
import register from '@/components/register'
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === '' ) {
callback( new Error( '请输入密码' ));
} else {
if (this.ruleForm.checkPass !== '' ) {
this. $refs .ruleForm.validateField( 'checkPass' );
}
callback();
}
};
return {
activeName: 'first' ,
ruleForm: {
name: '' ,
pass: '' ,
checkPass: '' ,
},
rules: {
name: [
{ required: true, message: '请输入您的名称' , trigger: 'blur' },
{ min: 2, max: 5, message: '长度在 2 到 5 个字符' , trigger: 'blur' }
],
pass: [
{ required: true, validator: validatePass, trigger: 'blur' }
]
},
};
},
methods: {
handleClick(tab, event) {
},
resetForm(formName) {
this. $refs [formName].resetFields();
},
submitForm(formName) {
this. $refs [formName].validate((valid) => {
if (valid) {
this. $message ({
type: 'success' ,
message: '登录成功'
});
this. $router .push( 'HelloWorld' );
} else {
console.log( 'error submit!!' );
return false;
}
});
},
},
components: {
register
}
}
</script>
<style rel= "stylesheet/scss" lang= "scss" >
.login {
width: 400px;
margin: 0 auto;
}
#app {
font-family: 'Avenir' , Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.el-tabs__item {
text-align: center;
width: 60px;
}
</style>
|
로그인 후 복사
接下来是注册组件
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | <template>
<el-form :model= "ruleForm" :rules= "rules" ref= "ruleForm" label-width= "100px" class = "demo-ruleForm" >
<el-form-item label= "名称" prop= "name" >
<el-input v-model= "ruleForm.name" ></el-input>
</el-form-item>
<el-form-item label= "密码" prop= "pass" >
<el-input type= "password" v-model= "ruleForm.pass" auto-complete= "off" ></el-input>
</el-form-item>
<el-form-item label= "确认密码" prop= "checkPass" >
<el-input type= "password" v-model= "ruleForm.checkPass" auto-complete= "off" ></el-input>
</el-form-item>
<el-form-item>
<el-button type= "primary" @click= "submitForm('ruleForm')" >注册</el-button>
<el-button @click= "resetForm('ruleForm')" >重置</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === '' ) {
callback( new Error( '请输入密码' ));
} else {
if (this.ruleForm.checkPass !== '' ) {
this. $refs .ruleForm.validateField( 'checkPass' );
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === '' ) {
callback( new Error( '请再次输入密码' ));
} else if (value !== this.ruleForm.pass) {
callback( new Error( '两次输入密码不一致!' ));
} else {
callback();
}
};
return {
activeName: 'second' ,
ruleForm: {
name: '' ,
pass: '' ,
checkPass: '' ,
},
rules: {
name: [
{ required: true, message: '请输入您的名称' , trigger: 'blur' },
{ min: 2, max: 5, message: '长度在 2 到 5 个字符' , trigger: 'blur' }
],
pass: [
{ required: true, validator: validatePass, trigger: 'blur' }
],
checkPass: [
{ required: true, validator: validatePass2, trigger: 'blur' }
],
}
};
},
methods: {
submitForm(formName) {
this. $refs [formName].validate((valid) => {
if (valid) {
this. $message ({
type: 'success' ,
message: '注册成功'
});
} else {
console.log( 'error submit!!' );
return false;
}
});
},
resetForm(formName) {
this. $refs [formName].resetFields();
}
}
}
</script>
|
로그인 후 복사
vue-router
vue-router 是vue创建单页项目的核心,可以通过组合组件来组成应用程序,我们要做的是将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。
上面的代码里已有涉及到一些路由切换,我们现在来完善路由:
安装
$ cnpm i vue-router
프로젝트 흐름도
프로젝트 아이디어와 선정된 기술을 보다 명확하게 하기 위해 이해하기 쉽도록 도표를 그렸습니다.
프로젝트 시작
0 .프로젝트 주소
github: https://github.com/stzhongjie/vue-login1. 프로젝트를 초기화합니다
$ npm install
🎜🎜2. 프로젝트를 시작합니다. >$ npm run dev🎜🎜3. MongoDB 시작🎜🎜
$ mongod --dbpath XXX
🎜🎜xxx는 프로젝트의 데이터 폴더입니다(별도 생성 가능, 데이터베이스 데이터를 저장하는 데 사용됩니다) 경로를 터미널로 직접 드래그할 수도 있습니다. 🎜🎜4. 서버 시작 🎜🎜
$ node server.js
🎜🎜🎜🎜Front-end UI🎜🎜🎜🎜Vue가 선호하는 UI 라이브러리 Ele.me 등에서 Element-UI를 선택한 것 같습니다. iview와 vue-strap은 ele만큼 포괄적이지 않습니다. 🎜🎜🎜🎜Element-UI 설치🎜🎜🎜🎜
$ npm i element-ui -S
🎜🎜🎜🎜Element-UI 소개🎜🎜🎜
1 2 | import Router from 'vue-router'
Vue. use (Router)
|
로그인 후 복사
🎜UI의 탭 스위치를 사용하여 등록하고 로그인 인터페이스 전환 시 로그인 컴포넌트는 전체 로그인 시스템의 메인 인터페이스로 사용되고, 레지스터 컴포넌트는 독립된 컴포넌트로 컷인된다. Element-UI의 구성방법, Form Validation, 기타 API에 대해서는 공식 홈페이지를 참고하시기 바랍니다. 🎜
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 | import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import login from '@/components/login'
import register from '@/components/register'
Vue. use (Router)
const router = new Router({
mode: 'history' ,
routes: [{
path: '/' ,
name: 'home' ,
component: HelloWorld,
meta: {
requiresAuth: true
}
},
{
path: '/HelloWorld' ,
name: 'HelloWorld' ,
component: HelloWorld,
},
{
path: '/login' ,
name: 'login' ,
component: login,
},
{
path: '/register' ,
name: 'register' ,
component: register,
},
]
});
router.beforeEach((to, from, next) => {
let token = store.state.token;
if (to.meta.requiresAuth) {
if (token) {
next();
} else {
next({
path: '/login' ,
query: { redirect: to.fullPath }
});
}
} else {
next();
}
});
export default router;
|
로그인 후 복사
로그인 후 복사
🎜다음 단계는 컴포넌트를 등록하는 것입니다🎜
1 2 | import store from './store'
|
로그인 후 복사
로그인 후 복사
🎜🎜vue-router🎜🎜🎜vue-router는 vue로 단일 페이지 프로젝트를 만드는 핵심입니다. 컴포넌트를 결합하여 애플리케이션을 구성할 수 있습니다. 구성 요소를 경로에 매핑하고 vue-router에 렌더링할 위치를 알려줍니다. 🎜🎜위 코드에는 이미 일부 라우팅 전환이 포함되어 있습니다. 이제 라우팅을 개선해 보겠습니다. 🎜🎜🎜🎜Installation🎜🎜🎜🎜
$ cnpm i vue-router
🎜🎜🎜🎜Introduction🎜🎜🎜
1 2 3 | import Vuex from 'vuex'
Vue. use (Vuex)
|
로그인 후 복사
로그인 후 복사
🎜Create src 폴더 아래에 새로운 라우터(폴더)/index.js🎜🎜세 가지 구성 요소를 도입했습니다.🎜🎜로그인 후 HelloWorld 표시 페이지🎜🎜로그인 로그인 메인 인터페이스🎜🎜등록 등록 구성 요소🎜🎜🎜🎜routing Guard🎜🎜🎜
利用 router.beforeEach
路由守卫设置需要先登录的页面。通过 requiresAuth 这个字段来判断该路由是否需要登录权限,需要权限的路由就拦截,然后再判断是否有token(下文会讲到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 | import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import login from '@/components/login'
import register from '@/components/register'
Vue. use (Router)
const router = new Router({
mode: 'history' ,
routes: [{
path: '/' ,
name: 'home' ,
component: HelloWorld,
meta: {
requiresAuth: true
}
},
{
path: '/HelloWorld' ,
name: 'HelloWorld' ,
component: HelloWorld,
},
{
path: '/login' ,
name: 'login' ,
component: login,
},
{
path: '/register' ,
name: 'register' ,
component: register,
},
]
});
router.beforeEach((to, from, next) => {
let token = store.state.token;
if (to.meta.requiresAuth) {
if (token) {
next();
} else {
next({
path: '/login' ,
query: { redirect: to.fullPath }
});
}
} else {
next();
}
});
export default router;
|
로그인 후 복사
로그인 후 복사
我们可以看到路由守卫中token是从store里面获取的,意味着我们是把token的各种状态存放到store里面,并进行获取,更新,删除等操作,这就需要引入vuex状态管理。
vuex
解释一下为什么一个简单的注册登录单页需要用到vuex:项目中我们各个组件的操作基本都需要获取到token进行验证,如果组件A存储了一个token,组件B要获取这个token就涉及到了组件通信,这会非常繁琐。引入vuex,不再是组件间的通信,而是组件和store的通信,简单方便。
安装
$ cnpm i vuex --S
引入
在main.js引入store,vue实例中也要加入store
1 2 | import store from './store'
|
로그인 후 복사
로그인 후 복사
然后在需要使用vuex的组件中引入
1 2 3 | import Vuex from 'vuex'
Vue. use (Vuex)
|
로그인 후 복사
로그인 후 복사
在src文件夹下面新建 store(文件夹)/index.js
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 | import Vue from 'vue'
import Vuex from 'vuex'
Vue. use (Vuex);
const state = {
token: window.sessionStorage.getItem( 'token' ),
username: ''
};
const mutations = {
LOGIN: (state, data) => {
state.token = data;
window.sessionStorage.setItem( 'token' , data);
},
LOGOUT: (state) => {
state.token = null;
window.sessionStorage.removeItem( 'token' );
},
USERNAME: (state, data) => {
state.username = data;
window.sessionStorage.setItem( 'username' , data);
}
};
const actions = {
UserLogin({ commit }, data){
commit( 'LOGIN' , data);
},
UserLogout({ commit }){
commit( 'LOGOUT' );
},
UserName({ commit }, data){
commit( 'USERNAME' , data);
}
};
export default new Vuex.Store({
state,
mutations,
actions
});
|
로그인 후 복사
可以看到我们通过actions提交mutation,进行token的更改、清除以及用户名储存的操作。
此时启动项目,可以看到初步的注册登录界面,点击注册或登录按钮可以切换到相应界面,并有基础的表单验证,登录后会进入helloworld页面。
我们写好了基础界面,接下来就是要把表单数据发送到后台并进行一系列处理。现在还没有后端接口没关系,我们先写好前端axios请求。
axios
vue的通讯之前使用 vue-resource ,有很多坑。直到vue2.0来临,直接抛弃 vue-resource ,而使用 axios 。
用途:
封装ajax,用来发送请求,异步获取数据。以Promise为基础的HTTP客户端,适用于:浏览器和node.js。
具体API中文说明: https://www.kancloud.cn/yunye/axios/234845
安装
$ cnpm i -S axios
引入
import axios from 'axios'
拦截器
在设置vue-router那部分加入了路由守卫拦截需要登录的路由,但这种方式只是简单的前端路由控制,并不能真正阻止用户访问需要登录权限的路由。当token失效了,但token依然保存在本地。这时候你去访问需要登录权限的路由时,实际上应该让用户重新登录。这时候就需要拦截器 interceptors + 后端接口返回的http状态码来判断。
在src文件夹下面新建axios.js(和App.vue同级)
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 54 55 | import axios from 'axios'
import store from './store'
import router from './router'
var instance = axios.create({
timeout: 5000,
headers: { 'Content-Type' : 'application/json;charset=UTF-8' },
});
instance.interceptors.request. use (
config => {
if (store.state.token) {
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
}
);
instance.interceptors.response. use (
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
router.replace({
path: 'login' ,
query: { redirect: router.currentRoute.fullPath }
});
}
}
return Promise.reject(error.response);
}
);
export default {
userRegister(data){
return instance.post( '/api/register' , data);
},
userLogin(data){
return instance.post( '/api/login' , data);
},
getUser(){
return instance.get( '/api/user' );
},
delUser(data){
return instance.post( '/api/delUser' , data);
}
}
|
로그인 후 복사
代码最后暴露了四个请求方法,分别对应注册(register)、登录(login)、获取(user)、删除(delUser)用户,并且都在/api下面,四个请求接口分别是:
http://localhost:8080/api/login
http://localhost:8080/api/register
http://localhost:8080/api/user
http://localhost:8080/api/delUser
后面我们再利用这四个方法写相对应的后台接口。
服务端 server
注意
文章从这里开始进入服务端,由于服务端需要和数据库、http安全通讯(jwt)共同搭建,因此请结合本节和下面的数据库、jwt章节阅读。
koa2可以使用可以使用async/await语法,免除重复繁琐的回调函数嵌套,并使用ctx来访问Context对象。
现在我们用koa2写项目的API服务接口。
安装
1 2 3 | $ cnpm i koa
$ cnpm i koa-router -S
$ cnpm i koa-bodyparser -S
|
로그인 후 복사
引入
const Koa = require('koa');
在项目根目录下面新建server.js,作为整个server端的启动入口。
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 | const Koa = require ( 'koa' );
const app = new Koa();
const Router = require ( 'koa-router' );
const router = new Router();
const bodyParser = require ( 'koa-bodyparser' );
app. use (bodyParser());
const UserController = require ( './server/controller/user.js' );
const checkToken = require ( './server/token/checkToken.js' );
const loginRouter = new Router();
loginRouter.post( '/login' , UserController.Login);
const registerRouter = new Router();
registerRouter.post( '/register' , UserController.Reg);
const userRouter = new Router();
userRouter.get( '/user' , checkToken, UserController.GetAllUsers);
const delUserRouter = new Router();
delUserRouter.post( '/delUser' , checkToken, UserController.DelUser);
router. use ( '/api' ,loginRouter.routes(),loginRouter.allowedMethods());
router. use ( '/api' ,registerRouter.routes(),registerRouter.allowedMethods());
router. use ( '/api' ,userRouter.routes(),userRouter.allowedMethods());
router. use ( '/api' ,delUserRouter.routes(),delUserRouter.allowedMethods());
app. use (router.routes()). use (router.allowedMethods());
app.listen(8888, () => {
console.log( 'The server is running at http://localhost:' + 8888);
});
|
로그인 후 복사
代码里可以看到,获取用户和删除用户都需要验证token(详见下文jwt章节),并且我们把四个接口挂在到了/api上,和前面axios的请求路径一致。
接口地址配置
另外由于我们的项目启动端口是8080,koa接口监听的端口是8888,于是需要在config/index.js文件里面,在dev配置里加上:
1 2 3 4 5 6 7 | proxyTable: {
'/api' : {
target: 'http://localhost:8888' ,
changeOrigin: true
}
},
jsonwebtoken(JWT)
|
로그인 후 복사
JWT能够在HTTP通信过程中,帮助我们进行身份认证。
具体API详见: https://segmentfault.com/a/1190000009494020
Json Web Token是怎么工作的?
1、客户端通过用户名和密码登录服务器;
2、服务端对客户端身份进行验证;
3、服务端对该用户生成Token,返回给客户端;
4、客户端将Token保存到本地浏览器,一般保存到cookie(本文是用sessionStorage,看情况而定)中;
5、客户端发起请求,需要携带该Token;
6、服务端收到请求后,首先验证Token,之后返回数据。服务端不需要保存Token,只需要对Token中携带的信息进行验证即可。无论客户端访问后台的哪台服务器,只要可以通过用户信息的验证即可。
在server文件夹,下面新建/token(文件夹)里面新增checkToken.js和createToken.js,分别放置检查和新增token的方法。
安装
1 2 3 4 5 6 7 8 | $ cnpm i jsonwebtoken -S
createToken.js
const jwt = require ( 'jsonwebtoken' );
module.exports = function (user_id){
const token = jwt.sign({user_id: user_id}, 'zhangzhongjie' , {expiresIn: '60s'
});
return token;
};
|
로그인 후 복사
创建token时,我们把用户名作为JWT Payload的一个属性,并且把密钥设置为‘zhangzhongjie',token过期时间设置为60s。意思是登录之后,60s内刷新页面不需要再重新登录。
checkToken.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const jwt = require ( 'jsonwebtoken' );
module.exports = async ( ctx, next ) => {
const authorization = ctx.get( 'Authorization' );
if (authorization === '' ) {
ctx. throw (401, 'no token detected in http headerAuthorization' );
}
const token = authorization.split( ' ' )[1];
let tokenContent;
try {
tokenContent = await jwt.verify(token, 'zhangzhongjie' );
} catch (err) {
ctx. throw (401, 'invalid token' );
}
await next();
};
|
로그인 후 복사
先拿到token再用jwt.verify进行验证,注意此时密钥要对应上createToken.js的密钥‘zhangzhongjie'。如果token为空、过期、验证失败都抛出401错误,要求重新登录。
数据库 mongodb
MongoDB是一种文档导向数据库管理系统,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。用node链接MongoDB非常方便。
安装
$ cnpm i mongoose -S
MongoDB的连接有好几种方式,这里我们用connection。connection是mongoose模块的默认引用,返回一个Connetion对象。
在server文件夹下新建db.js,作为数据库连接入口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | const mongoose = require ( 'mongoose' );
mongoose.connect( 'mongodb://localhost/vue-login' );
let db = mongoose.connection;
mongoose.Promise = global .Promise;
db.on( 'error' , function (){
console.log( '数据库连接出错!' );
});
db.on( 'open' , function (){
console.log( '数据库连接成功!' );
});
const userSchema = mongoose.Schema({
username: String,
password: String,
token: String,
create_time: Date
});
const User = mongoose.model( 'User' , userSchema)
module.exports = User;
|
로그인 후 복사
除了我们用的 connetion ,还有 connect() 和 createConnection() 连接方式。
Schema定义表的模板,让这一类document在数据库中有一个具体的构成、存储模式。但也仅仅是定义了Document是什么样子的,至于生成document和对document进行各种操作(增删改查)则是通过相对应的model来进行的,那我们就需要把userSchema转换成我们可以使用的model,也就是说model才是我们可以进行操作的handle。
编译完model我们就得到了一个名为 User 的model。
注意你在这里定义的schema表,后面写注册入库时数据的存储需要对应这个表。
在server文件夹下新建controller(文件夹)/user.js,存放数据库的操作方法。
先安装一些功能插件
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | $ cnpm i moment -s
$ cnpm i objectid-to-timestamp -s
$ cnpm i sha1 -s
const User = require ( '../db.js' ).User;
const moment = require ( 'moment' );
const objectIdToTimestamp = require ( 'objectid-to-timestamp' );
const sha1 = require ( 'sha1' );
const createToken = require ( '../token/createToken.js' );
const findUser = (username) => {
return new Promise((resolve, reject) => {
User.findOne({ username }, (err, doc) => {
if (err){
reject(err);
}
resolve(doc);
});
});
};
const findAllUsers = () => {
return new Promise((resolve, reject) => {
User.find({}, (err, doc) => {
if (err){
reject(err);
}
resolve(doc);
});
});
};
const delUser = function (id){
return new Promise(( resolve, reject) => {
User.findOneAndRemove({ _id: id }, err => {
if (err){
reject(err);
}
console.log( '删除用户成功' );
resolve();
});
});
};
const Login = async ( ctx ) => {
let username = ctx.request.body.name;
let password = sha1(ctx.request.body.pass);
let doc = await findUser(username);
if (!doc){
console.log( '检查到用户名不存在' );
ctx.status = 200;
ctx.body = {
info: false
}
} else if (doc.password === password){
console.log( '密码一致!' );
let token = createToken(username);
console.log(token);
doc.token = token;
await new Promise((resolve, reject) => {
doc.save((err) => {
if (err){
reject(err);
}
resolve();
});
});
ctx.status = 200;
ctx.body = {
success: true,
username,
token,
create_time: doc.create_time
};
} else {
console.log( '密码错误!' );
ctx.status = 200;
ctx.body = {
success: false
};
}
};
const Reg = async ( ctx ) => {
let user = new User({
username: ctx.request.body.name,
password: sha1(ctx.request.body.pass),
token: createToken(this.username),
create_time: moment(objectIdToTimestamp(user._id)).format( 'YYYY-MM-DD HH:mm:ss' ),
});
user.create_time = moment(objectIdToTimestamp(user._id)).format( 'YYYY-MM-DD HH:mm:ss' );
let doc = await findUser(user.username);
if (doc){
console.log( '用户名已经存在' );
ctx.status = 200;
ctx.body = {
success: false
};
} else {
await new Promise((resolve, reject) => {
user.save((err) => {
if (err){
reject(err);
}
resolve();
});
});
console.log( '注册成功' );
ctx.status = 200;
ctx.body = {
success: true
}
}
};
const GetAllUsers = async( ctx ) => {
let doc = await findAllUsers();
ctx.status = 200;
ctx.body = {
succsess: '成功' ,
result: doc
};
};
const DelUser = async( ctx ) => {
let id = ctx.request.body.id;
await delUser(id);
ctx.status = 200;
ctx.body = {
success: '删除成功'
};
};
module.exports = {
Login,
Reg,
GetAllUsers,
DelUser
};
|
로그인 후 복사
上面这些方法构成了项目中数据库操作的核心,我们来剖析一下。
首先定义了公用的三个基础方法:findUser、findAllUsers、delUser。其中findUser需要传入 username 参数,delUser需要传入 id 参数。
注册方法
拿到用户post提交的表单信息,new之前按数据库设计好的并编译成model的User,把获取到的用户名,密码(需要用sha1哈希加密),token(利用之前创建好的createToken方法,并把用户名作为jwt的payload参数),生成时间存入。
此时要先搜索数据库这个用户名是否存在,存在就返回失败,否则把user存入数据库并返回成功。
登录方法
拿到用户post的表单信息,用户名和密码(注册用了哈希加密,此时要解密)。从数据库搜索该用户名,判断用户名是否存在,不存在返回错误,存在的话判断数据库里存的密码和用户提交的密码是否一致,一致的话给这个用户生成一个新的token,并存入数据库,返回成功。
获得所有用户信息
就是把上面公用findAllUsers方法封装了一下并把信息放在result里面,让后面helloworld页面可以获取到这个数据并展示出来。
删除某个用户
注意要先拿到需要删除的用户id,作为参数传入。
写完这些方法,就可以把前面没有完善的注册登录功能完善了。
数据库可视化
当我们注册完,数据入库,此时我们想查看一下刚才注册入库的数据,要用到数据库可视化工具。我是用 MongoBooster ,操作简单。
由下图可以看到示例中注册的两条数据,包含了id、username、password、token、time。那串长长的密码是由于哈希加密编译而成。
整合完善注册组件
在register.vue的表单验证后加上下列代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | if (valid) {
axios.userRegister(this.ruleForm)
.then(({}) => {
if (data.success) {
this. $message ({
type: 'success' ,
message: '注册成功'
});
} else {
this. $message ({
type: 'info' ,
message: '用户名已经存在'
});
}
})
}
|
로그인 후 복사
完善登录组件
登录组件我们之前没有任何数据提交,现在在验证成功后加入一系列方法完成登录操作:
引入axios
import axios from '../axios.js'
然后在login.vue的表单验证后加上下列代码
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 | if (valid) {
axios.userLogin(this.ruleForm)
.then(({ data }) => {
if (data.info === false) {
this. $message ({
type: 'info' ,
message: '账号不存在'
});
return ;
}
if (data.success) {
this. $message ({
type: 'success' ,
message: '登录成功'
});
let token = data.token;
let username = data.username;
this. $store .dispatch( 'UserLogin' , token);
this. $store .dispatch( 'UserName' , username);
this. $router .push( 'HelloWorld' );
}
});
}
|
로그인 후 복사
将表单数据提交到后台,返回data状态,进行账号存在与否的判断操作。登录成功需要拿到返回的token和username存到store,跳到目标HelloWorld页。
完善目标页组件
注册登录成功后,终于到了实际的展示页了——helloworld!
我们来完善这个组件,让它展示出目前所有的已注册用户名,并给出删除按钮。
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | <template>
<p class = "hello" >
<ul>
<li v- for = "(item,index) in users" :key= "item._id" >
{{ index + 1 }}.{{ item.username }}
<el-button @click= "del_user(index)" >删除</el-button>
</li>
</ul>
<el-button type= "primary" @click= "logout()" >注销</el-button>
</p>
</template>
<script>
import axios from '../axios.js'
export default {
name: 'HelloWorld' ,
data () {
return {
users: ''
}
},
created(){
axios.getUser().then((response) => {
if (response.status === 401){
this. $router .push( '/login' );
this. $store .dispatch( 'UserLogout' );
} else {
this.users = response.data.result;
}
})
},
methods:{
del_user(index, event){
let thisID = {
id:this.users[index]._id
}
axios.delUser(thisID)
.then(response => {
this. $message ({
type: 'success' ,
message: '删除成功'
});
this.users.splice(index, 1);
}). catch ((err) => {
console.log(err);
});
},
logout(){
this. $store .dispatch( 'UserLogout' );
if (!this. $store .state.token) {
this. $router .push( '/login' )
this. $message ({
type: 'success' ,
message: '注销成功'
})
} else {
this. $message ({
type: 'info' ,
message: '注销失败'
})
}
},
}
}
</script>
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
.hello {
font-family: 'Avenir' , Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
width: 400px;
margin: 60px auto 0 auto;
}
</style>
|
로그인 후 복사
输出页面比较简单,这里说几个要点:
1.要在实例创建完成后( created() )立即请求getUser()接口,请求失败要清楚掉token,请求成功要把返回数据放入user以供页面渲染。
2. thisID 要写成对象格式,否则会报错
3.注销时要清除掉token
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
使用select组件案例详解
使用webpack做出ReactApp
위 내용은 koa2에서 등록 및 로그인 기능을 만드는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!