整理一下前端常见的用户验证方式
本文示例:
前端:js原生
后端:express
文件结构:
Http基本验证
http基本验证的流程是发送验证请求时将用户名和密码拼接成字符串,使用base64转义,然后作为header authorization 的值发送,服务端检测request head内是否存在authorization,如果存在获取authorization的值验证用户名和密码,如果不存在就响应一个code 401,同时要相应header WWW-Authenticate:Basic realm=”Secure Area” 这个header和401 code会触发浏览器的默认行为,弹出登录窗口
下面看代码
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
#root{
width: 300px;
display: flex;
flex-direction: column;
}
</style>
<body>
<div id="root">
</div>
</body>
<script>
// 登录
let login = () =>{
let xhr = new XMLHttpRequest()
xhr.open('post', 'http://127.0.0.1:3000/auth', true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.response)
if(JSON.parse(xhr.response).code === 200){
// 如果验证通过显示welcome
root.innerHTML = '<h1>welcom</h1>'
}
}
}
xhr.send()
}
window.onload = function(){
let root = document.getElementById('root')
login()
}
</script>
</html>
后端
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
// html放在public下
app.use(express.static('public'))
//解析post
app.use(express.urlencoded({
extended:true
}));
app.use(express.json())
app.post('/auth', function(req, res){
if(req.headers['authorization']){
//获取authorization header并base64转义
let credsArr = Buffer((req.headers['authorization'].split(' '))[1], 'base64').toString().split(':')
//获取用户名和密码
let user = credsArr[0]
let pwd = credsArr[1]
//验证用户名和密码
if(user === 'hello' && pwd === 'world'){
res.statusCode === 200
res.end(JSON.stringify({code: 200, msg: 'success'}))
}else{
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
res.end()
}
}else{
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
res.end()
}
})
app.listen(3000, function(){
console.log("listen port 3000")
})
工作流程总结:
Cookie-Session验证
常用的验证方式,原理是浏览器每一次会话都会产生一个session对象,该对象可保存在内存或文件内,第一次登录时更具特定的规则产生一个session保存在服务端,然后将 session的id 保存在cookie内返回,下一次请求时携带cookie就可以根据cookie内的id获取到session对象,查看是否需要验证
这里要用到包express-session, 管理session更方便
npm i express-session --save
对于所有的请求,我们指定一个生成session的规则
app.use(session({
secret: 'mySecret', // 用于加密的字符串,可以自定义
saveUninitialized: false, //用户未登录是否生成空session
resave: true,
cookie : {
maxAge : 10000, // cookie的有效期 ms
},
}))
然后可以通过req.session访问到该会话的session( 借助于cookie,用户即使关掉浏览器重新登录也能拿到session )
贴一下demo完整代码
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
#root{
width: 300px;
display: flex;
flex-direction: column;
}
</style>
<body>
<div id="root">
<input class="user" />
<input class="pwd" />
<button class="submit">submit</button>
</div>
</body>
<script>
let login = (user, pwd) =>{
let xhr = new XMLHttpRequest()
xhr.open('post', 'http://127.0.0.1:3000/auth', true)
xhr.setRequestHeader("Content-Type", "application/json")
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
if(JSON.parse(xhr.response).msg === 'success'){
root.innerHTML = '<h1>Login Success</h1>'
}else{
alert('登录失败')
}
}
}
xhr.send(JSON.stringify({user: user, pwd: pwd}))
}
let autoLogin = () =>{
let xhr = new XMLHttpRequest()
xhr.open('post', 'http://127.0.0.1:3000/autoLogin', true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
if(JSON.parse(xhr.response).msg === 'success'){
root.innerHTML = '<h1>Auto Login Success!</h1>'
}
}
}
xhr.send()
}
window.onload = function(){
let user = ''
let pwd = ''
//获取DOM
let root = document.getElementById('root')
let userInput = document.getElementsByClassName('user')[0]
let pwdInput = document.getElementsByClassName('pwd')[0]
let submitBtn = document.getElementsByClassName('submit')[0]
//监听数据变化
userInput.addEventListener('input', function(e){user = e.target.value})
pwdInput.addEventListener('input', function(e){pwd = e.target.value})
submitBtn.addEventListener('click', function(e){login(user, pwd)})
//自动登录
autoLogin()
}
</script>
</html>
后端
const express = require('express')
const session = require('express-session')
const app = express()
app.use(express.static('public'))
//设置session
app.use(session({
secret: 'mySecret',
saveUninitialized: false,
resave: true,
cookie : {
maxAge : 10000,
},
}))
//解析post
app.use(express.urlencoded({
extended:true
}));
app.use(express.json())
//自动登录验证
app.post('/autoLogin', function(req, res){
if(req.session && req.session.isLogin){
res.end(JSON.stringify({code: 200, msg: "success"}))
}else{
res.end(JSON.stringify({code: -1, msg: "error"}))
}
})
app.post('/auth', function(req, res){
if(req.body.user === 'hello' && req.body.pwd === 'world'){
req.session.isLogin = true
res.end(JSON.stringify({code: 200, msg: "success"}))
}else{
res.end(JSON.stringify({code: -1, msg: "error"}))
}
})
app.listen(3000, function(){
console.log("listen port 3000")
})
工作流程总结
token(jwt)验证
这里要用到jsonwebtoken
npm i jsonwebtoken --save
思路和session差不多,只不过将服务端加密签发的token保存在客户端,保存方式由客户端决定,相对于session可以缓解服务器压力
具体的工作流程:
- 客户登录后服务端检查是否携带了token(token是否有效),如有效跳过登录验证过程,无效则需要重新登录
- token无效,用户重新输入用户名和密码
- 服务端验证用户名密码有效性,若有效签发新的token
- 客户端拿到新的token后保存在本地,下次登录时携带
看一下几个核心代码
function setToken(user, pwd){
const token = jwt.sign({user: user, pwd: pwd}, key, {expiresIn:10})
return token
}
jwt.sing用于签发token , 和session的secret一样,签发token需要一个key(不建议token保存密码,这里只是测试一下)
function verifyToken(token){
return jwt.verify(token, key)
}
使用之前的key解析token,返回的对象内包含之前传输的user和pwd
贴一下完整代码
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
#root{
width: 300px;
display: flex;
flex-direction: column;
}
</style>
<body>
<div id="root">
<input class="user" />
<input class="pwd" />
<button class="submit">submit</button>
</div>
</body>
<script>
let login = (user, pwd) =>{
let xhr = new XMLHttpRequest()
xhr.open('post', 'http://127.0.0.1:3000/auth', true)
xhr.setRequestHeader("Content-Type", "application/json")
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
if(JSON.parse(xhr.response).msg === 'success'){
localStorage.setItem('token', JSON.parse(xhr.response).token)
root.innerHTML = '<h1>Login Success</h1>'
}else{
alert('登录失败')
}
}
}
xhr.send(JSON.stringify({user: user, pwd: pwd}))
}
let autoLogin = () =>{
let xhr = new XMLHttpRequest()
xhr.open('post', 'http://127.0.0.1:3000/autoLogin', true)
xhr.setRequestHeader("Content-Type", "application/json")
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
if(JSON.parse(xhr.response).msg === 'success'){
root.innerHTML = '<h1>Auto Login Success!</h1>'
}
}
}
xhr.send(JSON.stringify({token: localStorage.getItem('token')}))
}
window.onload = function(){
let user = ''
let pwd = ''
//获取DOM
let root = document.getElementById('root')
let userInput = document.getElementsByClassName('user')[0]
let pwdInput = document.getElementsByClassName('pwd')[0]
let submitBtn = document.getElementsByClassName('submit')[0]
//监听数据变化
userInput.addEventListener('input', function(e){user = e.target.value})
pwdInput.addEventListener('input', function(e){pwd = e.target.value})
submitBtn.addEventListener('click', function(e){login(user, pwd)})
//自动登录
autoLogin()
}
</script>
</html>
后端
const express = require('express')
const jwt = require('jsonwebtoken')
const app = express()
app.use(express.static('public'))
//解析post
app.use(express.urlencoded({
extended:true
}));
app.use(express.json())
const key = 'myKey'
function setToken(user, pwd){
const token = jwt.sign({user: user, pwd: pwd}, key, {expiresIn:10})
return token
}
function verifyToken(token){
return jwt.verify(token, key, (err) =>{
return null
})
}
//自动登录验证
app.post('/autoLogin', function(req, res){
if(req.body && req.body.token){
let verifyRes = verifyToken(req.body.token)
if(verifyRes && verifyRes.user === 'hello' && verifyRes.pwd === 'world'){
res.end(JSON.stringify({code: 200, msg: 'success'}))
}
}else{
res.end(JSON.stringify({code: -1, msg: 'failed'}))
}
})
app.post('/auth', function(req, res){
console.log(req.body)
if(req.body.user === 'hello' && req.body.pwd === 'world'){
const token = setToken(req.body.user, req.body.pwd)
res.end(JSON.stringify({code: 200, token: token, msg: 'success'}))
}else{
res.end(JSON.stringify({code: -1, msg: "error"}))
}
})
app.listen(3000, function(){
console.log("listen port 3000")
})
工作流程总结