基于 Flask、JWT 和 Vue 的前后端分离架构是一种现代且流行的开发方式,它允许前端和后端独立开发、部署和扩展。以下是一个基本的实现步骤和示例代码,帮助你快速搭建这样的架构。
后端(flask+jwt)
安装依赖
pip install Flask Flask-JWT-Extended
创建 Flask 应用
创建一个简单的 Flask 应用,包括用户注册、登录和受保护的路由。
from flask import Flask,jsonify,request
from flask_jwt_extended import JWTManager,create_access_token,jwt_required,get_jwt_identity
from flask_cors import CORS
app=Flask(__name__)
app.config['JWT_SECRET_KEY']='123456'
jwt=JWTManager(app)
CORS(app)
@app.route('/login',methods=['POST'])
def login():
username=request.json.get('username',None)
password=request.json.get('password',None)
if username!='admin' or password !='password':
return jsonify({"msg":"bad name or password"})
access_token=create_access_token(identity=username)
return jsonify(access_token=access_token)
#受保护的路由
@app.route('/protected',methods=['GET'])
@jwt_required()
def protected():
current_user=get_jwt_identity()
return jsonify(logged_in_as=current_user),200
if __name__=='__main__':
app.run(debug=True)
前端(Vue)
安装 Vue CLI
如果你还没有安装 Vue CLI,可以通过以下命令安装:
npm install -g @vue/cli
创建 Vue 项目
创建一个新的 Vue 项目:
vue create my-vue-app
cd my-vue-app
安装 Axios
Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 node.js。
npm install axios
创建 Vue 组件和服务
在 src
目录下,创建一些基本的组件和服务来处理登录、注册和受保护的请求。
src/services/authService.js
import axios from 'axios';
const API_URL = 'http://127.0.0.1:5000'; // 后端 API 地址
const register = async (username, password) => {
const response = await axios.post(`${API_URL}/register`, { username, password });
return response.data;
};
const login = async (username, password) => {
const response = await axios.post(`${API_URL}/login`, { username, password });
localStorage.setItem('token', response.data.access_token);
return response.data;
};
const getToken = () => {
return localStorage.getItem('token');
};
const setHeaders = (axiosInstance) => {
axiosInstance.interceptors.request.use(
config => {
const token = getToken();
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
};
const axiosInstance = axios.create();
setHeaders(axiosInstance);
const getProtectedResource = async () => {
const response = await axiosInstance.get(`${API_URL}/protected`);
return response.data;
};
export { register, login, getProtectedResource };
创建Login, Proteced组件
src/components/Login.vue
<template>
<div>
<h2>Login</h2>
<form @submit.prevent="loginUser">
<div>
<label for="username">Username:</label>
<input type="text" v-model="username" required />
</div>
<div>
<label for="password">Password:</label>
<input type="password" v-model="password" required />
</div>
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
import { login } from '../services/authService';
export default {
data() {
return {
username: '',
password: ''
};
},
methods: {
async loginUser() {
try {
const response = await login(this.username, this.password);
alert('Login successful');
// 你可以在这里重定向到受保护的页面或其他逻辑
} catch (error) {
alert('Login failed');
}
}
}
};
</script>
src/components/Protected.vue
<template>
<div>
<h2>Protected Resource</h2>
<div v-if="loading">Loading...</div>
<div v-else-if="error">{{ error }}</div>
<div v-else>
<p>Logged in as: {{ user }}</p>
</div>
</div>
</template>
<script>
import { getProtectedResource } from '../services/authService';
export default {
data() {
return {
user: null,
loading: true,
error: null
};
},
created() {
this.fetchProtectedResource();
},
methods: {
async fetchProtectedResource() {
try {
const response = await getProtectedResource();
this.user = response.logged_in_as;
} catch (error) {
this.error = 'Failed to fetch protected resource';
} finally {
this.loading = false;
}
}
}
};
</script>
src/App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
};
</script>
创建路由组件
src/router/index.js
import Vue from 'vue';
import {createRouter, createWebHistory} from 'vue-router'
import Login from '@/components/Login.vue';
import Protected from '@/components/Protected.vue';
Vue.use(Router);
const routes:=[
{
path: '/',
name: 'Login',
component: Login
},
{
path: '/protected',
name: 'Protected',
component: Protected,
meta: { requiresAuth: true }
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 路由守卫,检查token
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
if (to.matched.some(record => record.meta.requiresAuth) && !token) {
next('/login');
} else {
next();
}
})
export default router
前端main代码
src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')