1.http无协议
因为HTTP是无状态的协议,也就是说,这个协议是无法记录用户访问状态的,其每次请求都是独立的无关联的,一笔是一笔。而我们的网站都是设计成多个页面的,所在页面跳转过程中我们需要知道用户的状态,尤其是用户登录的状态,这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。
所以,我们每个页面都需要对用户的身份进行认证。为了实现这一功能,用得最多的技术就是浏览器的cookie,我们会把用户登录的信息存放在客户端的cookie里,这样,我们每个页面都从这个cookie里获得用户是否登录的信息,从而达到记录状态,验证用户的目的。但是,你真的会用cookie吗?下面是使用cookie的一些原则。
千万不要在cookie中存放用户的密码。加密的密码都不行 。因为这个密码可以被人获取并尝试离线穷举。所以,你一定不能把用户的密码保存在cookie中。我看到太多的站点这么干了。 关于设计“记住密码” 。一般的设计是当用户勾选了这个功能,系统会生成一个cookie,cookie包括用户名和一个固定的散列值,这个固定的散列值一直使用。这样,你就可以在所有的设备和客户上都可以登录,而且可以有多个用户同时登录。
2.cookie
在cookie中,保存三个东西——用户名,登录序列,登录token。 用户名 :明文存放。 登录序列 :一个被MD5散列过的随机数,仅当强制用户输入口令时更新(如:用户修改了口令)。 登录token :一个被MD5散列过的随机数,仅一个登录session内有效,新的登录session会更新它。 上述三个东西会存在服务器上,服务器的验证用户需要验证客户端cookie里的这三个事。
3.登录逻辑
- (1)首次登录时,后端服务器判断用户账号密码正确之后,根据用户id、用户名、定义好的秘钥、过期时间
生成 token ,返回给前端; - (2)前端拿到后端返回的 token ,
存储在 localStorage 和 Vuex 里 ; - (3)前端每次路由跳转,
判断 localStorage 有无 token ,没有则跳转到登录页,有则请求获取用户信息,改变登录状态; - (4)每次请求接口,
在 Axios 请求头里携带 token ; - (5)后端接口判断请求头有无 token,
没有或者 token 过期 ,返回401 ; 前端得到 401 状态码,重定向到登录页面。
3.前端登录(vue)
3.1在axios请求头里携带
使用 respone 拦截器,对 2xx 状态码以外的结果进行拦截。
if (window.localStorage.getItem('token')) {
Axios.defaults.headers.common['Authorization'] = `Bearer ` + window.localStorage.getItem('token')
}
3.2判断状态码
如果状态码是401,则有可能是 Token 过期,跳转到登录页
instance.interceptors.response.use(
response => {
return response
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
router.replace({
path: 'login',
query: { redirect: router.currentRoute.fullPath }
})
}
}
return Promise.reject(error.response)
}
)
3.3进行路由守卫拦截
localStorage 里有 Token ,就调用获取 userInfo 的方法,并继续执行,如果没有 Token ,调用退出登录的方法,重定向到登录页。
router.beforeEach((to, from, next) => {
let token = window.localStorage.getItem('token')
if (to.meta.requiresAuth) {
if (token) {
store.dispatch('getUser')
next()
} else {
store.dispatch('logOut')
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
} else {
next()
}
})
3.4Vuex部分
3.4.1在 vuex的mutation_types 里定义:
export const LOGIN = 'LOGIN'
export const USERINFO = 'USERINFO'
export const LOGINSTATUS = 'LOGINSTATUS'
3.4.2然后在 mutation 里使用它们:
const mutations = {
[types.LOGIN]: (state, value) => {
state.token = value
},
[types.USERINFO]: (state, info) => {
state.userInfo = info
},
[types.LOGINSTATUS]: (state, bool) => {
state.loginStatus = bool
}
}
3.4.3在request.js里定义接口
export const login = ({ loginUser, loginPassword }) => {
return instance.post('/login', {
username: loginUser,
password: loginPassword
})
}
export const getUserInfo = () => {
return instance.get('/profile')
}
3.4.4在vuex的actions里引入
import * as types from './types'
import { instance, login, getUserInfo } from '../api'
3.4.5定义actios
export default {
toLogin ({ commit }, info) {
return new Promise((resolve, reject) => {
login(info).then(res => {
if (res.status === 200) {
commit(types.LOGIN, res.data.token)
commit(types.LOGINSTATUS, true)
instance.defaults.headers.common['Authorization'] = `Bearer ` + res.data.token
window.localStorage.setItem('token', res.data.token)
resolve(res)
}
}).catch((error) => {
console.log(error)
reject(error)
})
})
},
getUser ({ commit }) {
return new Promise((resolve, reject) => {
getUserInfo().then(res => {
if (res.status === 200) {
commit(types.USERINFO, res.data)
}
}).catch((error) => {
reject(error)
})
})
},
logOut ({ commit }) {
return new Promise((resolve, reject) => {
commit(types.USERINFO, null)
commit(types.LOGINSTATUS, false)
commit(types.LOGIN, '')
window.localStorage.removeItem('token')
})
}
}
4.后端登录逻辑(express)
4.1安装 express-session中间件
npm install express-session
|