GVKun编程网logo

基于 Vue + Koa2 + MongoDB + Redis 实现一个完整的登录注册(vue登录注册页面demo)

17

本文将介绍基于Vue+Koa2+MongoDB+Redis实现一个完整的登录注册的详细情况,特别是关于vue登录注册页面demo的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这

本文将介绍基于 Vue + Koa2 + MongoDB + Redis 实现一个完整的登录注册的详细情况,特别是关于vue登录注册页面demo的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于Express/MongoDB/登录注册模块、go 语言实现一个简单的登录注册 web 小程序、JQuery+Ajax+Struts2+Hibernate框架整合实现完整的登录注册、Koa & Mongoose & Vue实现前后端分离--06前端登录&注册的知识。

本文目录一览:

基于 Vue + Koa2 + MongoDB + Redis 实现一个完整的登录注册(vue登录注册页面demo)

基于 Vue + Koa2 + MongoDB + Redis 实现一个完整的登录注册(vue登录注册页面demo)

项目地址:https://github.com/caochangkui/vue-element-responsive-demo/tree/login-register

通过 vue-cli3.0 + Element 构建项目前端,Node.js + Koa2 + MongoDB + Redis 实现数据库和接口设计,包括邮箱验证码、用户注册、用户登录、查看删除用户等功能。

1. 技术栈

  • 前端

    • 初始化项目:vue-cli3.0
    • 组件库:Element-ui
    • 路由控制/拦截:Vue-router
    • 状态管理:Vuex
  • 服务端

    • 运行环境:Node.js
    • 后台开发框架:Koa2
    • 路由中间件:Koa-router
    • 发送邮件: nodemailer
  • HTTP通讯

    • 接口请求/拦截:Axios
    • Token认证:jsonwebtoken
  • 数据库

    • MongoDB
    • 数据库操作:Mongoose
    • 缓存工具:Redis

2. 项目依赖:

  "dependencies": { 
    "axios": "^0.18.0",
    "crypto-js": "^3.1.9-1", 
    "element-ui": "^2.4.5",
    "js-cookie": "^2.2.0",
    "jsonwebtoken": "^8.5.0", 
    "koa": "^2.7.0",
    "koa-bodyparser": "^4.2.1",
    "koa-generic-session": "^2.0.1",
    "koa-json": "^2.0.2",
    "koa-redis": "^3.1.3",
    "koa-router": "^7.4.0",
    "mongoose": "^5.4.19",
    "nodemailer": "^5.1.1",
    "nodemon": "^1.18.10", 
    "vue": "^2.5.21", 
    "vue-router": "^3.0.1",
    "vuex": "^3.0.1"
  }

3. 前端实现步骤

3.1 登录注册页面

通过 vue-cli3.0 + Element 构建项目前端页面

登录页(@/view/users/Login.vue):

<img src="https://www.cnblogs.com/images/cnblogs_com/cckui/1069317/o_login.jpg" width="330">

注册页(@/view/users/Register.vue):

发送验证码前需要验证用户名和邮箱,用户名必填,邮箱格式需正确。

<img src="https://www.cnblogs.com/images/cnblogs_com/cckui/1069317/o_register.jpg" width="330">

用户设置页(@/view/users/setting/Setting.vue)

用户登录后,可以进入用户设置页查看用户和删除用户

3.2 Vuex 状态管理

通过 vuex 实现保存或删除用户 token,保存用户名等功能。

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter。

根目录下新建store文件夹,创建modules/user.js:

const user = {
  state: {
    token: localStorage.getItem(''token''),
    username: localStorage.getItem(''username'')
  },

  mutations: {
    BIND_LOGIN: (state, data) => {
      localStorage.setItem(''token'', data)
      state.token = data
    },
    BIND_LOGOUT: (state) => {
      localStorage.removeItem(''token'')
      state.token = null
    },
    SAVE_USER: (state, data) => {
      localStorage.setItem(''username'', data)
      state.username = data
    }
  }
}

export default user

创建文件 getters.js 对数据进行处理输出:

const getters = {
    sidebar: state => state.app.sidebar,
    device: state => state.app.device,
    token: state => state.user.token,
    username: state => state.user.username
  }
export default getters

创建文件 index.js 管理所有状态:

import Vue from ''vue''
import Vuex from ''vuex'' 
import user from ''./modules/user''
import getters from ''./getters''

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: { 
    user
  },
  getters
})

export default store

3.3 路由控制/拦截

路由配置(router.js):

import Vue from ''vue''
import Router from ''vue-router'' 
const Login = () => import(/* webpackChunkName: "users" */ ''@/views/users/Login.vue'')
const Register = () => import(/* webpackChunkName: "users" */ ''@/views/users/Register.vue'')  
const Setting = () => import(/* webpackChunkName: "tables" */ ''@/views/setting/Setting.vue'') 

Vue.use(Router)

const router = new Router({ 
  base: process.env.BASE_URL,
  routes: [
    {
      path: ''/login'',
      name: ''Login'',
      component: Login,
      meta: {
        title: ''登录''
      }
    },
    {
      path: ''/register'',
      name: ''Register'',
      component: Register,
      meta: {
        title: ''注册''
      }
    },
    {
      path: ''/setting'',
      name: ''Setting'',
      component: Setting,
      meta: {
        breadcrumb: ''设置'',
        requireLogin: true
      },
    }
  ]
})

路由拦截:

关于vue 路由拦截参考:https://www.cnblogs.com/cckui/p/10319013.html

// 页面刷新时,重新赋值token
if (localStorage.getItem(''token'')) {
  store.commit(''BIND_LOGIN'', localStorage.getItem(''token''))
}

// 全局导航钩子
router.beforeEach((to, from, next) => {
  if (to.meta.title) { // 路由发生变化修改页面title
    document.title = to.meta.title
  }
  if (to.meta.requireLogin) {
    if (store.getters.token) {
      if (Object.keys(from.query).length === 0) { // 判断路由来源是否有query,处理不是目的跳转的情况
        next()
      } else {
          let redirect = from.query.redirect // 如果来源路由有query
          if (to.path === redirect) { // 避免 next 无限循环
              next()
          } else {
              next({ path: redirect }) // 跳转到目的路由
          }
      }
    } else {
      next({
        path: ''/login'',
        query: { redirect: to.fullPath } // 将跳转的路由path作为参数,登录成功后跳转到该路由
      })
    }
  } else {
    next()
  }
})

export default router

3.4 Axios 封装

封装 Axios

// axios 配置
import axios from ''axios''
import store from ''./store''
import router from ''./router''

//创建 axios 实例
let instance = axios.create({
  timeout: 5000, // 请求超过5秒即超时返回错误
  headers: { ''Content-Type'': ''application/json;charset=UTF-8'' },
})

instance.interceptors.request.use(
  config => {
    if (store.getters.token) { // 若存在token,则每个Http Header都加上token
      config.headers.Authorization = `token ${store.getters.token}`
      console.log(''拿到token'')
    }
    console.log(''request请求配置'', config)
    return config
  },
  err => {
    return Promise.reject(err)
  })

// http response 拦截器
instance.interceptors.response.use(
  response => {
    console.log(''成功响应:'', response)
    return response
  },
  error => {
    if (error.response) {
      switch (error.response.status) {
        case 401:
          // 返回 401 (未授权) 清除 token 并跳转到登录页面
          store.commit(''BIND_LOGOUT'')
          router.replace({
            path: ''/login'',
            query: {
              redirect: router.currentRoute.fullPath
            }
          })
          break
        default:
          console.log(''服务器出错,请稍后重试!'')
          alert(''服务器出错,请稍后重试!'')
      }
    }
    return Promise.reject(error.response) // 返回接口返回的错误信息
  }
)

export default {
  // 发送验证码
  userVerify (data) {
    return instance.post(''/api/verify'', data)
  },
  // 注册
  userRegister (data) {
    return instance.post(''/api/register'', data)
  },
  // 登录
  userLogin (data) {
    return instance.post(''/api/login'', data)
  },
  // 获取用户列表
  getAllUser () {
    return instance.get(''/api/alluser'')
  },
  // 删除用户
  delUser (data) {
    return instance.post(''/api/deluser'', data)
  }
}

4. 服务端和数据库实现

在根目录下创建 server 文件夹,存放服务端和数据库相关代码。

4.1 MongoDB和Redis

创建 /server/dbs/config.js ,进行数据库和邮箱配置

// mongo 连接地址
const dbs = ''mongodb://127.0.0.1:27017/[数据库名称]''

// redis 地址和端口
const redis = {
  get host() {  
    return ''127.0.0.1''
  },
  get port() {
    return 6379
  }
}

// qq邮箱配置
const smtp = {
  get host() {
    return ''smtp.qq.com''
  },
  get user() {
    return ''1********@qq.com'' // qq邮箱名
  },
  get pass() {
    return ''*****************'' // qq邮箱授权码
  },
  // 生成邮箱验证码
  get code() {
    return () => {
      return Math.random()
        .toString(16)
        .slice(2, 6)
        .toUpperCase()
    }
  },
  // 定义验证码过期时间rules,5分钟
  get expire() {
    return () => {
      return new Date().getTime() + 5 * 60 * 1000
    }
  }
}

module.exports = {
  dbs,
  redis,
  smtp
}

使用 qq 邮箱发送验证码,需要在“设置/账户”中打开POP3/SMTP服务和MAP/SMTP服务。

4.2 Mongo 模型

创建 /server/dbs/models/users.js:

// users模型,包括四个字段
const mongoose = require(''mongoose'')
const Schema = mongoose.Schema
const UserSchema = new Schema({
  username: {
    type: String,
    unique: true,
    required: true
  },
  password: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true
  },
  token: {
    type: String,
    required: true
  }
})

module.exports = {
  Users: mongoose.model(''User'', UserSchema)
}

4.3 接口实现

创建 /server/interface/user.js:

const Router = require(''koa-router'')
const Redis = require(''koa-redis'') // key-value存储系统, 存储用户名,验证每个用户名对应的验证码是否正确
const nodeMailer = require(''nodemailer'') // 通过node发送邮件
const User = require(''../dbs/models/users'').Users
const Email = require(''../dbs/config'')

// 创建和验证token, 参考4.4
const createToken = require(''../token/createToken.js'') // 创建token
const checkToken = require(''../token/checkToken.js'') // 验证token


// 创建路由对象
const router = new Router({
  prefix: ''/api'' // 接口的统一前缀
})

// 获取redis的客户端
const Store = new Redis().client

// 接口 - 测试
router.get(''/test'', async ctx => {
  ctx.body = {
    code: 0,
    msg: ''测试'',
  }
})

// 发送验证码 的接口
router.post(''/verify'', async (ctx, next) => {
  const username = ctx.request.body.username
  const saveExpire = await Store.hget(`nodemail:${username}`, ''expire'') // 拿到过期时间

  console.log(ctx.request.body)
  console.log(''当前时间:'', new Date().getTime())
  console.log(''过期时间:'', saveExpire)

  // 检验已存在的验证码是否过期,以限制用户频繁发送验证码
  if (saveExpire && new Date().getTime() - saveExpire < 0) {
    ctx.body = {
      code: -1,
      msg: ''发送过于频繁,请稍后再试''
    }
    return
  }

  // QQ邮箱smtp服务权限校验
  const transporter = nodeMailer.createTransport({
    /**
     *  端口465和587用于电子邮件客户端到电子邮件服务器通信 - 发送电子邮件。
     *  端口465用于smtps SSL加密在任何SMTP级别通信之前自动启动。
     *  端口587用于msa
     */
    host: Email.smtp.host,
    port: 587,
    secure: false, // 为true时监听465端口,为false时监听其他端口
    auth: {
      user: Email.smtp.user,
      pass: Email.smtp.pass
    }
  })

  // 邮箱需要接收的信息
  const ko = {
    code: Email.smtp.code(),
    expire: Email.smtp.expire(),
    email: ctx.request.body.email,
    user: ctx.request.body.username
  }

  // 邮件中需要显示的内容
  const mailOptions = {
    from: `"认证邮件" <${Email.smtp.user}>`, // 邮件来自
    to: ko.email, // 邮件发往
    subject: ''邀请码'', // 邮件主题 标题
    html: `您正在注册****,您的邀请码是${ko.code}` // 邮件内容
  }

  // 执行发送邮件
  await transporter.sendMail(mailOptions, (err, info) => {
    if (err) {
      return console.log(''error'')
    } else {
      Store.hmset(`nodemail:${ko.user}`, ''code'', ko.code, ''expire'', ko.expire, ''email'', ko.email)
    }
  })

  ctx.body = {
    code: 0,
    msg: ''验证码已发送,请注意查收,可能会有延时,有效期5分钟''
  }
})

// 接口 - 注册
router.post(''/register'', async ctx => {
  const { username, password, email, code } = ctx.request.body

  // 验证验证码
  if (code) {
    const saveCode = await Store.hget(`nodemail:${username}`, ''code'') // 拿到已存储的真实的验证码
    const saveExpire = await Store.hget(`nodemail:${username}`, ''expire'') // 过期时间

    console.log(ctx.request.body)
    console.log(''redis中保存的验证码:'', saveCode)
    console.log(''当前时间:'', new Date().getTime())
    console.log(''过期时间:'', saveExpire)

    // 用户提交的验证码是否等于已存的验证码
    if (code === saveCode) {
      if (new Date().getTime() - saveExpire > 0) {
        ctx.body = {
          code: -1,
          msg: ''验证码已过期,请重新申请''
        }
        return
      }
    } else {
      ctx.body = {
        code: -1,
        msg: ''请填写正确的验证码''
      }
      return
    }
  } else {
    ctx.body = {
      code: -1,
      msg: ''请填写验证码''
    }
    return
  }

  // 用户名是否已经被注册
  const user = await User.find({ username })
  if (user.length) {
    ctx.body = {
      code: -1,
      msg: ''该用户名已被注册''
    }
    return
  }
  // 如果用户名未被注册,则写入数据库
  const newUser = await User.create({
    username,
    password,
    email,
    token: createToken(this.username) // 生成一个token 存入数据库
  })

  // 如果用户名被成功写入数据库,则返回注册成功
  if (newUser) {
    ctx.body = {
      code: 0,
      msg: ''注册成功'',
    }
  } else {
    ctx.body = {
      code: -1,
      msg: ''注册失败''
    }
  }
})


// 接口 - 登录
router.post(''/login'', async (ctx, next) => {
  const { username, password } = ctx.request.body

  let doc = await User.findOne({ username })
  if (!doc) { 
    ctx.body = {
      code: -1,
      msg: ''用户名不存在''
    }
  } else if (doc.password !== password) {
    ctx.body = {
      code: -1,
      msg: ''密码错误''
    }
  } else if (doc.password === password) {
    console.log(''密码正确'')
    let token = createToken(username) // 生成token 
    doc.token = token // 更新mongo中对应用户名的token
    try {
      await doc.save() // 更新mongo中对应用户名的token
      ctx.body = {
        code: 0,
        msg: ''登录成功'',
        username,
        token
      }
    } catch (err) {
      ctx.body = {
        code: -1,
        msg: ''登录失败,请重新登录''
      }
    }
  }
})

// 接口 - 获取所有用户 需要验证 token
router.get(''/alluser'', checkToken, async (ctx, next) => {
  try {
    let result = []
    let doc = await User.find({}) 
    doc.map((val, index) => {
      result.push({
        email: val.email,
        username: val.username,
      })
    }) 
    ctx.body = {
      code: 0,
      msg: ''查找成功'',
      result
    }
  } catch (err) {
    ctx.body = {
      code: -1,
      msg: ''查找失败'',
      result: err
    }
  }
})

// 接口 - 删除用户 需要验证 token
router.post(''/deluser'', checkToken, async (ctx, next) => {
  const { username } = ctx.request.body

  try {
    await User.findOneAndRemove({username: username})
    ctx.body = {
      code: 0,
      msg: ''删除成功'',
    }
  } catch (err) {
    ctx.body = {
      code: -1,
      msg: ''删除失败'',
    }
  }
})

module.exports = {
  router
}

上面实现了五个接口:

  • 发送验证码至邮箱: router.post(''/verify'')
  • 注册:router.post(''/register'')
  • 登录:router.post(''/login'')
  • 获取用户列表:router.get(''/alluser'')
  • 删除数据库中的某个用户:router.post(''/deluser'')

分别对应了前面 3.4 中 axios 中的5个请求地址

4.4 JSON Web Token 认证

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。详情参考:http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

分别创建 /server/token/createToken.js 和 /server/token/checkToken.js

// 创建token
const jwt = require(''jsonwebtoken'') 

module.exports = function (id) {
  const token = jwt.sign(
    {
      id: id
    },
    ''cedric1990'',
    {
      expiresIn: ''300s''
    }
  )

  return token
}
// 验证token
const jwt = require(''jsonwebtoken'')

// 检查 token
module.exports = async (ctx, next) => {
  // 检验是否存在 token
  // axios.js 中设置了 authorization
  const authorization = ctx.get(''Authorization'')
  if (authorization === '''') {
    ctx.throw(401, ''no token detected in http headerAuthorization'')
  }

  const token = authorization.split('' '')[1]

  // 检验 token 是否已过期
  try {
    await jwt.verify(token, ''cedric1990'')
  } catch (err) {
    ctx.throw(401, ''invalid token'')
  }

  await next()
}

4.5 服务端入口

根目录创建 server.js:

// server端启动入口
const Koa = require(''koa'')
const app =  new Koa();
const mongoose = require(''mongoose'')
const bodyParser = require(''koa-bodyparser'')
const session = require(''koa-generic-session'')
const Redis = require(''koa-redis'')
const json = require(''koa-json'') // 美化json格式化
const dbConfig = require(''./server/dbs/config'')

const users = require(''./server/interface/user.js'').router

// 一些session和redis相关配置
app.keys = [''keys'', ''keyskeys'']
app.proxy = true
app.use(
  session({ 
    store: new Redis()
  })
)

app.use(bodyParser({
  extendTypes: [''json'', ''form'', ''text'']
}))

app.use(json())

// 连接数据库
mongoose.connect(
  dbConfig.dbs,
  { useNewUrlParser: true }
)

mongoose.set(''useNewUrlParser'', true)
mongoose.set(''useFindAndModify'', false)
mongoose.set(''useCreateIndex'', true)

const db = mongoose.connection
mongoose.Promise = global.Promise // 防止Mongoose: mpromise 错误

db.on(''error'', function () {
    console.log(''数据库连接出错'')
})

db.on(''open'', function () {
    console.log(''数据库连接成功'')
})

// 路由中间件
app.use(users.routes()).use(users.allowedMethods())

app.listen(8888, () => {
  console.log(''This server is running at http://localhost:'' + 8888)
})

5. 跨域处理

详情参考:https://www.cnblogs.com/cckui/p/10331432.html

vue 前端启动端口9527 和 koa 服务端启动端口8888不同,需要做跨域处理,打开vue.config.js:

devServer: {
    port: 9527,
    https: false,
    hotOnly: false,
    proxy: { 
      ''/api'': {
        target: ''http://127.0.0.1:8888/'', // 接口地址
        changeOrigin: true,
        ws: true,
        pathRewrite: {
          ''^/'': ''''
        }
      }
    }
  }

6. 接口对接

import axios from ''../../axios.js''
import CryptoJS from ''crypto-js'' // 用于MD5加密处理

发送验证码:

// 用户名不能为空,并且验证邮箱格式
sendCode() {
  let email = this.ruleForm2.email
  if (this.checkEmail(email) && this.ruleForm2.username) {  
    axios.userVerify({
      username: encodeURIComponent(this.ruleForm2.username),
      email: this.ruleForm2.email
    }).then((res) => {
      if (res.status === 200 && res.data && res.data.code === 0) {
        this.$notify({
          title: ''成功'',
          message: ''验证码发送成功,请注意查收。有效期5分钟'',
          duration: 1000,
          type: ''success''
        })

        let time = 300
        this.buttonText = ''已发送''
        this.isDisabled = true
        if (this.flag) {
          this.flag = false;
          let timer = setInterval(() => {
            time--;
            this.buttonText = time + '' 秒''
            if (time === 0) {
              clearInterval(timer);
              this.buttonText = ''重新获取''
              this.isDisabled = false
              this.flag = true;
            }
          }, 1000)
        }
      } else {
        this.$notify({
          title: ''失败'',
          message: res.data.msg,
          duration: 1000,
          type: ''error''
        })
      }
    })
  }
}

注册:

submitForm(formName) {
  this.$refs[formName].validate(valid => {
    if (valid) {
      axios.userRegister({
        username: encodeURIComponent(this.ruleForm2.username),
        password: CryptoJS.MD5(this.ruleForm2.pass).toString(),
        email: this.ruleForm2.email,
        code: this.ruleForm2.smscode
      }).then((res) => {
        if (res.status === 200) {
          if (res.data && res.data.code === 0) {
            this.$notify({
              title: ''成功'',
              message: ''注册成功。'',
              duration: 2000,
              type: ''success''
            })
            setTimeout(() => {
              this.$router.push({
                path: ''/login''
              })
            }, 500)
          } else {
            this.$notify({
              title: ''错误'',
              message: res.data.msg,
              duration: 2000,
              type: ''error''
            })
          }
        } else {
          this.$notify({
            title: ''错误'',
            message: `服务器请求出错, 错误码${res.status}`,
            duration: 2000,
            type: ''error''
          })
        }
      }) 
    } else {
      console.log("error submit!!");
      return false;
    }
  })
},

登录:

login(formName) {
  this.$refs[formName].validate(valid => {
    if (valid) { 
      axios.userLogin({
        username: window.encodeURIComponent(this.ruleForm.name),
        password: CryptoJS.MD5(this.ruleForm.pass).toString()
      }).then((res) => { 
        if (res.status === 200) {
          if (res.data && res.data.code === 0) {
            this.bindLogin(res.data.token)
            this.saveUser(res.data.username)
            this.$notify({
              title: ''成功'',
              message: ''恭喜,登录成功。'',
              duration: 1000,
              type: ''success''
            })
            setTimeout(() => {
              this.$router.push({
                path: ''/''
              })
            }, 500)
          } else {
            this.$notify({
              title: ''错误'',
              message: res.data.msg,
              duration: 1000,
              type: ''error''
            })
          }
        } else {
          this.$notify({
            title: ''错误'',
            message: ''服务器出错,请稍后重试'',
            duration: 1000,
            type: ''error''
          })
        }
      })
    }
  })
},

7. 启动项目 测试接口

7.1 vue端:

$ npm run serve

7.2 启动mogod:

$ mongod

7.3 启动Redis:

$ redis-server

7.4 启动服务端server.js:

安装 nodemon 热启动辅助工具:

$ npm i nodemon
$ nodemon server.js

8. 项目目录

<img src="https://www.cnblogs.com/images/cnblogs_com/cckui/1069317/o_1552553146441.jpg" width="260">

Express/MongoDB/登录注册模块

Express/MongoDB/登录注册模块

写在前面

为了练习Express.js框架,写了个问答网站。最近打算把登录注册功能添加上。所以另外开了个repo,先单独实现下这个功能。

思路分析

代码写的多了就会发现:不论写什么项目,"思路"是比"动手写代码"要重要的.
理想的情况就是: 花几个小时想思路,想细节.然后噼里啪啦几十分钟写完了.

注册的本质就是:把用户输入的(合法的)帐号密码存到数据库里。
登录的本质就是:检测用户输入的帐号在不在数据库里,然后再检查密码对不对。


当然实际上登录注册没这么简单.验证帐号密码的合法性就不说了.还有:

  • 密码在存进数据库前要加密(MD5)

  • 用Session(cookie)实现所谓的"记住密码"功能(下一次打开网站无需登录)

  • 注册时使用邮箱(短信)进行验证,以后还可以用它进行重置等服务.

  • 第三方登录

  • (差点忘了) 验证码


代码写好了: Github地址


PS:
最后说说怎么学习MongoDB/mongoose.
相比于在项目实战中学习,我倒是觉得还是老实跟着文档一个点一个点的过更好! 这里推荐两个Gitbook(我都看过):
MongoDB入门指南
mongoose入门

go 语言实现一个简单的登录注册 web 小程序

go 语言实现一个简单的登录注册 web 小程序

    最近学习 golang 也有一段时间了,基础差不多学了个大概,因为本人是 java 程序员,所以对 web 更感兴趣。根据《go web 编程》中的例子改编一个更简单的例子,供新手参考,废话不多说,上菜:

    这个例子使用到了 beego 框架和 beedb 框架,如果是 go 新手 beego 和 beedb 得自己去 google 下载安装。

    目录结构:

index.go


package controllers

import (
	"fmt"
	"github.com/astaxie/beego"
	"login/models"
)

type IndexController struct {
	beego.Controller
}

func (index *IndexController) Get() {
	sess := index.StartSession()
	username := sess.Get("username")
	fmt.Println(username)
	if username == nil || username == "" {
		index.TplNames = "index.tpl"
	} else {
		index.TplNames = "success.tpl"
	}

}

func (index *IndexController) Post() {
	sess := index.StartSession()
	var user models.User
	inputs := index.Input()
	//fmt.Println(inputs)
	user.Username = inputs.Get("username")
	user.Pwd = inputs.Get("pwd")
	err := models.ValidateUser(user)
	if err == nil {
		sess.Set("username", user.Username)
		fmt.Println("username:", sess.Get("username"))
		index.TplNames = "success.tpl"
	} else {
		fmt.Println(err)
		index.TplNames = "error.tpl"
	}
}
regist.go



package controllers

import (
	"fmt"
	"github.com/astaxie/beego"
	"login/models"
)

type RegistController struct {
	beego.Controller
}

func (this *RegistController) Get() {
	this.TplNames = "regist.tpl"
}

func (this *RegistController) Post() {
	var user models.User
	inputs := this.Input()
	//fmt.Println(inputs)
	user.Username = inputs.Get("username")
	user.Pwd = inputs.Get("pwd")
	err := models.SaveUser(user)
	if err == nil {
		this.TplNames = "success.tpl"
	} else {
		fmt.Println(err)
		this.TplNames = "error.tpl"
	}
}
models.go



package models

import (
	"database/sql"
	"errors"
	"fmt"
	"github.com/astaxie/beedb"
	_ "github.com/ziutek/mymysql/godrv"
)

type User struct {
	Id       int `PK`
	Username string
	Pwd      string
}

func getLink() beedb.Model {
	db, err := sql.Open("mysql", "root:root@tcp(192.168.1.81:3306)/test_my?charset=utf8")
	if err != nil {
		panic(err)
	}
	orm := beedb.New(db)
	return orm
}

func SaveUser(user User) error {
	orm := getLink()
	fmt.Println(user)
	err := orm.Save(&user)
	return err
}

func ValidateUser(user User) error {
	orm := getLink()
	var u User
	orm.Where("username=? and pwd=?", user.Username, user.Pwd).Find(&u)
	if u.Username == "" {
		return errors.New("用户名或密码错误!")
	}
	return nil
}

main.go


package main

import (
	//"fmt"
	"github.com/astaxie/beego"
	"login/controllers"
)

type MainController struct {
	beego.Controller
}

func main() {
	beego.SessionOn = true
	beego.RegisterController("/", &controllers.IndexController{})
	beego.RegisterController("/regist", &controllers.RegistController{})
	beego.Run()
}


error.tpl



<html>
	<body>
            error
	</body>
</html>
success.tpl



<html>
	<body>
            successed
	</body>
</html>
index.tpl



<html>
	<body>
		<h1>login</h1>
		<form action="/" method="post">
			用户名:<input type="text" name="username"><br>
			密码:<input type="password" name="pwd"><br>
			<input type="submit" value="登录"><br>
			<a href="/regist">注册</a>
		</form>
	</body>
</html>
regist.tpl
<html>
	<body>
		<h1>regist</h1>
		<form action="/regist" method="post">
			用户名:<input type="text" name="username"><br>
			密码:<input type="password" name="pwd"><br>
			<input type="submit" value="注册">
		</form>
	</body>
</html>

数据库用的是 mysql, 建表语句

 

CREATE TABLE user (
id      INT,
username   VARCHAR(32),
pwd VARCHAR(32),
PRIMARY KEY(id) 
);

  程序跑起来是没有问题,但是 session 老是取不到数据,求大神解释!

JQuery+Ajax+Struts2+Hibernate框架整合实现完整的登录注册

JQuery+Ajax+Struts2+Hibernate框架整合实现完整的登录注册

最近在仿造一个书城的网站: http://www.yousuu.com ,UI直接拿来用,前端后端自己写,目前大部分功能已经实现,

就把具体的 登录注册功能 拿来分享一下。PS:又写登录注册会不会被人喷啊=。=

一、开发环境的部署

程序结构:

BootStrap+Ajax+Struts2+Hibernate+MySql

仅供参考:能实现相关功能即可

操作系统:ubuntu 14.10

前端框架:BootStrap   注:此框架只是为了实现用户界面,和具体功能无关

数据库:mysql-5.5 数据库工具:emma

服务器:tomcat 服务器工具:Myeclipse 10(已配置好Struts2和Hibernate环境)

注意:

程序调试过程可能会产生乱码,只需保持所有工具编码方式相同即可。

二、项目文件配置

1、新建Web Project,命名为ROOT

2、配置/WebRoot/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://java.sun.com/xml/ns/javaee"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 id="WebApp_ID" version="3.0">
 <display-name>ROOT</display-name>
 <filter>
  <filter-name>struts2</filter-name>
  <filter-class>
     org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
   </filter-class>
 </filter>
 <filter-mapping>
  <filter-name>struts2</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
 <error-page>
  <error-code>404</error-code>
  <location>/error.jsp</location>
 </error-page>
 <error-page>
  <error-code>500</error-code>
  <location>/error.jsp</location>
 </error-page>
 <welcome-file-list>
  <welcome-file>index.html</welcome-file>
  <welcome-file>index.htm</welcome-file>
  <welcome-file>index.jsp</welcome-file>
  <welcome-file>default.html</welcome-file>
  <welcome-file>default.htm</welcome-file>
  <welcome-file>default.jsp</welcome-file>
 </welcome-file-list>
</web-app>

3 、 配置/src/struts.xml(struts配置文件),其他的action和interceptor被我删了,这点够用了。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
 <package name="default" namespace="/" extends="struts-default">
  <!-- 登录 -->
  <action name="login"method="login"></action>
  <!-- 登出 -->
  <action name="logout"method="logout"></action>
  <!-- 注册 -->
   <action name="register"method="register"></action>
  <!-- 邮件发送 -->
   <action name="sendmail"method="sendmail"></action>
 </package>
</struts> 

4、配置/src/hibernate.cfg.xml(hibernate数据库配置文件),注意倒数第4行有个<mapping />是没有的需要自己创建,将在下一步配置

<?xml version=''1.0'' encoding=''UTF-8''?>
<!DOCTYPE hibernate-configuration PUBLIC
   "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
   "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
 <session-factory>
  <property name="myeclipse.connection.profile">Myeclipse Mysql</property>
  <!--指明JDBC路径、指明数据库名称-->
  <property name="connection.url">jdbc:mysql://localhost:3306/test</property>
  <!--指明数据库账户和密码-->
  <property name="connection.username">root</property>
  <property name="connection.password">root</property>
  <!--指明JDBC驱动-->
  <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
  <!--指明mysql方言-->
  <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="hibernate.current_session_context_class">thread</property>
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="show_sql">true</property>
  <property name="format_sql">true</property>
  <mapping resource="com/hibernate/bookchat.hbm.xml" />
 </session-factory>
</hibernate-configuration>

5、/src下创建com.hibernate包,在该包下创建bookchat.hbm.xml(hibernate对象关系映射文件),并配置

注意<class name="com.hibernate.User" />中的这个User类是自定义的数据库对象类(pojo),将在下一步配置

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <!--指明Bean类名,指明数据库表名-->
 <class name="com.hibernate.User" table="user">
  <id column="id" type="int">
   <generator/>
  </id>
  <!--指明数据库字段名、字段类型-->
  <property name="user_id" column="user_id" type="int" />
  <property name="phone" column="phone" type="int" />
  <property name="email" column="email" type="string" />
  <property name="username" column="username" type="string" />
  <property name="password" column="password" type="string" />
  <property name="icon" column="icon" type="string" />
  <property name="description" column="description" type="string" />
  <property name="followThreadNum" column="followThreadNum" type="int" />
  <property name="followPeopleNum" column="followPeopleNum" type="int" />
  <property name="fansNum" column="fansNum" type="int" />
  <property name="haveMsg" column="haveMsg" type="int" />
 </class>
</hibernate-mapping>

6、/src下的com.hibernate包下创建User类

package com.hibernate;
public class User {
 private int user_id; //对应数据库中user_id
 private int phone; //手机号
 private String email; //邮件
 private String username; //用户名
 private String password; //密码
 private String icon; //用户头像
 private String description;  //自定义描述
 private int followThreadNum; //关注书单数量
 private int followPeopleNum; //关注的人数量
 private int fansNum; //粉丝数量
 private int haveMsg; //当前是否有新消息
 public User() {
  super();
 }
   //这个构造方法在注册时有用
 public User(String email, String username, String password) {
  // 用户内容:username,password,email
  // 系统定义:user_id,icon,followThreadNum,followPeopleNum,fansNum,haveMsg
  // 留空:phone,description,
  this.user_id = 39212;
  // this.phone = phone;
  this.email = email;
  this.username = username;
  this.password = password;
  this.icon = "images/icon.png";
  // this.description = description;
  this.followThreadNum = 0;
  this.followPeopleNum = 0;
  this.fansNum = 0;
  this.haveMsg = 0;
 }
 public int getUser_id() {
  return user_id;
 }
 public void setUser_id(int user_id) {
  this.user_id = user_id;
 }
 public int getPhone() {
  return phone;
 }
 public void setPhone(int phone) {
  this.phone = phone;
 }
 public String getEmail() {
  return email;
 }
 public void setEmail(String email) {
  this.email = email;
 }
 public String getUsername() {
  return username;
 }
 public void setUsername(String username) {
  this.username = username;
 }
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
 public String getIcon() {
  return icon;
 }
 public void setIcon(String icon) {
  this.icon = icon;
 }
 public String getDescription() {
  return description;
 }
 public void setDescription(String description) {
  this.description = description;
 }
 public int getFollowThreadNum() {
  return followThreadNum;
 }
 public void setFollowThreadNum(int followThreadNum) {
  this.followThreadNum = followThreadNum;
 }
 public int getFollowPeopleNum() {
  return followPeopleNum;
 }
 public void setFollowPeopleNum(int followPeopleNum) {
  this.followPeopleNum = followPeopleNum;
 }
 public int getFansNum() {
  return fansNum;
 }
 public void setFansNum(int fansNum) {
  this.fansNum = fansNum;
 }
 public int getHaveMsg() {
  return haveMsg;
 }
 public void setHaveMsg(int haveMsg) {
  this.haveMsg = haveMsg;
 }
}

7、/src下的com.db包下创建CreateTable类,之后Run as - Java Application,查看控制台是否输出了sql语句

package com.db;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
public class CREATTABLEDONOT {
 public static void main(String[] args) {
  // 默认读取hibernate.cfg.xml文件
  Configuration cfg = new Configuration().configure();
  SchemaExport export = new SchemaExport(cfg);
  export.create(true, true);
 }
}

三、检查数据库

1、打开数据库GUI工具,查看test数据库下是否有一个user表,若能打开user表说明之前配置成功。

2、编辑user表:设置字段默认值,可以向表中添加数据。

 

四、网页UI设计

1、我们在struts.xml文件配置中已经埋下伏笔:

<action name="login"method="login"></action>
<action name="logout"method="logout"></action>
<action name="register"method="register"></action>
<action name="sendmail"method="sendmail"></action>

我们可以在网页中请求/login,/logout,/register来访问这三个Action处理类,当然这三个类具体的内容我们还没写,先放着。

2、现在开始思考网页设计需要什么东西...

<1> 首页提供登陆和注册链接

 

<2> 登陆弹出框和注册页面


<3> 登陆/注册成功,登陆和注册消失,显示用户名和退出登陆

 

<4> 我们想达到的效果:登陆/注册成功后显示用户名,登陆失败后动态提示错误详情!

五、JQuery+Ajax设计

1、 主要的JQuery和Ajax代码

(function(window, $) {
 var SOKK = {};
 ys.common = SOKK;
 //邮箱验证
 SOKK.sendmail = function(){
  var email = $("#inputEmail").val().trim();
  if(!checkEmail(email)){
   return false;
  }
  //发送请求
  $.get("/sendmail","email="+email,function(data){
   data = JSON.parse(data);
   tip(data.code);
  })
 }
 //注册
 SOKK.signup = function(form){
  var form = $(form);
  //成功方可继续执行
  if(!checkSignUp(form.find("input")))
   return false;
  //序列化表单,生成JSON对象
  var JStr =form.serialize();
  // var JStr = JSON.stringify(JForm);
  tip(JStr);
  $.post("/register",JStr,function(data){
   data = JSON.parse(data);
   if (data.code == 200) {
    location.reload(); //如何跳转到首页?
   } else {
    tip(data.code);
   }
  })
 };
 // 登录
 SOKK.login = function(form) {
  var form = $(form);
  var input = form.find("input");
  var username=$.trim(input[0].value);
  var password=$.trim(input[1].value);
  if(checkLogin(username,password)){
   return false;
  }
  var dataParam = {};
  dataParam.username = username;
  dataParam.password = password;
  // 这里的dataParam是键值对,但服务器获取的时候是?username=xx&password=xx;
  // 如果使用json传输那么就不能用这种方式而必须用$.ajax,而且json在服务器端还要再解析,
  // 所以在发送请求时,不建议使用json。接受数据可以使用json
  $.post("/login", dataParam, function(data) {
   // json字符串->json对象
   data = JSON.parse(data);
   if (data.code == 200) {
    location.reload();
   } else {
    tip(data.code);
   }
  })
 };
 //登出
 SOKK.logout = function(){
  $.get("/logout", function (data) {
   //json字符串->json对象  
   data = JSON.parse(data);
   if (data.code==200){
    location.reload();
   }
  })
 };
})(window, $)

2、自定义工具代码

// 自定义提示
function tip(info){
 if(isNaN(info)){
  toastr.info(info);
 }else{
  var msg;
  if(info<300){  
   switch(info){
    case 100: msg="加入书架成功!"; break;
    case 101: msg="关注本书成功!"; break;
    case 102: msg="已移动到【正在看】!"; break;
    case 103: msg="已移动到【准备看】!"; break;
    case 104: msg="已移动到【已看完】!"; break;
    case 105: msg="已移动到【回收站】!"; break;
    case 110: msg="验证邮件已发送到你的邮箱!";break;
    case 200: msg="请求成功!"; break;
    case 202: msg="请求已接受,但尚未处理。"; break;
    case 204: msg="请求成功,但无返回内容。"; break;
    default : break;
   }
   toastr.success(msg);
  }else if(info<1000){
   switch(info){
    case 301: msg="请求网页的位置发生改变!"; break;
    case 400: msg="错误请求,请输入正确信息!"; break;
    case 401: msg="非法请求,未授权进入此页面!"; break;
    case 403: msg="拒绝请求!"; break;
    case 404: msg="请求页面不存在!"; break;
    case 408: msg="请求超时!"; break;
    case 500: msg="服务器出错!"; break;
    case 500: msg="服务不可用!"; break;
    case 900: msg="用户名/密码错误,请重新输入"; break;
    case 903: msg="服务器出错,请重试!"; break;
    case 904: msg="服务器无返回信息!"; break;
    case 905: msg="网络出错!"; break;
    case 906: msg="注册失败,请重试!";break;
    case 907: msg="邮箱验证码错误!";break;
    case 908: msg="用户名已存在!";break;
    case 909: msg="邮箱已被注册!";break;
    case 910: msg="验证邮件发送失败!";break;
    default : break;
   }
   toastr.error(msg);
  }else{
   toastr.info(info);
  }
 }
}
//注册检查
function checkSignUp(input){
 var username = $.trim(input[0].value);
 var password1 = $.trim(input[1].value);
 var password2 = $.trim(input[2].value);
 var email = $.trim(input[3].value);
 var emailcode = $.trim(input[4].value);
 for (var i = 0; i < input.length; i++) {
  if(input[i].value.length<=0){
   tip("所有内容不得为空!");
   return false;
  }
 };
 if(username.length<4){
  tip("用户名不得少于4个字符!");
  return false;
 }
 if(password1!==password2){
  tip("两次输入的密码不同!");
  return false;
 }
 if(password1.length<6){
  tip("密码不得少于6个字符!");
  return false;
 }
 return true;
}
function checkLogin(username,password){
 if(!username){
  tip("请输入用户名!");
  return false;
 }
 if(!password){
  tip("请输入密码!");
  return false;
 }
}
function checkEmail(email){
 var reg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/;
 if(email){
  if(reg.test(email)){
   return true;
  }else{
   tip("邮箱地址不符合规范!");
   return false;
  }
 }else{
  tip("邮箱地址不得为空!");
  return false;
 }
}

3、toastr是一个前端非阻塞的提示插件,可以到 http://www.bootcdn.cn/toastr.js/ 下载使用

        

六、Action设计

1、Login.java

package com.action;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.service.BaseService;
import com.service.BaseServiceImpl;
import com.util.OperateJSON;
public class Login extends ActionSupport {
 private static final long serialVersionUID = 4679952956618457478L;
 private String username;
 private String password;
 public void login() {
  HttpServletRequest request = ServletActionContext.getRequest();
  BaseService hs = new BaseServiceImpl();
  OperateJSON oj = new OperateJSON();
  username = request.getParameter("username");
  password = request.getParameter("password");
  System.out.println("用户名:" + username + "--密码:" + password);
  // 登陆返回用户id
  Object obj = hs.login(username, password);
  if (obj != null) {
   System.out.println("用户名密码正确");
   request.getSession().setAttribute("username", username);
   request.getSession().setAttribute("userid", obj);
   System.out.println("用户名" + username + "的Session设置完毕~");
   System.out.println("用户id的Session设置完毕~");
   oj.putCode(200);
  } else {
   System.out.println("用户名密码错误");
   oj.putCode(900);
  }
  oj.send();
 }
}

2、Logout.java

package com.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.util.OperateJSON;
public class Logout extends ActionSupport {
 private static final long serialVersionUID = -6758897982192371466L;
 HttpServletRequest request = ServletActionContext.getRequest();
 HttpServletResponse response = ServletActionContext.getResponse();
 OperateJSON oj = new OperateJSON();
 public void logout() {
  request.getSession().removeAttribute("username");
  request.getSession().invalidate();
  if (request.getSession().getAttribute("username") == null) {
   oj.putCode(200);
  } else {
   oj.putCode(903);
  }
  oj.send();
 }
}

3、Register.java

package com.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.hibernate.User;
import com.opensymphony.xwork2.ActionSupport;
import com.service.BaseService;
import com.service.BaseServiceImpl;
import com.util.OperateJSON;
public class Register extends ActionSupport {
 private static final long serialVersionUID = -3356620731966076779L;
 HttpServletRequest request = ServletActionContext.getRequest();
 HttpServletResponse response = ServletActionContext.getResponse();
 BaseService bs = new BaseServiceImpl();
 OperateJSON oj = new OperateJSON();
 SendMail sm = new SendMail();
 public void register() {
  String username = request.getParameter("username");
  String password1 = request.getParameter("password1");
  String password2 = request.getParameter("password2");
  String password = (password1.equals(password2) ? password1 : null);
  String email = request.getParameter("email");
  String emailcode = request.getParameter("emailcode");
  // 判断用户输入和生成的邮箱验证码是否相同
  if (!(emailcode.equals(sm.getMailCode()))) {
   oj.putCode(907);
   oj.send();
   return;
  }
  // 检测用户名/邮箱是否唯一
  if (!bs.isUnique("User", "username", username)) {
   oj.putCode(908);
   oj.send();
   return;
  }
  if (!bs.isUnique("User", "email", email)) {
   oj.putCode(909);
   oj.send();
   return;
  }
  // 构建User对象
  User user = new User(email, username, password);
  // 建立对象关系映射
  Boolean reged = bs.register(user);
  if (reged) {
   System.out.println("用户注册成功");
   request.getSession().setAttribute("username", username);
   oj.putCode(200);
  } else {
   System.out.println("注册失败");
   oj.putCode(906);
  }
  oj.send();
 }
}

4、 SendMail.java  SMTP协议发送邮件的类,使用前需要导入mail的jar包,同时,还要设置开启发件人邮箱的smtp服务

package com.action;
import java.util.Date;
import java.util.Properties;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.util.OperateJSON;
public class SendMail extends ActionSupport {
  private static final long serialVersionUID = -4724909293302616101L;
  private static String QQ = "392102018"; // qq
  private static String HOST = "qq.com"; // SMTP服务器主机名
  private static String PASS = "xxxxxxxx"; // SMTP服务器密码
  private static String mailCode; // 邮件验证码

  OperateJSON oj = new OperateJSON();
  public void sendmail() {
 HttpServletRequest request = ServletActionContext.getRequest();
 String email = request.getParameter("email");
 System.out.println(email);
 String mailCode = SendMail.setMailCode();
 try {
   beginSend(email, mailCode);
   oj.putCode(110);
 } catch (MessagingException e) {
   oj.putCode(910);
 } finally {
   oj.send();
 }
  }
  public static String setMailCode() {
 mailCode = 100000 + (int) (Math.random() * 900000) + "BC";
 System.out.println(mailCode);
 return mailCode;
  }
  public String getMailCode() {
 return SendMail.mailCode;
  }
  public void beginSend(String email, String mailCode)
   throws MessagingException {
 String mailTo = email; // 收件方mail地址
 String mailTitle = "欢迎您使用书聊网! 立即激活您的账户";
 String mailContent = "<p>尊敬的用户:</p><p>你好!立即激活您的账户,和书聊网会员一起看书交流。要激活您的账户,只需复制下面的验证码到注册页面确认。 </p>"
  + mailCode + "<p>版权所有© 1999 - 2015 BookChat。保留所有权利。</p>";
 // 设置主要信息
 Properties props = new Properties();
 props.put("mail.smtp.host", "smtp." + HOST);
 props.put("mail.smtp.auth", "true");
 Session session = Session.getInstance(props);
 session.setDebug(true);
 // 开启邮件对象
 MimeMessage message = new MimeMessage(session);
 // 设置发件人/收件人/主题/发信时间
 InternetAddress from = new InternetAddress(QQ + "@" + HOST);
 message.setFrom(from);
 InternetAddress to = new InternetAddress(mailTo);
 message.setRecipient(Message.RecipientType.TO, to);
 message.setSubject(mailTitle);
 message.setSentDate(new Date());
 // 设置消息对象内容
 BodyPart mdp = new MimeBodyPart();// 新建一个存放信件内容的BodyPart对象
 mdp.setContent(mailContent, "text/html;charset=utf-8");// 给BodyPart对象设置内容和格式/编码方式
 Multipart mm = new MimeMultipart();// 新建一个MimeMultipart对象用来存放BodyPart对象(事实上可以存放多个)
 mm.addBodyPart(mdp);// 将BodyPart加入到MimeMultipart对象中(可以加入多个BodyPart)
 message.setContent(mm);// 把mm作为消息对象的内容
 message.saveChanges();
 // 开启传输对象
 Transport transport = session.getTransport("smtp");
 transport.connect("smtp." + HOST, QQ, PASS); // 这里的115798090也要修改为您的QQ号码
 transport.sendMessage(message, message.getAllRecipients());
 transport.close();
  }
}

5、OpreateJSON

package com.util;
import java.io.IOException;
import java.io.PrintWriter;
import org.apache.struts2.ServletActionContext;
import net.sf.json.JSONObject;
public class OperateJSON {
 JSONObject json;
 public OperateJSON() {
  json = new JSONObject();
  json.put("code", "");
  json.put("msg", "");
  json.put("data", "");
 }
 public OperateJSON(String str) {
  json = JSONObject.fromObject(str);
 }
 public void put(String key, Object value) {
  json.remove(key);
  json.put(key, value);
 }
 public void putCode(Object value) {
  json.remove("code");
  this.put("code", value);
 }
 public void putMsg(Object value) {
  json.remove("msg");
  this.put("msg", value);
 }
 public void remove(String key) {
  json.remove(key);
 }
 public void send() {
  System.out.println("----------返回的数据是:" + json);
  try {
   PrintWriter out = ServletActionContext.getResponse().getWriter();
   out.print(json);
   out.flush();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

七、Hibernate Dao设计

这块都是一些操作数据库的内容,大家都有自己的风格,仔细一点写就好了。代码太乱,我就不放出来吓人了-.-! 。

八、总结

开始想展示下结果的还是算了,也就那么回事。一个小例子,瑕疵难免,还望大神们指正。

写完了,心情好多了,招聘会去看看也好,找不找工作不重要,重要的是我走在正确的路上,只有依靠自己才是强者...

您可能感兴趣的文章:
  • 防止未登录用户操作—基于struts2拦截器的简单实现
  • Struts2拦截器 关于解决登录的问题
  • 详解Struts2中对未登录jsp页面实现拦截功能
  • Struts2拦截器登录验证实例
  • Struts2开发环境搭建 附简单登录功能实例
  • struts2与cookie 实现自动登录和验证码验证实现代码
  • Java struts2 validate用户登录校验功能实现
  • 使用MyEclipse 开发struts2框架实现登录功能(结构教程)
  • struts2+jquery组合验证注册用户是否存在
  • 基于struts2和hibernate实现登录和注册功能

Koa & Mongoose & Vue实现前后端分离--06前端登录&注册

Koa & Mongoose & Vue实现前后端分离--06前端登录&注册

上节回顾

  • 荷载的解析
  • 服务端注册&登录逻辑
  • 数据库的存储 & 查询

工作内容

  • 初始化前端环境
  • 创建前端路由
  • axios请求接口

准备工作

  • 全局安装依赖 // 以便通过vue/cli初始化项目

├── @vue/cli@4.1.2

├── @vue/cli-init@4.1.2

  • vue init webpack ./ // 先切换到/client目录下

回答初始化问题

  • npm i -S axios // 先切换到/client目录下

页面逻辑

  • npm run start查看页面是否能正常访问localhost:8080

技术选型

自己的项目,没特殊要求,选择自己熟悉的element-uiUI库、scss预编译语言快速搭建页面。

初始化页面

// 更新文件:/client/src/App.vue
<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: ''App''
}
</script>
// 更新文件:/client/src/router/index.js
// 顺便删除文件:/client/src/components/Helloworld.vue
import Vue from ''vue''
import Router from ''vue-router''
import Login from ''@/views/login''

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: ''/'',
      redirect: ''/login''
    },
    {
      path: ''/login'',
      name: ''login'',
      component: Login
    }
  ]
})
// 新建文件: /client/src/views/login/index.vue
<template>
  <div>
    Login
  </div>
</template>

展示效果 //更新不及时,可以重启前端服务
Login

引入element-uiscss

  • 安装依赖:npm i -S element-ui node-scss sass-loader@7 // sass-loader安装7.版本,目前最新版8.编译失败
  • 完整引入Element
// 更新文件:/client/src/main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from ''vue''
import ElementUI from ''element-ui''
import ''element-ui/lib/theme-chalk/index.css''
import App from ''./App''
import router from ''./router''

Vue.config.productionTip = false
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
  el: ''#app'',
  router,
  components: { App },
  template: ''<App/>''
})
  • 测试可用性
// 更新文件:/client/src/views/login/index.vue
<template>
  <div>
    <el-button type="primary">Login</el-button>
    <p>这是scss</p>
  </div>
</template>
<style lang="scss" scoped>
$color: red;
.red {
  color: $color;
}
</style>
  • 测试结果

Element

快速搭建页面

为了省事,直接从Element官网 > 组件 > Form表单,拷贝一份带校验的示例改改

// 更新文件:/client/src/views/login/index.vue
<template>
  <div>
    <el-form :ref="formName":model="form" :rules="rules">
      <el-form-item label="帐号" prop="account">
        <el-input v-model="form.account" placeholder="请输出帐号"></el-input>
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input type="password" v-model="form.password" placeholder="请输出密码"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onLogin">登陆</el-button>
        <el-button type="primary" @click="onRegister">注册</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: ''Login'',
  data () {
    return {
      formName: ''LoginForm'',
      form: {
        account: '''',
        password: ''''
      },
      rules: {
        account: [
          { required: true, message: ''请输入帐号'', trigger: ''blur'' },
          { min: 5, message: ''长度至少5个字符'', trigger: ''blur'' }
        ],
        password: [
          { required: true, message: ''请输入密码'', trigger: ''blur'' },
          { min: 3, message: ''长度至少3个字符'', trigger: ''blur'' }
        ]
      }
    }
  },
  methods: {
    async onLogin () {
      console.log(''login'')
    },
    async onRegister () {
      console.log(''register'')
    }
  }
}
</script>

<style lang="scss" scoped>
@import ''./index.scss'';
</style>
// 新建文件:client/src/views/login/index.scss
.form-login {
  width: 600px;
  margin: 0 auto;
}
  • 页面展示

Login页面展示

添加http请求

在准备工作时,已经npm i -S axios安装axios

// 新建配置文件:client/src/config/http.js
export const BASE_URL = ''http://localhost:3000/''
export const TIMEOUT = 15000
// 新建axios实例文件:client/src/utils/http.js
import axios from ''axios''
import { BASE_URL, TIMEOUT } from ''@/config/http''

const instance = axios.create({
  baseURL: BASE_URL,
  timeout: TIMEOUT,
  validateStatus: function (status) {
    // return status >= 200 && status < 300; // default
    return status >= 200 // 可拦截状态码>=200的请求响应
  }
})

export default instance

注意:axios默认只返回Http Code为2**请求的响应

测试Http请求

//更新文件:server/control/users.js
async function list (ctx) {
  try {
    const users = await userModel.find(); //查出全部用户
    ctx.body = {
      code: ''200'',
      data: users,
      msg: ''查询成功''
    }
  } catch (err) {
    ctx.body = {
      code: ''403'',
      data: null,
      msg: err.message
    }
  }
}
//更新文件:client/src/views/login/index.vue
...
<script>
import http from ''@/utils/http''
...
  methods: {
    async onLogin () {
      console.log(''register'')
    },
    async onRegister () {
      console.log(''register'')
    }
  },
  async created () {
    const res = await http.get(''/users'')
    console.log(res)
  }
...
</script>
...
  • 效果展示

cors
发生跨域请求,这里我们切换到/server/目录下,安装依赖npm i -S koa2-cors(线上的话,可以使用nginx做代理)

// 更新文件:server/app.js
const koa = require(''koa'');
const bodyParser = require(''koa-body'');
const cors = require(''koa2-cors'');

const routes = require(''./router'');

const app = new koa();
app.use(cors());
...

重启后端服务,即可在页面http://localhost:8080/#/login看到请求结果
users请求

过滤返回结果

从返回接口可以看出,请求服务端成功时,只需要res.data即可,使用instance.interceptors对返回数据进行过滤。

// 更新文件:client/src/utils/http.js
...
// Add a response interceptor
instance.interceptors.response.use(
  async res => {
    if (/^20./.test(res.status)) {
      return res.data
    }
    console.log(''------response======='', res)
    return res
  },
  error => {
    return Promise.reject(error)
  }
)

export default instance

请求结果
interceptor

登录逻辑

//更新文件:client/src/views/login/index.vue
...
    async onLogin () {
      try {
        const valid = await this.$refs[this.formName].validate()
        if (valid) {
          const { account, password } = this.form
          const res = await http.post(
            ''/users?action=login'',
            {
              account,
              password,
            }
          )
          console.log(res)
          if (res && res.code === ''200'') {
            this.$router.replace(''/home'')
          } else {
            this.$message({ // 没有使用this.$message.error('''')
              type: ''error'',
              message: res.msg
            })
          }
        }
      } catch (err) {
        console.error(err)
      }
    },
...
  • this.$message({}),而没有使用this.$message.error(),因为发现如果res没有返回的话,会报Element的错误,造成信息误导。

更新路由文件,使登录成功跳转到Home组件

// 更新路由文件:
import Vue from ''vue''
import Router from ''vue-router''
import Login from ''@/views/login''
import Home from ''@/views/home''

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: ''/'',
      redirect: ''/login''
    },
    {
      path: ''/login'',
      name: ''login'',
      component: Login
    },
    {
      path: ''/home'',
      name: ''home'',
      component: Home
    }
  ]
})
// 新建文件:client/src/views/home/index.vue
<template>
  <div>Home</div>
</template>

使用上节通过Postman创建的admin账户登录,结果展示
admin登录
缓慢的动图如下:
login.gif

注册逻辑

//更新文件:client/src/views/login/index.vue
...
    async onRegister () {
      try {
        const valid = await this.$refs[this.formName].validate()
        if (valid) {
          const { account, password } = this.form
          const res = await http.post(
            ''/users?action=register'',
            {
              account,
              password
            }
          )
          if (res.code === ''200'') {
            this.$refs[this.formName].resetFields()
            this.$message({
              type: ''success'',
              message: ''注册成功''
            })
          } else {
            this.$message({
              type: ''error'',
              message: res.msg
            })
          }
        }
      } catch (err) {
        console.error(err)
      }
    }
...

测试失败结果
失败
测试成功结果
成功
可以使用新创建的帐号登录,发现可以成功/查看数据库,是否成功新增用户

参考文档

element-ui
vue-router
axios

关于基于 Vue + Koa2 + MongoDB + Redis 实现一个完整的登录注册vue登录注册页面demo的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于Express/MongoDB/登录注册模块、go 语言实现一个简单的登录注册 web 小程序、JQuery+Ajax+Struts2+Hibernate框架整合实现完整的登录注册、Koa & Mongoose & Vue实现前后端分离--06前端登录&注册的相关知识,请在本站寻找。

本文标签: