<template> <el-card style="max-width: 480px" class="center"> <el-form :model="form" label-width="auto"> <el-form-item label="账号"> <el-input v-model="form.employeeId" /> </el-form-item> <el-form-item label="密码"> <el-input v-model="form.password" type="password"/> </el-form-item> <el-form-item> <el-button type="primary" @click="onSubmit" style="width: 100%;">登录</el-button> </el-form-item> </el-form> </el-card> </template> <script setup> import { ElMessage } from 'element-plus' import useUserStore from '@/store/modules/user'; import productApi from '@/api/products' const userStore = useUserStore(); const router = useRouter(); const form = reactive({ employeeId: '', password: '', role:'' }) // 添加一个函数用于调用后台接口 const loginWithBackend = async () => { debugger try { // 假设登录接口是 /login const response = await productApi.selectLogin(form); // 根据响应判断是否登录成功 if (response && response.status === 200 && response.data != "") { // 登录成功返回用户数据 return Promise.resolve(response.data); } else { // 登录失败返回错误信息 return Promise.reject(new Error(response.message || '登录失败')); } } catch (error) { // 网络错误或其他异常 return Promise.reject(new Error('网络错误,请稍后再试')); } } function onSubmit() { if (!form.employeeId || !form.password) { ElMessage.error('账号和密码不能为空'); return; } // 显示加载状态 ElMessage.info('正在登录...'); // 调用后台接口进行登录验证 loginWithBackend(form.employeeId, form.password) .then(userData => { // 登录成功 ElMessage.success('登录成功'); // 存储用户信息到Vuex/Pinia userStore.login(userData); // 跳转到首页 router.push('home'); }) .catch(error => { // 登录失败,显示错误信息 ElMessage.error(error.message); }); } </script> <style scoped lang="scss"> .center { margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } </style> 我想把取来的role,让所有画面都能取到,他是权限
时间: 2025-07-22 16:24:16 AIGC 浏览: 27
<think>我们需要在用户登录成功后,将用户的角色(role)存储在一个全局可访问的地方,以便在所有页面中都能获取到。在Vue 3中,我们可以使用Pinia(推荐)或Vuex来管理全局状态。
步骤:
1. 在Pinia store中存储用户角色(role)。
2. 登录成功后,将返回的用户数据(包括role)存储到Pinia store中。
3. 在其他组件中,通过Pinia store获取role。
下面我们详细说明:
首先,确保你已经创建了一个Pinia store(例如:`@/store/modules/user.js`)来管理用户状态。
修改user store(@/store/modules/user.js):
在store中,我们需要一个状态(state)来存储用户信息,包括role。同时,我们提供一个mutation(或action)来设置用户信息。
示例代码(使用Pinia):
```javascript
// @/store/modules/user.js
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null, // 存储整个用户信息对象,包括role
// 或者可以单独存储role: role: null
}),
getters: {
// 获取用户角色
role: (state) => state.userInfo?.role,
// 或者如果单独存储role,则直接返回state.role
},
actions: {
// 登录成功后的设置用户信息
login(userData) {
this.userInfo = userData; // 存储整个用户信息
// 如果只需要存储role,可以单独存储: this.role = userData.role;
// 但建议存储整个用户信息,以便其他信息也能获取
},
// 退出登录时清空用户信息
logout() {
this.userInfo = null;
}
},
// 持久化存储(可选):使用pinia-plugin-persistedstate插件,将用户状态持久化到localStorage或sessionStorage
persist: {
enabled: true,
strategies: [
{
key: 'user', // 存储的key
storage: localStorage, // 存储的位置
},
],
},
});
```
然后,在登录成功后,调用userStore的login方法存储用户信息(包括role):
在登录组件的onSubmit方法中,我们调用loginWithBackend,在成功回调中调用userStore.login(userData)来存储用户信息。
修改后的登录组件中的onSubmit方法(部分):
```javascript
loginWithBackend()
.then(userData => {
ElMessage.success('登录成功');
// 存储用户信息到Pinia store
userStore.login(userData);
// 跳转到首页
router.push('home');
})
.catch(error => {
ElMessage.error(error.message);
});
```
现在,在任何组件中,我们都可以通过user store获取到role:
```javascript
import { useUserStore } from '@/store/modules/user';
const userStore = useUserStore();
// 获取角色
const role = userStore.role;
// 或者通过计算属性(在模板中使用)
import { computed } from 'vue';
const role = computed(() => userStore.role);
```
在模板中直接使用:
```html
<template>
<div>当前角色:{{ userStore.role }}</div>
</template>
```
注意:如果使用了持久化插件,刷新页面后role仍然存在。
另外,在路由守卫中,我们也可以使用这个role来做权限控制。例如,在路由的meta中设置需要的角色,然后在全局前置守卫中检查。
示例:路由守卫
```javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import { useUserStore } from '@/store/modules/user';
const routes = [
{
path: '/admin',
component: () => import('@/views/AdminPage.vue'),
meta: { requiresAuth: true, roles: ['admin'] } // 需要admin角色
},
// 其他路由...
];
const router = createRouter({
history: createWebHistory(),
routes,
});
router.beforeEach((to, from, next) => {
const userStore = useUserStore();
const role = userStore.role;
// 检查路由是否需要特定角色
if (to.meta.roles) {
// 如果用户没有登录或者角色不匹配
if (!role || !to.meta.roles.includes(role)) {
next({ path: '/login' }); // 跳转到登录页
return;
}
}
next();
});
```
注意:在路由守卫中使用store,需要确保store已经安装并可用。
以上就是在所有画面中获取role的方法,以及如何使用它进行权限控制。
下面我们总结一下代码:
1. 修改user store以存储用户信息(包括role)。
2. 登录成功后,将用户信息存入store。
3. 在其他组件或路由守卫中,通过store获取role。
注意:如果后端返回的数据结构不同,请根据实际情况调整。
例如,如果后端返回的数据结构是:
```json
{
"code": 200,
"message": "登录成功",
"data": {
"employeeId": "1001",
"name": "张三",
"role": "admin"
}
}
```
那么,在loginWithBackend函数中,我们返回的是response.data(即整个响应数据对象),但我们在then中接收的userData应该是response.data.data(即用户数据对象)。所以需要调整:
修改loginWithBackend函数:
```javascript
const loginWithBackend = async () => {
try {
const response = await productApi.selectLogin(form);
if (response && response.status === 200 && response.data) {
// 注意:这里根据后端返回的数据结构,用户数据在response.data.data中
// 但具体要看后端返回的数据结构,如果后端返回的data字段就是用户数据,那么直接返回response.data
// 假设后端返回的数据结构如上述,那么用户数据在response.data.data
// 但是,根据你的代码,你之前判断的是response.data不为空字符串,所以可能是直接返回用户数据对象?
// 需要确认后端返回的数据结构。
// 如果后端返回的是统一格式,如:
// { code:200, message:'成功', data: { ...用户数据 } }
// 那么这里应该返回response.data.data
// 但根据你之前的代码,你判断的是response.data != "",说明response.data就是用户数据?
// 所以这里需要根据实际情况调整。
// 假设你的后端返回的是用户数据对象(没有包裹在code/message/data结构中),那么直接返回response.data
// 如果是有包裹的结构,则返回response.data.data
// 这里我假设后端返回的是有包裹的结构(因为之前你判断了response.status和response.data不为空字符串)
// 但你的代码中写的是response.data != "",而实际上如果返回的是对象,那么不为空字符串,所以可以。
// 但是,为了保险,我们最好和后端约定统一格式,然后在这里处理。
// 例如,如果后端返回的是:
// 状态码200,响应体为:{ code:200, message:'成功', data: userData }
// 那么我们可以这样:
if (response.data.code === 200) {
return Promise.resolve(response.data.data);
} else {
// 如果后端返回的code不是200,说明有错误
return Promise.reject(new Error(response.data.message || '登录失败'));
}
} else {
return Promise.reject(new Error('登录失败,响应数据为空'));
}
} catch (error) {
return Promise.reject(new Error('网络错误,请稍后再试'));
}
}
```
因此,需要根据后端返回的实际数据结构调整loginWithBackend函数的解析逻辑。
最后,关于权限控制,我们还可以使用自定义指令来控制页面元素的显示(根据角色)。
例如,自定义一个v-role指令:
```javascript
// main.js 或 directives.js
app.directive('role', {
mounted(el, binding, vnode) {
const userStore = useUserStore();
const role = userStore.role;
const roles = binding.value; // 指令绑定的值,可以是一个数组,如['admin','manager']
if (!roles.includes(role)) {
// 如果当前用户角色不在允许的角色列表中,则移除元素
el.parentNode && el.parentNode.removeChild(el);
}
}
});
```
在组件中使用:
```html
<button v-role="['admin']">只有admin可以看到的按钮</button>
```
综上所述,我们通过Pinia全局存储role,并在需要的地方使用,同时可以通过路由守卫和自定义指令实现权限控制。
§§相关问题§§:
1. 如何在Vue 3中使用Pinia进行状态管理?
2. 如何实现路由级别的权限控制?
3. 如何自定义指令来实现元素级别的权限控制?
4. 如何持久化存储用户状态(如使用localStorage)?</think># 全局角色权限管理解决方案
在 Vue 应用中实现全局角色权限管理,需要结合状态管理、路由守卫和权限验证组件。下面是完整的解决方案:
## 1. Pinia 状态管理 - 存储用户角色
```javascript
// @/store/modules/user.js
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null, // 存储整个用户对象
roles: [], // 存储用户角色数组
permissions: [] // 存储具体权限列表
}),
getters: {
// 检查用户是否具有特定角色
hasRole: (state) => (role) => {
return state.roles.includes(role);
},
// 检查用户是否具有特定权限
hasPermission: (state) => (permission) => {
return state.permissions.includes(permission);
}
},
actions: {
// 登录成功时设置用户信息
login(userData) {
this.userInfo = userData;
this.roles = userData.roles || []; // 从后端返回的角色数组
this.permissions = userData.permissions || [];
// 持久化存储
localStorage.setItem('userInfo', JSON.stringify(userData));
localStorage.setItem('roles', JSON.stringify(this.roles));
localStorage.setItem('permissions', JSON.stringify(this.permissions));
},
// 退出登录时清空用户信息
logout() {
this.userInfo = null;
this.roles = [];
this.permissions = [];
localStorage.removeItem('userInfo');
localStorage.removeItem('roles');
localStorage.removeItem('permissions');
},
// 初始化时从本地存储加载用户信息
initialize() {
const userInfo = localStorage.getItem('userInfo');
const roles = localStorage.getItem('roles');
const permissions = localStorage.getItem('permissions');
if (userInfo) {
this.userInfo = JSON.parse(userInfo);
this.roles = JSON.parse(roles);
this.permissions = JSON.parse(permissions);
}
}
}
});
```
## 2. 路由守卫 - 实现基于角色的路由控制
```javascript
// @/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import { useUserStore } from '@/store/modules/user';
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/HomeView.vue'),
meta: { requiresAuth: true, roles: ['user', 'admin'] } // 允许的角色
},
{
path: '/admin',
name: 'Admin',
component: () => import('@/views/AdminView.vue'),
meta: { requiresAuth: true, roles: ['admin'] } // 仅管理员可访问
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/LoginView.vue')
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
// 全局前置路由守卫
router.beforeEach(async (to, from, next) => {
const userStore = useUserStore();
// 初始化用户状态
if (!userStore.userInfo) {
userStore.initialize();
}
// 检查路由是否需要认证
if (to.matched.some(record => record.meta.requiresAuth)) {
// 用户未登录
if (!userStore.userInfo) {
next({
path: '/login',
query: { redirect: to.fullPath }
});
return;
}
// 检查用户角色是否满足要求
const requiredRoles = to.meta.roles || [];
if (requiredRoles.length > 0) {
const hasRequiredRole = requiredRoles.some(role =>
userStore.roles.includes(role)
);
if (!hasRequiredRole) {
// 角色权限不足
next({ path: '/unauthorized' });
return;
}
}
}
next();
});
```
## 3. 权限验证组件 - 实现细粒度控制
```vue
<!-- @/components/AuthGuard.vue -->
<script setup>
import { useUserStore } from '@/store/modules/user';
import { computed } from 'vue';
const props = defineProps({
role: {
type: [String, Array],
default: null
},
permission: {
type: [String, Array],
default: null
},
fallback: {
type: Boolean,
default: false
}
});
const userStore = useUserStore();
// 检查用户是否满足权限要求
const hasAccess = computed(() => {
if (props.role) {
const roles = Array.isArray(props.role) ? props.role : [props.role];
return roles.some(role => userStore.hasRole(role));
}
if (props.permission) {
const permissions = Array.isArray(props.permission) ? props.permission : [props.permission];
return permissions.some(perm => userStore.hasPermission(perm));
}
return true; // 没有指定权限要求时默认显示
});
</script>
<template>
<template v-if="fallback">
<slot v-if="hasAccess" name="default"></slot>
<slot v-else name="fallback"></slot>
</template>
<template v-else>
<slot v-if="hasAccess"></slot>
</template>
</template>
```
## 4. 登录组件优化
```vue
<!-- @/views/LoginView.vue -->
<script setup>
import { reactive } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { useUserStore } from '@/store/modules/user';
import productApi from '@/api/products';
const userStore = useUserStore();
const router = useRouter();
const form = reactive({
employeeId: '',
password: ''
});
const loginWithBackend = async () => {
try {
const response = await productApi.selectLogin(form);
// 根据后端返回的数据结构调整
if (response && response.status === 200 && response.data) {
// 假设后端返回的数据结构为:
// {
// id: 1,
// employeeId: 'user123',
// name: '张三',
// roles: ['admin', 'editor'], // 角色数组
// permissions: ['create', 'edit'] // 权限数组
// }
return Promise.resolve(response.data);
} else {
return Promise.reject(new Error(response.message || '登录失败'));
}
} catch (error) {
return Promise.reject(new Error('网络错误,请稍后再试'));
}
}
function onSubmit() {
if (!form.employeeId || !form.password) {
ElMessage.error('账号和密码不能为空');
return;
}
ElMessage.info('正在登录...');
loginWithBackend()
.then(userData => {
ElMessage.success('登录成功');
// 存储用户信息到Pinia store
userStore.login({
...userData,
// 确保角色和权限是数组格式
roles: Array.isArray(userData.roles) ? userData.roles : [userData.role],
permissions: Array.isArray(userData.permissions) ? userData.permissions : []
});
// 跳转到首页或重定向页面
const redirect = router.currentRoute.value.query.redirect || '/';
router.push(redirect);
})
.catch(error => {
ElMessage.error(error.message);
});
}
</script>
```
## 5. 后端接口调整建议
```java
// Java 后端接口调整
@RestController
@RequestMapping("/vue/review")
public class AuthController {
@PostMapping("/select_login")
public ResponseEntity<ApiResponse<UserDTO>> selectLogin(@RequestBody LoginRequest loginRequest) {
try {
// 验证用户
User user = userService.authenticate(loginRequest.getEmployeeId(), loginRequest.getPassword());
if (user != null) {
// 转换为DTO对象,包含角色和权限信息
UserDTO userDTO = new UserDTO();
userDTO.setId(user.getId());
userDTO.setEmployeeId(user.getEmployeeId());
userDTO.setName(user.getName());
userDTO.setRoles(user.getRoles()); // 角色列表
userDTO.setPermissions(user.getPermissions()); // 权限列表
return ResponseEntity.ok(ApiResponse.success(userDTO));
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error(401, "账号或密码错误"));
}
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error(500, "服务器内部错误"));
}
}
}
// DTO类
public class UserDTO {
private Long id;
private String employeeId;
private String name;
private List<String> roles = new ArrayList<>();
private List<String> permissions = new ArrayList<>();
// getters and setters
}
```
## 6. 使用权限验证组件示例
```vue
<template>
<div>
<!-- 基于角色的控制 -->
<AuthGuard :role="'admin'">
<el-button type="danger">管理员操作</el-button>
</AuthGuard>
<!-- 基于权限的控制 -->
<AuthGuard :permission="'delete'">
<el-button type="danger">删除操作</el-button>
</AuthGuard>
<!-- 多角色控制 -->
<AuthGuard :role="['admin', 'manager']">
<el-button type="primary">管理操作</el-button>
</AuthGuard>
<!-- 带回退内容 -->
<AuthGuard :role="'admin'" fallback>
<template #default>
<el-button type="success">管理员可见</el-button>
</template>
<template #fallback>
<el-alleet type="warning">权限不足</el-alleet>
</template>
</AuthGuard>
</div>
</template>
<script setup>
import AuthGuard from '@/components/AuthGuard.vue';
</script>
```
## 7. 权限数据持久化方案
```javascript
// 在应用启动时初始化用户状态
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.use(router);
// 初始化用户状态
const userStore = useUserStore();
userStore.initialize();
app.mount('#app');
```
## 8. 安全增强建议
1. **JWT 认证**:使用 JWT 代替 session 管理认证状态
2. **角色最小化原则**:只赋予用户完成工作所需的最小权限
3. **权限审计**:记录用户的关键操作
4. **定期刷新令牌**:实现访问令牌和刷新令牌机制
5. **服务端权限验证**:客户端验证只是用户体验优化,关键操作必须在服务端验证权限
阅读全文