本文将介绍基于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)
- Express/MongoDB/登录注册模块
- go 语言实现一个简单的登录注册 web 小程序
- JQuery+Ajax+Struts2+Hibernate框架整合实现完整的登录注册
- Koa & Mongoose & Vue实现前后端分离--06前端登录&注册
基于 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.js框架,写了个问答网站。最近打算把登录注册功能添加上。所以另外开了个repo,先单独实现下这个功能。
思路分析
代码写的多了就会发现:不论写什么项目,"思路"是比"动手写代码"要重要的.
理想的情况就是: 花几个小时想思路,想细节.然后噼里啪啦几十分钟写完了.
注册的本质就是:把用户输入的(合法的)帐号密码存到数据库里。
登录的本质就是:检测用户输入的帐号在不在数据库里,然后再检查密码对不对。
当然实际上登录注册没这么简单.验证帐号密码的合法性就不说了.还有:
密码在存进数据库前要加密(MD5)
用Session(cookie)实现所谓的"记住密码"功能(下一次打开网站无需登录)
注册时使用邮箱(短信)进行验证,以后还可以用它进行重置等服务.
第三方登录
(差点忘了) 验证码
代码写好了: Github地址
PS:
最后说说怎么学习MongoDB/mongoose.
相比于在项目实战中学习,我倒是觉得还是老实跟着文档一个点一个点的过更好! 这里推荐两个Gitbook(我都看过):
MongoDB入门指南
mongoose入门
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框架整合实现完整的登录注册
最近在仿造一个书城的网站: 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前端登录&注册
上节回顾
- 荷载的解析
- 服务端注册&登录逻辑
- 数据库的存储 & 查询
工作内容
- 初始化前端环境
- 创建前端路由
-
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-ui
UI库、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>
展示效果 //更新不及时,可以重启前端服务
引入element-ui
和scss
- 安装依赖:
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
官网 > 组件 > 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;
}
- 页面展示
添加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>
...
- 效果展示
发生跨域请求,这里我们切换到/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
看到请求结果
过滤返回结果
从返回接口可以看出,请求服务端成功时,只需要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
请求结果
登录逻辑
//更新文件: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
账户登录,结果展示
缓慢的动图如下:
注册逻辑
//更新文件: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前端登录&注册的相关知识,请在本站寻找。
本文标签: