• VUE+Flask登录的初探--前端(Vue+element+axios)+后端(Flask+FlaskLogin+JWT)


    0.前端部分依然基于VueCLI (https://cli.vuejs.org/zh/

    1.创建hello-login文件夹,然后再此文件夹内执行 vue create front-end ,一顿狂回车后,如下图所示:

     2.安装elementUI,axios,js-cookie,qs
      2.1  npm i element-ui -S  (https://element.eleme.cn/#/zh-CN/component/installation
      2.2  npm install --save axios vue-axios  (http://www.axios-js.com/zh-cn/docs/vue-axios.html)
      2.3  npm install js-cookie --save (https://www.npmjs.com/package/js-cookie)
      2.4  npm install qs --save (https://www.npmjs.com/package/qs)

    3.打开main.js,把elementUI和axios加载。搞定这块代码,npm run serve,试试能否正常把项目跑起来。(这是一种编码方式,安装组件算是破坏性的动作,需要勤于测试。以免后期跪了)

    import Vue from 'vue'
    import App from './App.vue'
    import ElementUI from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css'
    import axios from 'axios'
    import VueAxios from 'vue-axios'
    
    //Vue.config.productionTip = false
    axios.defaults.withCredentials = true
    
    Vue.use(VueAxios,axios)
    Vue.use(ElementUI)
    
    new Vue({
      render: h => h(App),
    }).$mount('#app')
    View Code

    4.在components目录下 创建Login.vue文件

    <template>
      <div class='login'>
        <h1>{{ titleMsg }}</h1>
        <el-form ref="loginForm" :model="loginData" label-width="100px">
          <el-form-item label="用户名" prop="username" :rules="[{required: true, message: '用户名不能为空'}]">
            <el-input ref="username" type="password" v-model="loginData.username" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="密码" prop="password" :rules="[{required: true, message: '密码不能为空'}]">
            <el-input type="password" v-model="loginData.password" autocomplete="off"></el-input>
          </el-form-item>      
          <el-form-item>
            <el-button type="primary" @click="submitForm('loginForm')">提交</el-button>
            <el-button @click="resetForm('loginForm')">重置</el-button>
          </el-form-item>
        </el-form>
      </div>
    </template>
    
    <script>
    export default {
      name: 'loginForm',
      data() {
        return {
          titleMsg: '欢迎来到旗帜世界',
          loginData: {
            username: '',
            password: ''
          }
        }
      },
      methods: {
        submitForm(formName) {
          this.$refs[formName].validate((valid) => {
             if (valid) {
               console.log('submit')
             } else {
              console.log('illegad submit!!');
              return false;
            }
          })
        },
        resetForm(formName) {
          this.$refs[formName].resetFields()
          console.log('reset')
        }
      }
    }
    </script>
    View Code

    5.打开App.vue,将helloworld相关代码注释,改写成Login。这种操作可以加深理解vue的组件机制。为后期学习使用router打基础

    <template>
      <div id="app">
        <img alt="Vue logo" src="./assets/logo.png">
        <!-- <HelloWorld msg="Welcome to Your Vue.js App"/> -->
        <Login/>
      </div>
    </template>
    
    <script>
    // import HelloWorld from './components/HelloWorld.vue'
    import Login from './components/Login.vue'
    
    export default {
      name: 'App',
      components: {
        //HelloWorld
        Login
      }
    }
    </script>
    
    <style>
    #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;
    }
    </style>
    View Code

    6.npm run serve 跑起来后如下图。

    7.接下来,使用axios把此表单提交到py搭建的后台并返回消息。先转到py端,把后端代码实现一部分。在完成这个功能吧。

    --------------------------------------------------------------------------------------------------------------------------------------------------------------

    0.后端实现,使用flask(https://dormousehole.readthedocs.io/en/latest/

    1.在hello-login文件夹下创建 back-end文件夹,并运行命令行 py -m venv venv  这时就会在当前目录下创建venv虚拟机 (https://dormousehole.readthedocs.io/en/latest/installation.html#id4

    2.执行venvscriptsactivate 这样就启动了虚拟机环境 

    3.开始安装flask, flask-login, flask-cors, jwt 。注意都要在venv虚拟机环境下安装
      3.1 pip install Flask (https://dormousehole.readthedocs.io/en/latest/installation.html#flask
      3.2 pip install flask-login  (https://flask-login.readthedocs.io/en/latest/)
      3.3 pip install pyjwt  (https://pypi.org/project/PyJWT/)
      3.4 pip install -U flask-cors  (https://flask-cors.readthedocs.io/en/latest/)

    4.在当前目录下创建app.py文件 敲入代码:

    from flask import Flask
    import json
    
    app = Flask(__name__)
    app.secret_key =b'x15fx07xd3xd9xbf*x82xd1xe6xb4xf2x95xddx8fx12'
    #命令行中运行后拷贝出随机值  python -c "import os; print(os.urandom(16))"
    
    @app.route('/hello')
    def helloworld():
      returnData = {'code': 0, 'msg': 'success', 'data': 'hello world' }
      return json.dumps(returnData),200
    
    if __name__ == '__main__':
      app.run(debug = True)
    View Code

    5.在venv虚拟机下 运行py app.py 然后再浏览器中查看 http://localhost:5000/hello  。这说明基本框架已经构建成功。
    之所以返回如下格式,是参考了这篇博文(https://sobird.me/http-json-api-guide.htm),原始出处并未找到,

    6.创建用户单元.user.py。实现了flask_login (https://flask-login.readthedocs.io/en/latest/index.html#your-user-class)所提及的功能。以及用USERS字典暂时代替未来的数据库表。

    from flask_login import UserMixin
    from werkzeug.security import check_password_hash,generate_password_hash
    
    
    USERS = [
        {
            "id":1,
            "name":"admin",
            "password":generate_password_hash('123')
        },
        {
            "id":2,
            "name":"李四",
            "password":generate_password_hash('123')
        },    
    ]
    
    
     
    class User(UserMixin):
        def __init__(self,user):
            self.username = user.get("name")
            self.password_hash = user.get("password")
            self.id = user.get("id")
    
    
        @staticmethod
        def queryUser(username):
            for user in USERS:
                if (user.get('name') == username) :
                    return User(user)
            return None
        
        def verifyPassword(self,password):
            if self.password_hash is None:
                return False
            return check_password_hash(self.password_hash,password)
    
        def get_id(self):
            return self.id
    
        def get(user_id):
            if not user_id:
                return None
            for user in USERS:
                if str(user.get('id')) == str(user_id) :
                    return User(user)
            print('None')
            return None
    View Code

    7.创建jwt操作单元jwt_token.py 。实现了对jwt的简单二次封装。其实不做封装也可以
      7.1 JWT中 “Registered claims” 包含 iss(发行者),exp(到期时间),sub(主题),aud(受众)是官方建议携带的。我偷懒只采用了到期时间这一个声明。
      7.2 jwt可以参考官网(https://jwt.io/)介于国内强大的长城。此官网偶发型能打开。   亦可参考此博文 https://www.cnblogs.com/mantoudev/p/8994341.html

    from jwt import jwt,PyJWTError
    from datetime import datetime,timedelta
    
    SECRECT_KEY = b'x92R!x8exc6x9cxb3x89#xa6x0cxcbxf6xcbxd7xbc'
    
    
    def genToken(data):
      expInt = datetime.utcnow() + timedelta(seconds=3)
      payload = {
        'exp': expInt,
        'data': data 
        }
      token = jwt.encode(payload,key= SECRECT_KEY,algorithm= 'HS256')
      return bytes.decode(token)
    
    def verfiyToken(tokenStr):
      try:
        tokenBytes =  tokenStr.encode('utf-8')
        payload = jwt.decode(tokenBytes,key= SECRECT_KEY,algorithm= 'HS256')
        return payload
      except PyJWTError as e:
        print("jwt验证失败: %s" % e)
        return None
    View Code

    8.创建登录逻辑单元 login.py 。同前端的主要交互逻辑都在此处。flask_login的具体实现(https://flask-login.readthedocs.io/en/latest/index.html#installation)

    import time
    import json
    from flask import Blueprint,request
    from flask_login import LoginManager,login_user,logout_user,login_required,current_user
    from user import User,USERS
    from jwt_token import genToken,verfiyToken
    
    login_page = Blueprint('login_page',__name__)
    
    login_manager = LoginManager()
    login_manager.login_view = 'helloworld'
    
    @login_page.record_once
    def on_load(state):
      login_manager.init_app(state.app)
    
    # @login_manager.user_loader
    # def load_user(user_id):
    #   return User.get(user_id)
    
    @login_manager.request_loader
    def load_user_from_request(request):
      token = request.headers.get('Authorization')
      if token == None:
        return None
    
      payload = verfiyToken(token)
      if payload != None:
        user = User.queryUser(payload['data']['username'])
      else:
        user = None
      return user
    
    
    @login_page.route('/first')
    @login_required
    def firstPage():
      returnData = {'code': 0, 'msg': 'success', 'data': 'First Blood(来自' + current_user.username +')' }
      return returnData,200
    
    @login_page.route('/login', methods=['POST'])
    def login():
      if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.queryUser(username)
        if (user != None) and (user.verifyPassword(password)) :
          login_user(user)
          token = genToken({'username':username,'password':'******'})
          returnData = {'code': 0, 'msg': 'success', 'data': {'token':token} }
          return json.dumps(returnData),200
        else :
          returnData = {'code': 1, 'msg': 'success', 'data': 'username or password is not correct' }
          return json.dumps(returnData),200  
    
    @login_page.route('/logout') 
    @login_required
    def logout():
      username = current_user.username
      logout_user()
      returnData = {'code': 0, 'msg': 'success', 'data': ' Bye ' + username }
      return json.dumps(returnData),200  
    View Code

    9.执行py app.py 后,在postman 分别测试如下链接(注意图中红框内容。):
      9.1 http://127.0.0.1:5000/hello
      9.2 http://127.0.0.1:5000/login
      9.3 http://127.0.0.1:5000/first
      9.4 http://127.0.0.1:5000/logout 

     

     

     10.后端代码初步结束。下一阶段,连接前后端

    --------------------------------------------------------------------------------------------------------------------------------------------------

    1.打开front-end项目,用axios把后端接口调用起来
    2.在项目src目录下 创建文件夹 utils 然后在其内创建文件request.js。这里对axios做了简单封装。

    import axios from 'axios'
    import Cookies from 'js-cookie'
    
    
    /****** 创建axios实例 ******/
    const service = axios.create({
      baseURL: 'http://localhost:5000',  // api的base_url
      timeout: 5000  // 请求超时时间
    })
    
    service.interceptors.request.use(
      config => {
        config.headers['Authorization'] = Cookies.get('Authorization')
        return config
      },
      error => {
        console.log(error)
        return Promise.reject(error)
      }
    )
    
    /****** respone拦截器==>对响应做处理 ******/
    // service.interceptors.response.use(
    //   response => {
    //     console.log(response)
    //     //这里根据后端提供的数据进行对应的处理
    //     if (response.data.result === 'TRUE') {
    //         return response.data;
    //     }
    //   },
    //   error => {
    //     console.log(error);
    //     return Promise.reject(error)
    //   }
    // )
    
    export default service;
    request.js
    <template>
      <div class='login'>
        <h1>{{ titleMsg }}</h1>
        <el-form ref="loginForm" :model="loginData" label-width="100px">
          <el-form-item label="用户名" prop="username" :rules="[{required: true, message: '用户名不能为空'}]">
            <el-input ref="username" type="password" v-model="loginData.username" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="密码" prop="password" :rules="[{required: true, message: '密码不能为空'}]">
            <el-input type="password" v-model="loginData.password" autocomplete="off"></el-input>
          </el-form-item>      
          <el-form-item>
            <el-button type="primary" @click="loginForm('loginForm')">提交</el-button>
            <el-button @click="resetForm('loginForm')">重置</el-button>
          </el-form-item>
          <el-form-item>
            <el-button @click="testForm()">测试</el-button>
            <el-button @click="logoutForm()">登出</el-button>
          </el-form-item>
        </el-form>
      </div>
    </template>
    
    <script>
    import qs from 'qs'
    import service from '../utils/request'
    import Cookies from 'js-cookie'
    export default {
      name: 'loginForm',
      data() {
        return {
          titleMsg: '欢迎来到旗帜世界',
          loginData: {
            username: '',
            password: ''
          }
        }
      },
      methods: {
        loginForm(formName) {
          this.$refs[formName].validate((valid) => {
             if (valid) {
               service({url: '/login',method: 'post',data: qs.stringify(this.loginData)})
                 .then(response => {
                   const { data } = response
                   Cookies.set('Authorization',data.data.token)
                   alert('submit!!!' +'
    '+ data.msg)
                 })
                 .catch(error => {
                   console.log(error)
                 })
             } else {
               console.log('illegad submit!!');
               return false;
            }
          })
        },
        testForm() {
          service({url: '/first',method: 'get'})
            .then(response => {
              const { data } = response
              alert('firstPage!!!' +'
    '+ data.data)
            })
            .catch(error => {
              console.log(error)
            })
        },
        logoutForm() {
          service({url: '/logout',method: 'get'})
            .then(response => {
              const { data } = response
              alert('logout!!!' +'
    '+ data.data)
            })
            .catch(error => {
              console.log(error)
            })
        }
      }
    }
    </script>
    Login.vue

    3.npm run serve 后,测试。四个按钮

     4.其中关注一个状况,当登出后,再次点击测试。测试依然返回成功。这就出现一个问题,登出功能无效,回看后端代码logout是正常运作。
    稍加分析,即可得出产生这种情况的原因是jwt Token本身的弊端。前文的连接中已经提醒。如何解决此状况,日后再分析

    5.收工了。

  • 相关阅读:
    SpringBoot多数据源动态切换数据源
    @ConfigurationProperties 在IDEA中出现红色波浪线问题
    springboot+mybatis实现动态切换数据源
    Spring Boot配置多个DataSource
    模拟测试 20190714
    暴力日记
    模拟测试20190707 [排序//划艇//放棋子]
    组合数学总结
    莫比乌斯专题总结
    AC自动机总结
  • 原文地址:https://www.cnblogs.com/yaoshi641/p/13331029.html
Copyright © 2020-2023  润新知