Vue系列第五篇:Vue2(Element UI) + Go(gin框架) + nginx开发登录页面及其校验登录功能

   本篇使用Vue2开发前端,Go语言开发服务端,使用nginx代理部署实现登录页面及其校验功能。

目录

1.部署结构

2.Vue2前端

2.1代码结构

2.1源码

3.Go后台服务

3.2代码结构

3.2 源码

3.3单测效果

4.nginx

5.运行效果

6.问题总结


1.部署结构

 

2.Vue2前端

2.1代码结构

2.1源码

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>demo</title>
  </head>
  <body>
    <div id="myapp"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

src/App.vue

<template>
<div>
<router-view></router-view>
</div>
</template>

src/assets/css/reset.css

/* http://meyerweb.com/eric/tools/css/reset/ 
   v2.0 | 20110126
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
    display: block;
}
body {
    line-height: 1;
}
ol, ul {
    list-style: none;
}
blockquote, q {
    quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
    content: '';
    content: none;
}
table {
    border-collapse: collapse;
    border-spacing: 0;
}

src/components/Home.vue

<template>
<div id="YYYYYYYYYYY" class="hello">
<button @click="goLogin">登录</button>
<el-button type="primary">你好</el-button>
<el-button type="info">你好</el-button>
<el-button type="danger">你好</el-button>
<el-button type="success">你好</el-button>
<el-tag> fsfsd dsd dsfv</el-tag>
<i class="fa fa-user"></i>
<i class="fa fa-users"></i>
</div>
</template>

<style lang="scss">
    .hello {
        background: yello;
        .el-button {
            color: red;
        }
    }
@import url('../assets/css/reset.css')
</style>


<script>
export default {
  methods: {
    goLogin () {
      this.$router.push('/login')
    }
  }
}
</script>

src/components/Login.vue

<template>
  <div class="login">
    <el-card class="box-card">
        <div slot="header" class="clearfix">
            <span>业务后台管理系统</span>
        </div>

        <el-form label-width="100px" :model="form" ref="form" :rules='rules'>
            <el-form-item label="用户名" prop='username'>
                <el-input v-model="form.username"></el-input>
            </el-form-item>
            <el-form-item label="密码" prop='password'>
                <el-input type='password' v-model="form.password"></el-input>
            </el-form-item>
            <el-form-item>
                <el-button type='primary' @click="login('form')">登录</el-button>
            </el-form-item>
        </el-form>
    </el-card>
  </div>
</template>

/*
原生AJAX和Axios在使用上存在一定的区别。Axios可以支持多种方式,包括浏览器环境、node环境,而AJAX则只能在浏览器环境中使用。
Axios还支持多种请求方式,包括GET、POST、PUT、DELETE等;而AJAX只能支持GET和POST方式发送请求。此外,Axios还可以拦截请求和响应。
*/

<script>

//登录验证的封装
import {nameRule, passRule} from '../utils/validate.js'

import {setToken} from '@/utils/dealtoken.js'

export default {
  data () {
    return {
        form: {
            username: "",
            password: ""
        },
        rules: {
            username: [{validator: nameRule, required: true, trigger: "blur"}],
            password: [{validator: passRule, required: true, trigger: "blur"}]
        }
    }
  },
  methods: {
    login(form) {
        this.$refs[form].validate((valid) => {
            if (valid) {
                console.log(this.form)
                //this.$router.push('/home')
                //解决axios post请求 404  OPTIONS问题
                const posthead = {
                    headers: {
                    'Content-Type': 'text/plain;charset=utf-8',
                    },
                    //withCredentials: 'same-origin'
                }
                this.axios.post('http://localhost:8282/login', JSON.stringify(this.form), posthead).then(res => {
                    console.log(res)
                    if (res.status === 200) {
                        //localStorage.setItem('username', res.data.username)
                        setToken('username', res.data.username)
                        this.$message({message: res.data, type: 'success'})
                        this.$router.push('/home')
                        console.log("post mid")
                    }
                })
            } else {
                console.error(this.form)
            }
        })
    }
  }
}
</script>

<style lang='scss'>
    .login {
        width: 100%;
        height: 100%;
        position: absolute;
        background: #409EFF;
        .box-card {
            width: 450px;
            margin: 200px auto;
            .el-card_header {
                font-size: 34px;
            }
            .el-button {
                width: 100%;
            }
        }
    }
</style>

src/main.js

import Vue from 'vue'
import App from './App'
import 'font-awesome/css/font-awesome.min.css'
import axios from 'axios'
import router from './router'

// 挂载到原型就可以全局使用
Vue.prototype.axios = axios

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)


new Vue({
  router,
  render: h => h(App)
}).$mount('#myapp')

src/router/index.js

import Vue from 'vue'
import Home from '@/components/Home'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
  { path: '/', redirect: '/login', component: () => import('@/components/Login') },
  { path: '/login', name: 'Login', component: () => import('@/components/Login') },
  { path: '/home', component: Home },
  { path: '*', component: Home }
]


export default new VueRouter({
  mode: 'history',
  routes: routes
})


src/utils/validate.js

// Token的封装 Token存放在localStorage
export function setToken(tokenkey, token) {
    return localStorage.setItem(tokenkey, token)
}

export function getToken(tokenkey) {
    return localStorage.getItem(tokenkey)
}

export function removeToken(tokenkey) {
    return localStorage.removeItem(tokenkey)
}

src/utils/dealtoken.js

//用户名匹配
export function nameRule (rule, value, callback) {
    let reg = /(^[a-zA-Z0-9]{4,10}$)/;
    if (value === "") {
        callback(new Error("请输入用户名"));
    } else if (!reg.test(value)) {
        callback(new Error("请输入4-10用户名"));
    } else {
        callback();
    }
}

//密码匹配
export function passRule (rule, value, callback) {
    let pass = /^\S*(?=\S{6,12})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/;
    if (value === "") {
        callback(new Error("请输入密码"));
    } else if (!pass.test(value)) {
        callback(new Error("请输入6-12位密码需要包含大小写和数字及特殊字符"));
    } else {
        callback();
    }
}

plugins/element.js

import Vue from 'vue'
import { Button, Tag } from 'element-ui'

Vue.use(Button)
Vue.use(Tag)

3.Go后台服务

3.2代码结构

3.2 源码

controller/login.go

package controller

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "path/filepath"

    "github.com/gin-gonic/gin"
)

// post  http://127.0.0.1:8181/login
// axios.post 和 post json处理
func LoginPost(ctx *gin.Context) {
    version := ctx.DefaultQuery("version", "V1.0.0.1")

    //前端使用axios直接传递form时,axios会默认使用json,必须使用下面方式获取json数据,解析后再使用
    data, _ := ioutil.ReadAll(ctx.Request.Body)
    type UserInfo struct {
        Username string
        Password string
    }
    var u UserInfo
    err := json.Unmarshal(data, &u)
    if err != nil {
        fmt.Println(err)
    }
    username := u.Username
    password := u.Password

    fmt.Println("login info:: ", version, username, password)

    /*
        ctx.Header("Access-Control-Allow-Origin", "*")

            ctx.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
            ctx.Header("Access-Control-Allow-Credentials", "true")
            ctx.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
            ctx.Header("content-type", "application/json;charset=UTF-8")

            ctx.Writer.Header().Set("Access-Control-Max-Age", "86400")
            ctx.Writer.Header().Set("Access-Control-Allow-Methods", "*")
            ctx.Writer.Header().Set("Access-Control-Allow-Headers", "*")
            ctx.Writer.Header().Set("Access-Control-Expose-Headers", "*")
            ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
    */

    if username == "123456" && password == "1234abcdE@" {
        ctx.String(http.StatusOK, "登录成功")
    } else {
        ctx.String(http.StatusNotFound, "用户名或密码错误")
    }
}

// http://127.0.0.1:8181/formlogin
// form表单提交处理 application/x-www-form-urlencoded
func FormLoginPost(ctx *gin.Context) {

    //第一种
    username := ctx.PostForm("username")
    password := ctx.PostForm("password")

    //第二种
    /*
        username := ctx.DefaultPostForm("username", "somebody")
        password := ctx.DefaultPostForm("password", "***")
    */

    //第三种
    /*
        username, ok := ctx.GetPostForm("username")
        if !ok {
            username = "取不到的话"
        }
        password, ok := ctx.GetPostForm("password")
        if !ok {
            password = "***"
        }
    */

    fmt.Println("FormLoginPost :: ", username, password)
    /*
        ctx.HTML(http.StatusOK, "home.html", gin.H{
            "Name":     username,
            "Password": password,
        })
    */
    ctx.JSON(http.StatusOK, gin.H{
        "Name":     username,
        "Password": password,
    })

    if username == "123456" && password == "1234abcdE@" {
        ctx.String(http.StatusOK, "登录成功")
    } else {
        ctx.String(http.StatusNotFound, "用户名或密码错误")
    }
}

// form表单提交文件上传处理 multipart/form-data
func UploadFile(ctx *gin.Context) {
    file, _ := ctx.FormFile("uploadfile")
    fmt.Println(file.Filename)
    file_path := "upload/" + filepath.Base(file.Filename)
    fmt.Println(file_path)
    ctx.SaveUploadedFile(file, file_path)
    ctx.String(http.StatusOK, "上传成功")
}

server.go

package main

import (
    "main/controller"
    "net/http"

    "github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
)

/*
// 错误: server.go:4:2: package main/controller is not in GOROOT (/home/tiger/go/go/src/main/controller)
go mod init main

//错误: server.go:7:2: no required module provides package github.com/gin-gonic/gin; to add it:
go get github.com/gin-gonic/gin

//处理跨域框架
go get github.com/gin-contrib/cors
*/

/*
当客户端(尤其是基于 Web 的客户端)想要访问 API 时,服务器会决定允许哪些客户端发送请求。这是通过使用称为 CORS 来完成的,它代表跨源资源共享。
跨域资源共享 (CORS) 是一种机制,允许从提供第一个资源的域之外的另一个域请求网页上的受限资源。
*/

func CrosHandler() gin.HandlerFunc {
    return func(context *gin.Context) {
        context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        context.Header("Access-Control-Allow-Origin", "*") // 设置允许访问所有域
        context.Header("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE,UPDATE")
        context.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma,token,openid,opentoken")
        context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar")
        context.Header("Access-Control-Max-Age", "172800")
        context.Header("Access-Control-Allow-Credentials", "true")
        context.Set("content-type", "application/json") //设置返回格式是json
        //处理请求
        context.Next()
    }
}

// http://127.0.0.1:8181/ping
// http://127.0.0.1:8181/index
func main() {
    r := gin.Default()

    // 设置全局跨域访问
    //r.Use(CrosHandler())

    //cors处理跨域
    r.Use(cors.Default())

    // 返回一个json数据
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
            "num":     888,
        })
    })

    // 返回一个html页面
    r.LoadHTMLGlob("templates/*")
    r.GET("/index", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.html", nil)
    })

    r.POST("/login", controller.LoginPost)
    r.POST("/formlogin", controller.FormLoginPost)
    r.POST("/upload", controller.UploadFile)

    //r.Run()  // <===> r.Run(":8080")  监听并在 0.0.0.0:8080 上启动服务
    r.Run(":8181")
}

templates/home.html

欢迎进入首页

templates/index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8"/>
        <title>欢迎进入首页</title>
    </head>
    <body>
        <h3>登录测试</h3>
        <hr/>

        <form action="http://localhost:8282/formlogin" method="post">
            <table border=0 title="测试">
                <tr>
                    <td>用户名:</td>
                    <td><input type="text" name="username"></td>
                </tr>
                <tr>
                    <td>密码:</td>
                    <td><input type="password" name="password"></td>
                </tr>
                <tr>
                    <td colspan=2>
                        <input type="reset" />
                        <input type="submit" value="登录" />
                    </td>
                </tr>
            </table>
        </form>
        <br>
        <h3>文件上传测试</h3>
        <hr/>
        <form action="http://localhost:8282/upload" method="post" enctype="multipart/form-data">
            <input type="file" name="uploadfile"/>
            <input type="submit" value="upload">
        </form>
    </body>
</html>

3.3单测效果

http://127.0.0.1:8181/ping


http://127.0.0.1:8181/index

 

上传文件:

8282端口nginx代理测试:

4.nginx

#user nobody;

worker_processes 1;

#error_log logs/error.log;

#error_log logs/error.log notice;

#error_log logs/error.log info;

#pid logs/nginx.pid;


 

events {

worker_connections 1024;

}

#location [ = | ~ | ~* | ^~ ] uri { }

# ~区分大小写的正则匹配;

# ~*不区分大小写的正则匹配;

# ^~常规字符串匹配;

http {

include mime.types;

default_type application/octet-stream;

#log_format main '$remote_addr - $remote_user [$time_local] "$request" '

# '$status $body_bytes_sent "$http_referer" '

# '"$http_user_agent" "$http_x_forwarded_for"';

#access_log logs/access.log main;

sendfile on;

#tcp_nopush on;

#keepalive_timeout 0;

keepalive_timeout 65;

fastcgi_intercept_errors on;

#gzip on;

limit_conn_zone $binary_remote_addr zone=addr:10m;

#代理静态页面

server {

listen 80;

server_name www.liudehua.com;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {

root /home/tiger/nginx-1.22.1/nginx/html/dist;

index index.html;

try_files $uri $uri/ /index.html;

}

#404配置

#error_page 404 /404.html;

#location /404.html {

# alias /home/tiger/nginx-1.22.1/nginx/html/dist;

# index index.html index.htm;

#}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html

#

error_page 500 502 503 504 /50x.html;

location = /50x.html {

root html;

}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80

#

#location ~ \.php$ {

# proxy_pass http://127.0.0.1;

#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

#

#location ~ \.php$ {

# root html;

# fastcgi_pass 127.0.0.1:9000;

# fastcgi_index index.php;

# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;

# include fastcgi_params;

#}

# deny access to .htaccess files, if Apache's document root

# concurs with nginx's one

#

#location ~ /\.ht {

# deny all;

#}

}

#代理真正的后台服务

server {

listen 8282;

location / {

proxy_pass http://127.0.0.1:8181/;

}

}


 

# another virtual host using mix of IP-, name-, and port-based configuration

#

#server {

# listen 8000;

# listen somename:8080;

# server_name somename alias another.alias;

# location / {

# root html;

# index index.html index.htm;

# }

#}


 

# HTTPS server

#

#server {

# listen 443 ssl;

# server_name localhost;

# ssl_certificate cert.pem;

# ssl_certificate_key cert.key;

# ssl_session_cache shared:SSL:1m;

# ssl_session_timeout 5m;

# ssl_ciphers HIGH:!aNULL:!MD5;

# ssl_prefer_server_ciphers on;

# location / {

# root html;

# index index.html index.htm;

# }

#}

}

部署前端程序:

 启动nginx:

5.运行效果

http://www.liudehua.com/

 可以看到nginx反向代理:

 

 postman测试:

不代理测试:8181端口

 代理测试:8282端口

6.问题总结

6.1 跨域访问错误:前后台一块解决
6.2 form表单请求,axios.post请求Go解析处理
6.2 vue中的正则表达式
vscode安装插件any-rule插件
ctrl + shift + p 输入密码,选择正则表达式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Vue2是一种流行的JavaScript框架,用于构建用户界面。它具有轻量级、高效和灵活的特点,可以帮助开发者快速构建交互性强的Web应用程序。 Element UI是一套基于Vue2的开源UI库,提供了一系列的组件,包括按钮、表格、表单等,方便开发者构建美观的前端界面。 Cesium是一个用于构建地球图像的JavaScript库,可以在Web上创建高度交互的3D地球视图。它提供了丰富的地理信息和可视化功能,可以用于制作地理信息系统(GIS)应用程序。 搭建Vue2、Element UI和Cesium的框架可以让开发者在Web应用程序中结合地理信息和用户界面。具体步骤如下: 1. 首先,在项目中安装Vue2、Element UI和Cesium的依赖项。 ``` npm install vue npm install element-ui npm install cesium ``` 2. 在Vue项目中引入Vue2和Element UI的库,可以使用CDN或本地引入的方式。 3. 创建一个Vue的根组件,并在模板中使用Element UI的组件。 4. 在Vue项目中引入Cesium的库,可以使用CDN或本地引入的方式。 5. 在Vue的根组件中,创建一个Cesium的容器,并在created生命周期钩子函数中初始化Cesium的地球视图。 6. 在Cesium地球视图中加载地理信息和相应的可视化效果,例如加载地图数据、添加3D模型等。 通过以上步骤,就可以搭建一个基于Vue2、Element UI和Cesium的框架开发者可以根据自己的需求进一步开发和定制化。这个框架可以让开发者在用户界面上展示地理信息,并提供交互性强的地球视图。同时,也可以使用Element UI的组件提供更美观友好的用户界面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值