文章目录
1.传统的登录鉴权跟基于 Token 的鉴权有什么区别?
- 以 Django 的账号密码登录为例来说明传统的验证鉴权方式是怎么工作的,当我们登录页面输入账号密码提交表单后,会发送请求给服务器,服务器对发送过来的账号密码进行验证鉴权,验证鉴权通过后,把用户信息记录在服务器端( django_session 表中),同时返回给浏览器一个 sessionid 用来唯一标识这个用户,浏览器将 sessionid 保存在 cookie 中,之后浏览器的每次请求都一并将 sessionid 发送给服务器,服务器根据 sessionid 与记录的信息做对比以验证身份
- Token 的鉴权方式就清晰很多了,客户端用自己的账号密码进行登录,服务端验证鉴权,验证鉴权通过生成 Token 返回给客户端,之后客户端每次请求都将 Token 放在 header 里一并发送,服务端收到请求时校验 Token 以确定访问者身份
- session 的主要目的是给无状态的 HTTP 协议添加状态保持,通常在浏览器作为客户端的情况下比较通用。而 Token 的主要目的是为了鉴权,同时又不需要考虑 CSRF 防护以及跨域的问题,所以更多的用在专门给第三方提供 API 的情况下,客户端请求无论是浏览器发起还是其他的程序发起都能很好的支持。所以目前基于 Token 的鉴权机制几乎已经成了前后端分离架构或者对外提供 API 访问的鉴权标准,得到广泛使用
2.drf 使用 jwt
2.1安装
pip install djangorestframework-jwt==1.10.0
安装完成后数据库迁移python manage.py migrate
2.2修改setting
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
INSTALLED_APPS = [
...
'rest_framework.authtoken'
]
# jwt载荷中的有效期设置
JWT_AUTH = {
#token 有效期
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=8),
'JWT_ALLOW_REFRESH': True,
#续期有效期(该设置可在24小时内带未失效的token 进行续期)
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24),
}
2.3添加urls
#urls.py
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
...
url(r'^login/$', obtain_jwt_token),
]
2.4添加管理员账号
python manage.py createsuperuser
2.5测试
vue中
3.vue 使用token
- 用户使用用户名密码来请求服务器
- 服务器进行验证用户的信息
- 服务器通过验证发送给用户一个token
- 客户端存储token,并在每次请求时附送上这个token值(添加在请求头中)
- 服务端验证token值,并返回数据
- 若token值过期,客户端清空token,跳转登录页
3.1获取token,保存到vuex => store 和localStorage
#在store.js中
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
...
token: localStorage.getItem('token') ? localStorage.getItem('token') : ''
},
mutations: {
changeLogin(state, token) {
state.token = token;
localStorage.setItem('token', token);
},
}
})
export default store
#在login.vue中
import { mapMutations } from "vuex";
export default {
data() {
return {
formValidate: {
username: "",
password: "",
},
};
},
methods: {
...mapMutations(["changeLogin"]),
handleSubmit() {
if (
this.formValidate.username == "" ||
this.formValidate.password == ""
) {
alert("账号或密码不能为空");
} else {
this.axios
.post("http://127.0.0.1:8000/login/", this.formValidate)
.then((res) => {
// console.log(res);
this.changeLogin({ token: res.data });
this.$router.push({
name: "pcm",
});
})
.catch(function (error) {
alert("密码错误,重新输入");
});
}
},
},
};
3.2建立router全局守卫
#在router.js中
//全局守卫
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next();
} else {
let token = localStorage.getItem('token');
if (token === 'null' || token === '') {
next('/login');
} else {
next();
}
}
})
3.3 添加请求头
官方文档https://jpadilla.github.io/django-rest-framework-jwt/#usage
这里照着官方文档
带着尖括号,一直报错{"detail":"Error decoding signature."}
,去掉尖括号就好了,我整个人都不好了
// 添加请求拦截器,在请求头中加token
axios.interceptors.request.use(
config => {
if (localStorage.getItem('token')) {
config.headers.Authorization = localStorage.getItem('token');
}
return config;
},
error => {
return Promise.reject(error);
});
3.4当前端拿到状态码为401,就清除token信息,并跳转登录页面
// 拦截401错误,token过期,返回自定义错误
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
if (error.response.status === 401) {
console.log("401")
localStorage.removeItem('token');
router.replace({
path: '/login'
})
return response
}
return Promise.reject(error);
});