用vue和express写一个简单的用户登录

相关环境

服务端:express jsonwebtoken mysql bluebird debug

前端:axios iview js-cookie

思路

前端:路由守卫+vuex+js-cookie+axios(或sessionStorage)

1、路由守卫中首先判断本地cookie里面是否有token。(即每访问一个新的url都会去服务器校验token是否合法)

import Vue from 'vue'
import Router from 'vue-router'
import routes from './router'
import store from '@/store'
import { setTitle, setToken, getToken } from '@/lib/util'

Vue.use(Router)

const router = new Router({
  // mode: 'history', // 默认hash
  routes
})

// 全局导航守卫
router.beforeEach((to, from, next) => {
  to.meta && setTitle(to.meta.titile)
  const token = getToken()

  if(token){
    //通过vuex去服务器校验token
    store.dispatch('authorization', token).then(()=>{
      if(to.name==='login'){
        next({ name: 'home' })
      }else{
        next()
      }
    }).catch(()=>{
      setToken('')
      next({ name: 'login'})
    })
  }else{
    if(to.name === 'login')next()
    else next({ name: 'login' })// 如果没有登录则跳转到login
  }
})

export default router

   util

import Cookies from 'js-cookie'

export const setToken = (token, tokenName = 'token') => {
  Cookies.set(tokenName, token)
}

export const  getToken = (tokenName = 'token') => {
  return Cookies.get(tokenName)
}

  store user

import { login, authorization } from '@/api/user'
import { setToken } from '@/lib/util'

const state = {
  userName: 'Yafully'
}

const getters = {
  firstLetter: (state) => {
    return state.userName.substr(0, 1)
  }
}
const mutations = {
  SET_USER_NAME (state, params) {
    state.userName = params
  },
  SET_RULES (state, rules) {
    state.rules = rules
  }
}
const actions = {
  updateUserName ({ commit, state, rootState, dispatch }) {
    // rootState.appName
  },
  login ({ commit }, { userName, password }) {
    return new Promise((resolve, reject) => {
      login({ userName, password }).then(res => {
        
        if (res.code === 200 && res.token) {
          setToken(res.token)
          resolve()
        } else {
          reject(new Error('错误'))
        }
      }).catch(error => {
        reject(error)
      })
    })
  },
  authorization ({ commit }, token) {
    return new Promise((resolve, reject) => {
      authorization().then(res => {
        if (parseInt(res.code) === 401) {
          reject(new Error('token error'))
        } else {
          //服务器校验token完成把新的token更新到cookie
          setToken(res.token)
          resolve()
          //commit('SET_RULES', res.data.rules.component)
        }
      }).catch(error => {
        reject(error)
      })
    })
  },
  logout () {
    setToken('')
  }
}

export default {
  getters,
  state,
  mutations,
  actions
}

2、如果有则发送token去服务器校验合法性。

 如果合法则继续判断当前路由来路是否为登录页面,如果是则跳转,如果不是则停留;同时把写入新的token到本地cookie续期;

如果没有则继续判断路由来路,如果是登录页则停留,如果不是登录页则跳转登录;

3、登录页面提交登录信息到后端

<template>
  <div class="login">
    <h2>用户登录</h2>
    <Form ref="formInline" :model="formInline" :rules="ruleInline" label-position="top">
        <FormItem prop="user" label="用户名:">
            <Input type="text" v-model="formInline.user" placeholder="Username">
              <Icon type="ios-person-outline" slot="prepend"></Icon>
            </Input>
        </FormItem>
        <FormItem prop="password" label="密码:">
            <Input type="password" v-model="formInline.password" placeholder="Password">
              <Icon type="ios-lock-outline" slot="prepend"></Icon>
            </Input>
        </FormItem>
        <FormItem class="right">
            <Button type="primary" @click="handleSubmit('formInline')">Signin</Button>
        </FormItem>
    </Form>
  </div>
</template>

<script>
import { mapActions } from 'vuex'
export default {
    data () {
        const username = (rule, value, callback) => {
        if (value === '') {
            callback(new Error('Please enter your username'));
        } else {
            if (!/^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$|^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(value)) {
                // 验证用户名是否为手机号或邮箱
                callback(new Error('Please enter a valid username.'));
            }
            callback();
        }
        }

        return {
            formInline: {
                user: 'yafully@gmail.com',
                password: '123456'
            },
            ruleInline: {

                user: [
                    { required: true, validator: username, trigger: 'blur' }
                ],
                password: [
                    { required: true, message: 'Please fill in the password.', trigger: 'blur' },
                    { type: 'string', min: 6, message: 'The password length cannot be less than 6 bits', trigger: 'blur' }
                ]
            }
        }
    },
    methods: {
        ...mapActions([
        'login'
        ]),
        handleSubmit(name) {
            this.$refs[name].validate((valid) => {
                if (valid) {
                    //this.$Message.success('Success!');
                    this.login({
                      userName: this.formInline.user,
                      password: this.formInline.password
                    }).then((res)=>{
                      this.$router.push({ name: 'home' })
                    }).catch(error => {
                      console.log(error)
                    })
                } else {
                    this.$Message.error('Fail!');
                }
            })
        }
    }
}
</script>

<style lang="less">
@import '../assets/less/login.less';
</style>

4、退出登录 清空本地cookie token即可

 

后端:express jsonwebtoken mysql bluebird

1、路由中间件控制全局访问url,将登录的url设为白名单不去校验token,白名单以外的请求则全部校验token;

const express = require('express');
var bodyParser = require('body-parser');
const jwt = require("jsonwebtoken");
const whitelist = require('./lib/whitelist');//路由白名单
const indexRouter = require('./routes');
const usersRouter = require('./routes/user'); 

const app = express();

// 解析 application/json

app.use(bodyParser.urlencoded({ extended: true }))

app.use(bodyParser.json())

app.use(bodyParser.json({ type: 'application/vnd.api+json' }))

const hasOneOf = (str, arr)=> {
 return arr.some(item => {
     return item.includes(str)
 })
}


//设置跨域
app.all('*', function(req, res, next) {
    // res.header("Access-Control-Allow-Origin", "*");
    // res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
    // res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    // res.header("Access-Control-Request-Headers:content-type,xfilecategory,xfilename,xfilesize");
    // res.header("X-Powered-By",' 3.2.1');
    // if(req.method=="OPTIONS") res.sendStatus(200);/*让options请求快速返回*/
    
    let method = req.method.toLowerCase()
    let path = req.path
    if(whitelist[method] && hasOneOf(path, whitelist[method])){
        next()
    }else{
        const token = req.headers.authorization
        if(!token){
            res.sendStatus(403);
        }else{
            jwt.verify(token, 'abcd', (error, decode)=>{
                if(error) {
                    res.send({
                        code: 401,
                        message: 'token error',
                        data:{}
                    })
                }else{
                    req.userName = decode.name
                    next()
                }
            })
        }
        
    }
});

app.use('/index', indexRouter);
app.use('/users', usersRouter);
app.use(function(req, res, next){
    next(creatError(404));
})

app.listen(3000, function(){
  //let u = await getPasswordByName('yafully');
  console.log('server start');
});

2、登录,这是个异步操作,首先通过bluebird数据库中间件用前端发送过来的用户名去数据库查找密码跟前端发来的密码对比;对比成功则返回token到前端,否则返回错误;

const express = require('express');
//const path = require('path');
//const app = express();
const jwt = require("jsonwebtoken");
const router = express.Router();
const getPasswordByName = require('../lib/user');


router.post('/getUserInfo', function(req, res, next){
  res.status(200).send({
    code:200,
    data:{
      name: 'Yafully'
    }
  })
});

// 模拟一个登陆的接口
router.post('/login',async function(req, res, next){
  // 登录成功获取用户名

  const {userName, password} = req.body//req.query

  if(userName) {
    const userInfo = password ? await getPasswordByName(userName) : '';
    //console.log(userInfo);
    // console.log(password);
    // console.log(userInfo.password);
    
    if(!userInfo || !password || userInfo.password !==password){
      res.status(401).send({
        code: 401,
        message: 'Invalid user name or password.',
        data:{}
      })
    } else {
      res.send({
        code:200,
        message:'Success',
        token: jwt.sign({username:userName},'abcd',{
          expiresIn:10// 过期时间
        }),
        userName
        
      })
    }

  }else{
    res.status(401).send({
      code:401,
      message:'Username is empty',
      data:{}
    })
  }
})


module.exports = router;

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值