三.登陆认证
前言
用户通过登陆认证,识别相关用户,是一个允许用户访问其权限相应的资源的过程
1.登录注册部分
::: demo
vue
<template>
<div>
<el-form
ref="loginForm"
:model="form"
:rules="rules"
label-width="80px"
class="login-box"
>
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input
type="text"
placeholder="请输入账号"
v-model="form.username"
/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
type="password"
placeholder="请输入密码"
v-model="form.password"
auto-complete="new-password"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')"
>登录</el-button
>
</el-form-item>
</el-form>
<el-dialog title="温馨提示" :visible.sync="dialogVisible" width="30%">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false"
>确 定</el-button
>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
form: {
username: "",
password: "",
},
rules: {
username: [
{ required: true, message: "账号不可为空", trigger: "blur" },
],
password: [
{ required: true, message: "密码不可为空", trigger: "blur" },
],
},
dialogVisible: false,
};
},
methods: {
onSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert("登录成功!");
location.reload();
} else {
this.dialogVisible = true;
return false;
}
});
},
},
};
</script>
<style>
.login-box {
border: 1px solid #dcdfe6;
width: 350px;
margin: 180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title {
text-align: center;
margin: 0 auto 40px auto;
color: #303133;
}
</style>:::
2.权限认证部分
1·什么是 jwt
- JSON Web Token(JWT)是目前最流行的身份验证解决方案
解决问题:session 不支持分布式架构,无法支持横向扩展,只能通过数据库来保存会话数据实现共享。如果持久层失败会出现认证失败。
优点:服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。
JWT 包含了三部分
Header 头部
json{ "alg": "HS256", "typ": "JWT" } // algorithm => HMAC SHA256 // type => JWTPayload 负荷、载荷
shJWT 规定了7个官方字段 iss (issuer):签发人 exp (expiration time):过期时间 sub (subject):主题 aud (audience):受众 nbf (Not Before):生效时间 iat (Issued At):签发时间 jti (JWT ID):编号Signature 签名 对前面两部分的签名,防止数据篡改
jsHMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);JWT 作为一个令牌(token),有些场合可能会放到 URL(比如:api.example.com?token=xxx)。Base64 有三个字符
+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被忽略、+替换成-,/替换成_。这就是 Base64URL 算法。
使用方式
HTTP 请求头信息
shAuthorization: Bearer <token>通过 url 传输
shhttp://www.xxx.com/pwa?token=xxxxx
2.服务端返回 token
express 实现
js
let express = require('express')
let app = express()
let bodyParse = require('body-parser')
let jwt = require('jsonwebtoken')
app.use((req,res,next)=>{
res.header('Access-Control-Allow-Origin','http://localhost:8080')
res.header('Access-Control-Allow-Methods','GET,HEAD,OPTIONS,POST,PUT')
res.header('Access-Control-Allow-Methods','Origin,X-Requested-With,Content-Type,Accept,Authorization')
if(req.methods.toLowerCase() === 'options'){
return res.end()
}
next()
})
app.use(bodyParse.json())
let secret = 'zfjg'
app.get('/test',(req,res)=>{
res.end({test:'test'})
})
app.post('/login',(req,res)=>{
let {username} = req.body
if(username === 'admin'){
res.json({
code:0.
username:'admin',
token:jwt.sign({username:'admin'},secret,{
expiresIn:20
})
})
}else{
res.json({
code:1,
data:'用户名不存在'
})
}
})
app.get('/validate',(req,res)=>{
let token = req.headers.authorization
jwt.verify(token,secret,(err,decode)=>{
if(err){
return res.json({
code:1,
data:'token失效了'
})
}else{
res.json({
username:decode.username,
code:0,
token:jwt.sign({
username:'admin'
},secret,{
expiresIn:20
})
})
}
})
})
app.listen(3000)3.路由设置
- home.vue 首页
- profile.vue 个人中心
- login.vue 登陆页面
js
export default new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/profile",
name: "profile",
component: Profile,
meta: { needLogin: true }, // 必须要登录才能访问
},
{
path: "/login",
name: "login",
component: Login,
},
],
});4.axios 封装
js
import axios from "axios";
class FetchData {
constructor() {
this.baseURL =
process.env.NODE_ENV === "development" ? "http://localhost:3000" : "/"; // 请求路径
this.timeout = 3000; // 设置超时时间
}
setInterceptor(instance) {
// 设置拦截器
instance.interceptors.request.use(
(config) => {
config.headers.Authorization = `${localStorage.getItem("token")}`;
return config; // 增加token
},
(err) => {
Promise.reject(err);
}
);
instance.interceptors.response.use(
(res) => res.data,
(err) => {
Promise.reject(err);
}
);
}
request(request) {
const instance = axios.create();
const config = {
baseURL: this.baseURL,
timeout: this.timeout,
...request,
}; // 合并配置
this.setInterceptor(instance);
return instance(config);
}
}
export default new FetchData();5.测试接口
js
export const getTest = () => fetchData.request({ url: "/test" });
export const login = (username) =>
fetchData.request({
url: "/login",
method: "POST",
data: {
username,
},
});
export const validate = () => fetchData.request({ url: "/validate" });6.在 vuex 中发送请求
js
export default new Vuex.Store({
state: {
username: "",
},
mutations: {
setUsername(state, username) {
state.username = username;
},
},
actions: {
async login({ commit }, username) {
const r = await login(username); // 登录成功后返回用户名信息
if (r.token) {
// 如果有返回token说明成功
commit("setUsername", username); // 将用户存入state中
localStorage.setItem("token", r.token); // 将token存放起来
} else {
// 否则返回失败的promise
return Promise.reject(r);
}
},
},
});7.权限认证
js
async validate({ commit }) {
const r = await validate();
if (r.code === 1) {
return false;
}
commit('setUsername', r.username);
localStorage.setItem('token', r.token); // 将token存放起来
return true;
}判断用户访问权限
js
router.beforeEach(async (to, from, next) => {
// 如果不需要校验可以设置白名单
const isLogin = await store.dispatch("validate");
if (isLogin) {
// 如果是登录
if (to.name === "login") {
next("/profile");
} else {
next();
}
} else {
const flag = to.matched.some((item) => item.meta.needLogin);
if (flag) {
next("/login");
} else {
next();
}
}
});