Vue + Vant + Koa2 + MongoDB 实现用户登陆

前面的文章介绍了 Vue + Vant + koa2 + MongoDB 实现用户注册,今天我们来讲解一下用户登陆的实现。

 

主要实现:

1、前端通过 axios 向后端发送 username、password

2、后端检查用户名是否已注册、比对 password 是否正确。

3、后端生成 JWT 令牌返回给前端,前端存储于 localStorage 中。

其中,图形验证码部分将在下一节中讲解。

 

效果截图:

登陆成功,返回 code:200 msg : 登陆成功,并打印出 token

未注册用户返回:

密码错误返回:

 

JWT简介:

JWT 全称 JSON WEB TOKEN ,是目前最流行的跨域认证解决方案,假设用户登陆成功之后,首先访问 A 页面,然后访问 B 页面,而这两个页面的内容都需要根据用户的身份来获取,比如说 A 页面为用户的订单列表,需要根据 user_id 来查询该用户的所有订单,B 页面为用户的收货地址列表,又需要根据 user_id 字段来查询该用户的地址列表,而在不同页面之间,这个 user_id 是如何传递的呢,首先我们可以利用session ,服务端通过 cookie 将 session 发送给客户端,这种方式是将 session 存储于服务端,还有一种方式就是服务端通过 jwt 将 user_id、username 等信息生成加密的 token ,客户端接收之后将其存储于LocalStroage ,这样当用户访问 B 页面地址时,配置请求的 header 访问头,将 token 添加进请求头,服务端收到 token 进行解析,即可解析出 user_id、username 等信息,从而获取出 user_id ,从而 B 页面就会根据 user_id 获取到该用户的地址列表。

 

 另外,cookie 方式不支持跨域,JWT 支持跨域认证,例如单点登录就是依靠 JWT 实现。

 

一、前端部分

于.\vue-mall-mobile\mall\src 新建 Login.vue,采用 Vant 的表单控件 :

<template>
  <div id="login">
    <van-form>
      <van-field v-model="username" label="账号" />
      <van-field v-model="password" type="password" label="密码" />
      <van-button square block type="info" native-type="submit" size="normal" @click="Login"> 登陆 </van-button>
</van-form>
</div> </template> <script> import { Field } from 'vant'; import { Button } from 'vant'; import { Toast } from 'vant'; import { Form } from 'vant'; import ajax from '@/api';
export
default { components:{ [Field.name]: Field, [Button.name]: Button, [Toast.name]: Toast, [Form.name]: Form, }, data() { return { username:'', password:'', } } } </script>

 

mothods 中添加 Login() 方法:

async Login() {
  let { username, password} = this.$data;
  let res = await ajax.login(username, password);
  console.log(res)
}

 

在 src\api\index.js 中添加,通过 axios 将 username、password 发送到后端接口:

login(username = '', password = '') {
    return axios.post('localhost:3000/users/loginUser',{username, password})
  }

 

二、后端部分:

在后端 routes\users.js 中添加响应路由:

/**
 * 用户登陆
 */
router.post('/loginUser', async function (ctx) {
  let {username, password} = ctx.request.body;
  
  if(!username || !password) return ctx.body = {code: 4020,msg: '请填写完整的注册信息'};
  
  let args = {username, password};
  const userData = await userService.accountLogin(args);
  
  ctx.body = (userData.code === 200) 
     ? {code: 200, msg: '登陆成功', token: jwt._createToken(userData)} 
     : userData
})

 

在 service\userService 中添加处理登陆方法 accountLgoin({ username,  password }) :

async accountLogin({username, password}) {
    const userDoc = await UserModel.findOne({username});
    if(!userDoc) return {code: 0, msg: '该用户尚未注册'};
    
    let result = await userDoc.comparePassword(password, userDoc.password); // 进行密码比对是否一致
    return !result
      ? { code: -2, msg: '密码不正确' }
      : {
          code: 200,
          _id: userDoc._id,
          userName: userDoc.userName,
          gender: userDoc.gender,
          avatar: userDoc.avatar, 
          mobilePhone: userDoc.mobilePhone,
          email: userDoc.email,
          year: userDoc.year,
          month: userDoc.month, 
          day: userDoc.day
        };
  }

 这里的 userDoc.comparePassword() 方法,是我们在定义 UserModel 是添加的方法,bcryptjs 的 bcrypt.compare() 方法来实现对比我们在输入框中输入的秘密和保存在数据库里的 hash 处理后的密码。

 

生成 JWT 令牌,\utils\jwt.js 中的 _createToken() 方法:

const jwt = require('jsonwebtoken');
/**
 * 创建 Token
 */
const _createToken = (userInfo) => {
  // JWT 格式 token | 有效时间 1 小时
  return jwt.sign({ userInfo }, secret, { expiresIn: '1h' });
};

 

这里的 { userInfo } 存储了用户的 id、username、性别等信息,以 token 令牌的方式存储于客户端,也就是前端,等到客户端发送请求时,在 header 请求头中,加入该 token,后端通过 jwt.virefy() 将 token 中存储的信息解析出来,解析方法如下,从而使后端代码可以通过解析出来的信息认定用户的身份。

const _verify = (token) => {
  return jwt.verify(token, secret, (error, decoded) => {
      console.log(decoded);
  });
};

 

三、前端储存 token

 

方法一:可以直接在 axios 返回时,用 localStorage.setItem() 将 token 写入,

localStorage.setItem(USER_TOKEN, JSON.stringify(userToken));

这样,我们下次想要访问该 token 时,直接:

JSON.parse(localStorage.getItem(USER_TOKEN))

 

不过我们应该熟悉 Vuex 状态管理,为后续开发中大型应用做准备,方法二:

 在 axios 返回时,调用 this.$store.commit()

(res.token)&&(this.$store.commit('setUserToken', res.token));

 

setUserToken 位于 .\src\store\index.js :

export default new Vuex.Store({
  state: {
    token: '',
  },
  mutations: {
    setUserToken(state, token) {
      alert(token);
      state.token = token;
    } 
  },
}

 

到这里,完整的登陆功能就实现啦。下一节我们会实现图片验证码。

 

如果文章中有错误之处,欢迎大家交流指正。

posted @ 2020-06-23 14:45  几行  阅读(848)  评论(0编辑  收藏  举报