Django
Session
登录,登出演示
前端 vue3
<template>
<div>
<button v-if="loggedIn" @click="logout">退出</button>
<div v-else>
<input type="text" v-model="username" placeholder="用户名" />
<input type="password" v-model="password" placeholder="密码" />
<button @click="login">登录</button>
</div>
<div v-if="loggedIn">{{ user.username }} [{{ user.email }}]</div>
</div>
</template>
<script>
import { ref } from "vue";
import axios from "axios";
export default {
name: "App",
setup() {
const username = ref("");
const password = ref("");
const loggedIn = ref(false);
const user = ref({
username: "",
email: "",
});
async function login() {
try {
const response = await axios.post("/api/login/", {
username: username.value,
password: password.value,
});
if (response.status === 200) {
loggedIn.value = true;
getUser();
}
} catch (error) {
console.error(error);
}
}
async function getUser() {
try {
const response = await axios.get("/api/user/detail/");
user.value = response.data;
} catch (error) {
console.error(error);
}
}
async function logout() {
try {
await axios.get("/api/logout/");
loggedIn.value = false;
user.value = {
username: "",
email: "",
};
} catch (error) {
console.error(error);
}
}
return {
username,
password,
loggedIn,
user,
login,
getUser,
logout,
};
},
};
</script>
后端
# urls.py
from django.urls import path
from .views import LoginAPIView, user_detail, logout_view
urlpatterns = [
path('login/', LoginAPIView.as_view(), name='login-api'),
path('user/detail/', user_detail, name='user-detail'),
path('logout/', logout_view, name='logout'),
]
# views.py
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse, HttpResponseRedirect
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
class LoginAPIView(APIView):
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
return Response(status=200)
else:
return Response(status=401, data={"message": "Invalid credentials"})
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def user_detail(request):
user = request.user
data = {
'username': user.username,
'email': user.email,
'first_name': user.first_name,
'last_name': user.last_name
}
return JsonResponse(data)
@login_required
def logout_view(request):
logout(request)
return HttpResponseRedirect('/login/')
需要注意的是,在前端代码中,我们通过axios发送HTTP请求;而在后端代码中,我们使用了Django Rest Framework提供的装饰器实现了对登录状态的判断,并且通过permission_classes指定了需要验证auth权限。
JWT
前端
安装必需的库:
npm install axios vue-router vue3-jwt
在您的Vue项目入口文件中(如main.js)添加以下内容以在整个应用程序中使用Vue Router和Vue3-JWT:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { jwt } from 'vue3-jwt'
import axios from "axios"
const app = createApp(App)
app.use(router)
app.use(jwt, {
storage: window.localStorage,
keyName: 'access_token', // 存储JWT的访问令牌关键字
}, axios)
app.mount('#app')
接下来,我们可以编写登录页面组件,并在其中使用Axios来获取有效的JWT令牌:
<template>
<div>
<h2>登录页面</h2>
<form @submit.prevent="login">
<input type="text" v-model="username" placeholder="用户名">
<input type="password" v-model="password" placeholder="密码">
<button type="submit">登录</button>
</form>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const username = ref('')
const password = ref('')
async function login() {
try {
const response = await axios.post('/api/login/', {
username: username.value,
password: password.value
})
const accessToken = response.data.token
$jwt.setAccessToken(accessToken) // 将Access Token存储到localStorage中
$router.push('/') // 跳转到应用程序首页
} catch (error) {
console.error(error)
}
}
return {
username,
password,
login,
}
}
}
</script>
接下来,我们可以创建一个需要身份验证的组件(例如用户信息的详情页面),并使用Vue3-JWT和Axios进行JWT身份验证:
<template>
<div>
<h2>用户详情页</h2>
<p>当前用户</p>
<pre>{{ user }}</pre>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const user = ref(null)
onMounted(async () => {
try {
const response = await axios.get('/api/user/detail/', {
headers: {
Authorization: `Bearer ${$jwt.accessToken}`
}
})
user.value = response.data
} catch (error) {
console.error(error)
}
})
return {
user
}
}
}
</script>
在该示例中,我们在setup函数中使用onMounted钩子函数发送GET请求以获取当前登录用户的详细信息。由于我们已将存储在localStorage中的有效JWT令牌添加到Vue3-JWT实例中,因此我们在Axios请求中自动携带里含有设置好的Authorization头字段作为JWT身份验证标识。
最后,在路由配置文件中将这些组件添加到正确的路径:
import { createRouter, createWebHistory } from 'vue-router'
import LoginView from '@/views/LoginView.vue'
import UserDetailView from '@/views/UserDetailView.vue'
import { requireAuth } from './guards/auth' // 身份验证守卫
const routes = [
{
path: '/login',
component: LoginView
},
{
path: '/',
component: UserDetailView,
meta: { requiresAuth: true } // 将此路由保护起来,需要身份验证
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
router.beforeEach(requireAuth) // 应用程序中所有路由都严格要求身份验证
export default router
在该示例中,我们使用了一个自定义的路由守卫(route guard)来限制只有已经登录过的用户可以访问受保护的页面。如果未登录,则该守卫将用户重定向到登录页面。
后端
以下是使用Django REST Framework (DRF)实现JSON Web Token (JWT)身份验证的示例:
首先,安装必需的库:
pip install djangorestframework-jwt
接下来,在您的Django项目的settings.py文件中添加以下内容:
INSTALLED_APPS = [
# ...其他应用程序
'rest_framework',
'rest_framework_jwt',
]
REST_FRAMEWORK = {
# ...其他配置,比如默认渲染器或解析器等
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 可以添加其他身份验证类,如SessionAuthentication
),
}
JWT_AUTH = {
'JWT_SECRET_KEY': SECRET_KEY, # Django的SECRET_KEY
'JWT_ALGORITHM': 'HS256', # 使用HS256算法签发JWT
'JWT_VERIFY_EXPIRATION': True, # 验证JWT是否过期
# 更多JWT配置,如自定义JWT_PAYLOAD_HANDLER和JWT_RESPONSE_PAYLOAD_HANDLER可以在文档中找到。
}
接下来,让我们编写视图(Views)并使用JWT进行身份验证。例如,我们将创建一个验证当前用户身份的API端点:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
class CurrentUserView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
# 使用IsAuthenticated权限类,只有在有有效JWT时才允许访问该端点。
# 现在,我们可以使用request.user获取当前用户对象。
user = request.user
serialized_user = UserSerializer(user).data
return Response(serialized_user)
最后,为了获取有效的JWT令牌,我们可以编写一个视图来处理登录和生成JWT:
from rest_framework_jwt.views import ObtainJSONWebToken
class LoginView(ObtainJSONWebToken):
# 在这里定义其他自定义逻辑,如额外的验证。
def post(self, request, *args, **kwargs):
# 登录并获取有效的JWT
response = super(LoginView, self).post(request, *args, **kwargs)
# 响应通常返回带有token键的字典。
response.data['user'] = UserSerializer(request.user).data
# 附加该用户的序列化数据
return response
现在,当用户成功登录后,我们将发送有效JWT令牌作为响应。之后,他们可以通过使用该令牌访问需要身份验证的API端点。
Flask
Session
from flask import Flask, request, jsonify, session
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__)
app.secret_key = "thisissecret"
users = {
"user1": {"username": "user1", "password": generate_password_hash("password1"), "email": "user1@example.com"},
"user2": {"username": "user2", "password": generate_password_hash("password2"), "email": "user2@example.com"}
}
@app.route('/api/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
user = None
for u in users.values():
if u['username'] == username:
user = u
break
if user and check_password_hash(user['password'], password):
session['logged_in'] = True
session['user'] = {'username': user['username'], 'email': user['email']}
return jsonify({"message": "login success"})
else:
return jsonify({"message": "Invalid credentials."}), 401
@app.route('/api/user/detail', methods=['GET'])
def user_detail():
if not session.get('logged_in'):
return jsonify({"message": "User not logged in."}), 401
user = session.get('user')
data = {
'username': user['username'],
'email': user['email']
}
return jsonify(data)
@app.route('/api/logout', methods=['POST'])
def logout():
session.pop('logged_in', None)
session.pop('user', None)
return jsonify({"message": "logout success"})
在这个示例中,我们重新定义了三个接口:/api/login、/api/user/detail 和 /api/logout。其中,/api/login 仍然用于接收包含用户名和密码的 POST 请求,并将用户信息存储到 session 中;但返回值从重定向变为了一个 JSON 格式的消息。同理,/api/logout 同时也返回一个 JSON 格式的成功提示。
/api/user/detail 是一个 GET 请求,需要先检查是否已登录(通过读取 session)。如果未登录,则返回错误信息。否则返回当前登录用户的详细信息。
值得注意的是,Flask 的 session 实现默认把数据保存在 cookie 中,因此建议启用 HTTPS 连接以提高安全性。
JWT
下面提供使用 Flask 和 JWT 实现用户登录系统的示例代码。JWT(JSON Web Token)是一种广泛使用的认证标准,可用于在客户端和服务器之间安全地传输信息。
# app.py
from flask import Flask, request, jsonify
from werkzeug.security import generate_password_hash, check_password_hash
import jwt
from functools import wraps
app = Flask(__name__)
app.config['SECRET_KEY'] = 'thisissecret'
users = {
"user1": {"username": "user1", "password": generate_password_hash("password1"), "email": "user1@example.com"},
"user2": {"username": "user2", "password": generate_password_hash("password2"), "email": "user2@example.com"}
}
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'message': 'Token is missing!'}), 401
try:
data = jwt.decode(token.split()[1], app.config['SECRET_KEY'])
except Exception as e:
return jsonify({'message': 'Token is invalid!'}), 401
return f(*args, **kwargs)
return decorated
@app.route('/api/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
user = None
for u in users.values():
if u['username'] == username:
user = u
break
if user and check_password_hash(user['password'], password):
token = jwt.encode({'username': user['username']}, app.config['SECRET_KEY'])
return jsonify({'token': token.decode('UTF-8')})
else:
return jsonify({"message": "Invalid credentials."}), 401
@app.route('/api/user/detail', methods=['GET'])
@token_required
def user_detail():
data = {
'username': request.args.get('username'),
'email': request.args.get('email')
}
return jsonify(data)
在这个示例中,我们首先引入了 JWT 模块,并使用 wraps 装饰器定义了 token_required 装饰器,用于装饰需要认证的视图函数。该装饰器通过检查请求头中的 Authorization 属性,验证客户端是否提供有效的 token。如果未提供或无效,则返回错误响应;否则执行原来需要认证的视图。
/api/login 请求的处理与 Django 示例类似,接收包含用户名和密码的 JSON 数据。验证成功后,使用 JWT 加密用户信息,并将 token 返回给客户端。客户端需在后续每次请求中通过请求头(例如 Authorization: Bearer )携带该 token。
/api/user/detail 是一个 GET 请求,用于返回当前登录用户的详细信息。由于需要认证,我们通过装饰器 @token_required 确保只有提供了有效 token 的请求才能访问该页面。在视图函数内部,我们从请求参数中获取用户名和电子邮件等用户信息,然后返回 JSON 格式的数据。
需要注意的是,本示例中的 JWT 实现方式简单明了,但并未考虑到 token 过期、刷新等诸多细节问题。如果需要深入了解 JWT,建议参考官方文档或相关资料进行学习和实践。同时,生产环境下还需加强对 token 安全性的考虑,例如使用 HTTPS 协议、设置有效期限等。
Express
session
Node.js 和 Express 框架实现类似功能的代码示例:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const session = require('express-session');
const bcrypt = require('bcrypt');
app.use(bodyParser.json());
app.use(session({
secret: 'thisissecret',
resave: false,
saveUninitialized: true
}));
const users = {
"user1": {"username": "user1", "password": bcrypt.hashSync("password1", 10), "email": "user1@example.com"},
"user2": {"username": "user2", "password": bcrypt.hashSync("password2", 10), "email": "user2@example.com"}
};
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
const user = Object.values(users).find(u => u.username === username);
if (user && bcrypt.compareSync(password, user.password)) {
req.session.logged_in = true;
req.session.user = { username: user.username, email: user.email };
return res.status(200).json({ message: 'login success' });
} else {
return res.status(401).json({ message: 'Invalid credentials.' });
}
});
app.get('/api/user/detail', (req, res) => {
if (!req.session.logged_in) {
return res.status(401).json({ message: 'User not logged in.' });
}
const data = {
username: req.session.user.username,
email: req.session.user.email
};
return res.status(200).json(data);
});
app.post('/api/logout', (req, res) => {
req.session.destroy();
return res.status(200).json({ message: 'logout success' });
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Server listening on port ${port}`));
主要使用了 Express 的路由和中间件机制,使用 bcrypt 库进行密码哈希加密和比对。用户登录成功后使用 express-session 记录用户状态,并实现退出登录清除 session 信息的功能。
需要注意的是,同样为了保障安全性,在部署该应用时也需要设置合理的 CORS 配置和 CSRF 防护等安全措施。
JWT
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
app.use(bodyParser.json());
const users = {
"user1": {"username": "user1", "password": bcrypt.hashSync("password1", 10), "email": "user1@example.com"},
"user2": {"username": "user2", "password": bcrypt.hashSync("password2", 10), "email": "user2@example.com"}
};
function token_required(req, res, next) {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ message: 'Token is missing!' });
}
try {
const data = jwt.verify(token.split(' ')[1], process.env.SECRET_KEY || 'thisissecret')
req.user = { username: data.username };
next();
} catch (err) {
console.error(err);
return res.status(401).json({ message: 'Token is invalid!' });
}
}
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
const user = Object.values(users).find(u => u.username === username);
if (user && bcrypt.compareSync(password, user.password)) {
const token = jwt.sign({ username: user.username }, process.env.SECRET_KEY || 'thisissecret');
return res.status(200).json({ token: token });
} else {
return res.status(401).json({ message: 'Invalid credentials.' });
}
});
app.get('/api/user/detail', token_required, (req, res) => {
const data = {
username: req.user.username,
email: users[req.user.username].email
};
return res.status(200).json(data);
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Server listening on port ${port}`));
主要实现了 token_required 中间件,使用 jwt 库进行 token 的签发和验证,并通过 req.user 记录当前用户信息。在登录成功后返回 token 给前端,并在需要鉴权的路由中添加 token_required 中间件,判断请求头中是否带有有效的 token,如果验证通过,则将解码后的用户信息存入 req.user 对象中,并继续执行后续逻辑。
同样,为保障安全性,在部署该应用时需要设置合理的 CORS 配置和 CSRF 防护等安全措施。