包含前端vue,后端gin框架的登录注册以及mysql数据库的连接使用,主要是练习后端框架的使用,前端简陋见谅。
一.前端vue部分
首先搭建vue框架,可以参考我的这篇博客https://blog.csdn.net/m0_49049914/article/details/112527510
前端登录页面展示如下
<template>
<div>
<van-form @submit="onSubmit">
<van-field
v-model="username"
name="username"
label="账号"
placeholder="账号"
:rules="[{ required: true, message: '请填写账号' }]"
/>
<van-field
v-model="password"
type="password"
name="password"
label="密码"
placeholder="密码"
:rules="[{ required: true, message: '请填写密码' }]"
/>
<div style="margin: 16px;">
<van-button round block type="info" native-type="submit">登录</van-button>
<van-button type="primaty" to="signup">注册</van-button>
</div>
</van-form>
</div>
</template>
<script>
import { login } from "../api/login";
import Cookies from 'js-cookie'
// import { login1 } from "../api/login";
export default {
data() {
return {
username: "",
password: ""
}
},
methods: {
async onSubmit(values) {
var _this = this
console.log("submit", values);
await login(values).then(function(res) {
if(res.data.code == 2000) {
Cookies.set('token', res.data.data.token)
_this.$router.replace("/list")
} else {
alert("账号或密码错误")
}
});
}
}
};
</script>
里面用到的知识有:后端接口的调用,cookie的设置,以及前后端交互
二.接口调用
import request from '../../utils/request'
export function login(param) {
return request({
url:'v1/login',
method:'post',
data: param,
})
}
request.js文件如下:
import axios from 'axios'
import { getToken } from './auth'
import Cookies from 'js-cookie'
import Config from '../settings'
import qs from 'qs'
// 创建axios实例
const service = axios.create({
baseURL: Config.baseURL, // api 的 base_url
timeout: Config.timeout // 请求超时时间
});
// request拦截器
service.interceptors.request.use(
config => {
if (getToken()) {
config.headers['Authorization'] = "Bearer "+getToken() //让每个请求携带自定义token
// config.headers['Authorization'] = 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIzMGQ2NjU2Y2YwZTA0ZTFhODQ2Y2Q2ZGE0OTBhZmJlOCIsImF1dGgiOiJhZG1pbiIsInN1YiI6ImFkbWluIn0.owHaRwQYsSIBb8h5EjABbNsmNnjefVGemuR51iTdWw5cZDuwMd6BKzc5_RyV8STBZyPO_krte4qRZb3-D_n6zQ'; //让每个请求携带自定义token
}
config.headers['Content-Type'] = Config.contentType;
console.log(config.data,"ddddawdawd")
//对参数进行qs的序列化
config.data = JSON.stringify(config.data);
console.log(config.data,"ddddawdawd")
return config;
},
error => {
// 对请求错误做些什么,自己定义
//...
return new Promise(function(resolve, reject) {
reject(error);
}).catch(function(reason) {
console.log('catch:', reason);
});
}
);
// response 拦截器
service.interceptors.response.use(
response => {
console.log(response.status);
if (response.status === 200) {
return Promise.resolve(response);
} else {
return new Promise(function(resolve, reject) {
reject(response);
}).catch(function(reason) {
console.log('catch:', reason);
});
}
},
error => {
if (error.response.data.status === 401) {
console.log(123);
Cookies.remove('TOEKN');
}
let code = 0;
let message = error.response.data.message;
//网络请求超时
try {
code = error.response.status;
} catch (e) {
if (error.toString().indexOf('Error: timeout') !== -1) {
//...
return new Promise(function(resolve, reject) {
reject(error);
}).catch(function(reason) {
console.log('catch:', reason);
});
}
}
//其它有状态码的返回情况
if (code) {
return new Promise(function(resolve, reject) {
reject(message);
})
// .catch(function(reason) {
// console.log('catch:', reason);
// });
}
//网络断开情况
if (error.toString().indexOf('Error: Network Error') !== -1) {
//...
return new Promise(function(resolve, reject) {
reject(error);
}).catch(function(reason) {
console.log('catch:', reason);
});
}
//其他情况
//...
return new Promise(function(resolve, reject) {
reject(error);
}).catch(function(reason) {
console.log('catch:', reason);
});
}
);
export default service
在这里注意解决跨域问题,只需要在后端添加一个跨域中间件即可。
代码如下:
package middleware
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
fmt.Println(method)
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, PATCH, DELETE")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
// 放行所有OPTIONS方法,因为有的模板是要请求两次的
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
// 处理请求
c.Next()
}
}
三.进行mysql数据库连接并创建表
在这里我建了两张表
package Mysql
import (
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
)
type People struct {
ID uint `gorm:"primary_key" json:"id" db:"user_id"`
Account string `json:"account"`
Password string `json:"password" binding:"required" db:"password"`
Signature string `json:"signature"`
Username string `json:"username" binding:"required" db:"username"`
Speaks []Speak `json:"speaks"`
}
type Speak struct {
gorm.Model
PeopleId uint
Content string
Path string
}
var DB *gorm.DB
func Mysql1() (err error){
DB,_ = gorm.Open("mysql","root:password@(localhost)/gin_demo?charset=utf8&parseTime=True&loc=Local")
DB.AutoMigrate(&People{},&Speak{})
return err
}
四.后端登录操作
controller层分为三部分:1.获取参数和参数校验 2.业务处理 3.返回响应
//登录
func LoginHandler(c *gin.Context){
//1.获取参数和参数校验
p := new(models.ParamLogin)
//shouldbindjson只能检测类型对不对,是不是Json格式
if err := c.ShouldBindJSON(&p);err != nil {
//请求参数有误,直接返回响应
c.JSON(http.StatusOK, gin.H{
"msg": "请求参数有误",
})
return
}
//2.业务处理
if err := logic.Login(p);err != nil{
c.JSON(http.StatusOK,gin.H{
"msg":"用户名或密码错误",
"err" : err,
})
return
}
//生成token
tokenString,_ := GenToken(p.Username)
c.JSON(http.StatusOK,gin.H{
"code":2000,
"msg":"success",
"data":gin.H{"token":tokenString},
})
return
//3.返回响应
c.JSON(http.StatusOK,gin.H{
"code":2000,
"msg":"登录成功",
})
}
在登录成功后会生成token,并存储到cookie中,介绍完登录操作后,在进行token的操作
func Login(p *models.ParamLogin) error {
err := Mysql.Login(p)
if err != nil {
return err
}
return nil
}
type PWD struct {
Password string
}
func Login(user *models.ParamLogin) (err error) {
pwd := new(PWD)
err = DB.Table("peoples").Select("password").Where("username=?", user.Username).First(pwd).Error
if err == sql.ErrNoRows{
fmt.Println(err)
return errors.New("用户不存在")
}
if err != nil{
//查询数据库失败
return err
}
//判断密码是否正确
if user.Password != pwd.Password{
err = errors.New("密码错误")
return err
}
return nil
}
注意:从数据库中取数据时:先把前端发送过来的信息取出来赋值给一个结构体和数据库中的数据去做对比,首次登录如何把密码用户名传给后台,传给后台和数据库里的数据做对比,之后生成token,保存到cookie或localstorage中,localstorage是永久储存,cookie可以设置过期时间
接下来展示生成token的方式:
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
type UserInfo struct {
Username string `json:"username"`
Password string `json:"password"`
}
//GenToken生成JWT
func GenToken(username string) (string,error) {
mySigningKey := []byte("woshiadminfangguowoba")
d := MyClaims{
Username: username,
StandardClaims:jwt.StandardClaims{
NotBefore: time.Now().Unix() - 60, //生效时间
ExpiresAt: time.Now().Unix() + 60*60*2, //过期时间
Issuer: "admin", //签发人
},
}
t := jwt.NewWithClaims(jwt.SigningMethodHS256,d)
s,e := t.SignedString(mySigningKey) //对token进行加密
if e!=nil{
fmt.Printf("&s",e)
}
return s,e
}
//注册
func SignupHandler(c *gin.Context) {
p1 := new(models.ParamSignup)
if err := c.ShouldBindJSON(&p1);err != nil{
c.JSON(http.StatusOK,gin.H{
"msg":"请求参数错误",
})
return
}
//2.业务处理1
if err := logic.Signup(p1);err != nil{
c.JSON(http.StatusOK,gin.H{
"err" : err,
"msg" : "注册失败",
"code":8888,
})
return
}
//3.返回响应
c.JSON(http.StatusOK,gin.H{
"code":0,
"msg":"注册成功",
})
}
func Signup(p1 *models.ParamSignup) (err error) {
//1.判断用户存不存在
var exist bool
exist,err = Mysql.CheckUserExist(p1.Username)
if err != nil{
return err
}
if exist{
return errors.New("用户已存在")
}
err = Mysql.InsertUser(p1)
if err != nil{
return err
}
return nil
}
//注册时先检验用户是否已经存在
func CheckUserExist(username string)(bool,error) {
var p2 []People
DB.Find(&p2,"username=?",username)
var count int
count = len(p2)
return count > 0,nil
}
//注册时将用户信息存储到数据库中
func InsertUser(user *models.ParamSignup) (err error) {
DB.Create(&People{
Username: user.Username,
Password: user.Password,
Signature: user.Signature,
Account: user.Account,
})
if err != nil{
return err
}
return nil
}
第一次自己独立的去完成gin框架,其中遇到了很多问题,有很多人都在帮助我,历时很多天完成了这些,收获很大!!!加油!!!