编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Node.js + Express: 用户基于Token的身份校验

wxchong 2024-07-16 10:24:05 开源技术 8 ℃ 0 评论

点击右上方红色按钮关注“web秀”,让你真正秀起来

前言

Todo List项目到这里已经到了非常重要的部分,涉及到用户部分。权限,多人协同操作等,都是非常重要,且复杂的地方,所以这里分为几个章节,一步一步的完成。

本章节主要讲讲用户登录,注册会话部分。

代码请戳:GitHub - javanf/todo-list: 待办任务管理

本次《todo list: Vue待办事项任务管理》,分为一下章节,有兴趣的同学可以持续关注。

第一章: 初识(项目搭建、基本功能组件实现)

第二章: 数据动态化处理(localStorage + Vuex),可新增,可编辑

第三章:待办事项自定义分组

第四章:待办事项添加描述图片等信息

第五章:Node + Express 搭建服务端连接Mysql

第六章:Client端与Server端交互,待办任务入库等

第七章:多人协同处理待办事项,权限管理

第八章:完结:线上发布

初步定义8个章节,实际开发中有可能有所增减。

Client端

新增pages/user目录,里面存放用户相关的页面,如登录,注册,用户信息等。

/user/login.vue

<div id="login" v-if="type=='Login'"> 
 <div class="form"> 
 <h1>用户登录</h1> 
 <div class="c-input"> 
 <label for="">用户名</label> 
 <input type="text" v-model="username" placeholder="请输入用户名"> 
 </div> 
 <div class="c-input"> 
 <label for="">密码</label> 
 <input type="password" v-model="password" placeholder="请输入密码"> 
 </div> 
 <div class="c-input"> 
 <label for=""> </label> 
 <input class="btn" type="button" value="登录" @click="login"> 
 </div> 
 </div> 
</div> 
<div id="register" v-else> 
 <div class="form"> 
 <h1>用户注册</h1> 
 <div class="c-input"> 
 <label for="">用户名</label> 
 <input type="text" v-model="username" placeholder="请输入用户名"> 
 </div> 
 <div class="c-input"> 
 <label for="">密码</label> 
 <input type="password" v-model="password" placeholder="请输入密码"> 
 </div> 
 <div class="c-input"> 
 <label for="">重复密码</label> 
 <input type="password" v-model="password2" placeholder="再次输入密码"> 
 </div> 
 <div class="c-input"> 
 <label for=""> </label> 
 <input class="btn" type="button" value="注册" @click="register"> 
 </div> 
 </div> 
</div>

对应js方法

login () { 
 let vm = this 
 vm.$store.dispatch(types.A_LOGIN, { 
 username: vm.username, 
 password: vm.password 
 }).then((data) => { 
 vm.$router.push({ 
 name: 'TodoList' 
 }) 
 }, (err) => { 
 console.log(err) 
 }) 
}, 
register () { 
 let vm = this 
 vm.$store.dispatch(types.A_REGISTER, { 
 username: vm.username, 
 password: vm.password, 
 password2: vm.password2 
 }).then(() => { 
 vm.$router.push({ 
 name: 'Login', 
 query: { 
 type: 1 
 } 
 }) 
 }) 
}

/store/actions.js

[types.A_REGISTER] ({ commit, dispatch }, params) { 
 http({ 
 method: 'POST', 
 url: '/user/register', 
 json: true, 
 data: params 
 }).then(() => { 
 }) 
}, 
[types.A_LOGIN] ({ commit, dispatch }, params) { 
 return http({ 
 method: 'POST', 
 url: '/user/login', 
 json: true, 
 data: params 
 }).then((resp) => { 
 return Promise.resolve(resp) 
 }, (err) => { 
 return Promise.reject(err) 
 }) 
}

/commons/http.js

// 创建实例时设置配置的默认值 
var instance = axios.create({ 
 baseURL: '/api', 
 headers: { 
 'Content-Type': 'application/json; charset=UTF-8' 
 }, 
 timeout: 60000 // 设置超时时间为1分钟 
}) 
 
// 添加请求拦截器 
instance.interceptors.request.use(function (config) { 
 const headers = config.headers = config.headers || {} 
 // 在发送请求之前做些什么 
 let token = localStorage.getItem('x-token') 
 headers['x-token'] = token 
 if (config.json) { 
 headers['Content-Type'] = 'application/json; charset=UTF-8' 
 delete config.json 
 } 
 return config 
}, function (error) { 
 // 对请求错误做些什么 
 return Promise.reject(error) 
}) 
 
// 添加响应拦截器 
instance.interceptors.response.use(function (response) { 
 // 对响应数据做点什么 
 if (response.headers['x-token']) { 
 localStorage.setItem('x-token', response.headers['x-token']) 
 } 
 return response 
}, function (error) { 
 // 对响应错误做点什么 
 let code = error.response.data.code 
 switch (code) { 
 case 1000: 
 location.href = '/user/login?type=1' 
 break 
 } 
 return Promise.reject(error) 
})

请求拦截器里面,我们增加增加头x-token,响应里面我们也获取x-token,同时拦截错误码,如果是1000,表示token失效,未登录,我们跳转到登录页面。

创建实例我们修改了baseURL,主要解决跨域问题,同时我们修改根目录下方的/config/index.js文件。

/config/index.js 修改proxyTable为下列代码,拦截请求所有带'/api'的请求,target为后台接口地址域名,pathRewrite是修改请求接口地址url正则,将/api修改为空字符串。

proxyTable: { 
 '/api': { 
 target: 'http://localhost:3000', 
 changeOrigin: true, 
 pathRewrite: { 
 '^/api': '' 
 } 
 } 
}

Server端

/server/sql.js

增加2条SQL,登录查询和注册插入。

module.exports = { 
 ... 
 USER_LOGIN: "SELECT * FROM user where username = '${username}' and password = '${password}'", 
 USER_REGISTER: "INSERT INTO user (username, password, ip, date) VALUES('${username}', '${password}', '${ip}', '${date}')" 
}

/server/app.js

安装2个模块,用来做用户签名

npm i express-jwt jsonwebtoken -S
...
app.set('superSecret', 'todolist'); // 设置 app 的密码--用来生成签名的密码
/**
 * 用户登录
 */
app.post('/user/login', (req, res) => {
 const params = req.body;
 console.log(params)
 if(!params.username){
 sendError(res, '用户名称不能为空')
 return
 }
 if(!params.password){
 sendError(res, '密码名称不能为空')
 return
 }
 let username = params.username
 let password = params.password
 let cliendIp = utils.getClientIp(req)
 let csql = eval('`'+sql.USER_LOGIN+'`');
 console.log('[SQL:]', csql);
 query(csql, (err, result, fields) => {
 if (err) {
 console.log('[SELECT ERROR]:', err.message)
 return sendError(res, err.message)
 }
 result = JSON.stringify(result);
 result = JSON.parse(result);
 if(!result.length){
 return sendError(res, '用户信息错误')
 }
 result.map(item=>{
 delete item.password
 })
 let user = result[0];
 // 创建token
 var token = jwt.sign(user, app.get('superSecret'), {
 expiresIn : 60*60*24// 授权时效24小时
 });
 user.token = token;
 // 请求头返回
 res.header('x-token', token);
 res.send(user)
 })
})
/**
 * 用户注册
 */
app.post('/user/register', (req, res) => {
 const params = req.body;
 console.log(params)
 if(!params.username){
 sendError(res, '用户名称不能为空')
 return
 }
 if(!params.password){
 sendError(res, '密码名称不能为空')
 return
 }
 if(params.password !== params.password2){
 sendError(res, '两次密码必须相同')
 return
 }
 let username = params.username
 let password = params.password
 let ip = utils.getClientIp(req)
 let date = moment().format('YYYY-MM-DD HH:mm:ss');
 let csql = eval('`'+sql.USER_REGISTER+'`');
 console.log('[SQL:]', csql);
 query(csql, (err, result, fields) => {
 if (err) {
 console.log('[SELECT ERROR]:', err.message)
 return sendError(res, err.message)
 }
 res.send(result)
 })
})
// apiRoutes的路由接口都还校验token
var apiRoutes = express.Router();
apiRoutes.use(function(req, res, next) {
 var token = req.body.token || req.query.token || req.headers['x-token'];
 if (token) { 
 // 解码 token (验证 secret 和检查有效期(exp))
 jwt.verify(token, app.get('superSecret'), function(err, decoded) { 
 if (err) {
 sendError(res, 'token已过期', 1000) 
 } else {
 // 如果验证通过,在req中写入解密结果
 req.decoded = decoded; 
 next(); //继续下一步路由
 }
 });
 } else {
 // 没有拿到token 返回错误 
 sendError(res, '请传入token')
 }
})
// 获取所有任务
apiRoutes.get('/task/get-task-list', (req, res) => {
 ...
})
...
app.use('/', apiRoutes);

总结

这里用户密码也没有加密操作,直接是明文,登录查询,注册插入也做的比较简单,都没有做过多的校验,主要是示例,希望能给大家带来启发。详细代码后面优化,补充吧。

代码请戳:「链接」

喜欢小编或者觉得小编文章对你有帮助的,可以点击一波关注哦!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表