feat(auth): 增强用户信息接口返回详细角色和权限数据
- 修改 `/api/auth/userinfo` 接口,增加返回用户真实姓名、角色列表和权限详情 - 更新 UserInfoRes DTO,添加 realName 和 roles 字段 - 在 AuthController 中实现用户详细信息查询逻辑 - 前端页面适配新的用户信息结构,展示真实姓名和角色信息 - 优化前端用户信息加载逻辑,增加加载状态提示 - 更新 API 接口文档,补充详细的响应字段说明
Showing
5 changed files
with
185 additions
and
26 deletions
| ... | @@ -103,7 +103,7 @@ | ... | @@ -103,7 +103,7 @@ |
| 103 | 103 | ||
| 104 | **接口路径:** `GET /api/auth/userinfo` | 104 | **接口路径:** `GET /api/auth/userinfo` |
| 105 | 105 | ||
| 106 | -**功能描述:** 获取当前登录用户的详细信息 | 106 | +**功能描述:** 获取当前登录用户的详细信息,包括用户名、真实姓名、角色列表和权限列表 |
| 107 | 107 | ||
| 108 | **请求头:** `Authorization: Bearer {token}` | 108 | **请求头:** `Authorization: Bearer {token}` |
| 109 | 109 | ||
| ... | @@ -114,11 +114,46 @@ | ... | @@ -114,11 +114,46 @@ |
| 114 | "message": "获取用户信息成功", | 114 | "message": "获取用户信息成功", |
| 115 | "data": { | 115 | "data": { |
| 116 | "username": "admin", | 116 | "username": "admin", |
| 117 | - "authorities": ["ROLE_ADMIN"] | 117 | + "realName": "系统管理员", |
| 118 | + "roles": [ | ||
| 119 | + { | ||
| 120 | + "roleId": 1, | ||
| 121 | + "roleCode": "ADMIN", | ||
| 122 | + "roleName": "系统管理员", | ||
| 123 | + "status": 1, | ||
| 124 | + "statusText": "正常", | ||
| 125 | + "remark": "系统管理员角色", | ||
| 126 | + "createBy": "admin", | ||
| 127 | + "createTime": "2024-01-01T00:00:00", | ||
| 128 | + "updateBy": "admin", | ||
| 129 | + "updateTime": "2024-01-01T00:00:00" | ||
| 130 | + } | ||
| 131 | + ], | ||
| 132 | + "authorities": [ | ||
| 133 | + { | ||
| 134 | + "authority": "ROLE_ADMIN" | ||
| 135 | + } | ||
| 136 | + ] | ||
| 118 | } | 137 | } |
| 119 | } | 138 | } |
| 120 | ``` | 139 | ``` |
| 121 | 140 | ||
| 141 | +**响应字段说明:** | ||
| 142 | +- `username`: 用户名 | ||
| 143 | +- `realName`: 真实姓名 | ||
| 144 | +- `roles`: 用户角色列表 | ||
| 145 | + - `roleId`: 角色ID | ||
| 146 | + - `roleCode`: 角色编码 | ||
| 147 | + - `roleName`: 角色名称 | ||
| 148 | + - `status`: 角色状态(0-停用/1-启用) | ||
| 149 | + - `statusText`: 角色状态文本描述 | ||
| 150 | + - `remark`: 备注 | ||
| 151 | + - `createBy`: 创建者 | ||
| 152 | + - `createTime`: 创建时间 | ||
| 153 | + - `updateBy`: 更新者 | ||
| 154 | + - `updateTime`: 更新时间 | ||
| 155 | +- `authorities`: 用户权限列表(Spring Security权限对象) | ||
| 156 | + | ||
| 122 | ## 2. 用户管理 (SysUserController) | 157 | ## 2. 用户管理 (SysUserController) |
| 123 | 158 | ||
| 124 | ### 2.1 获取用户列表 | 159 | ### 2.1 获取用户列表 | ... | ... |
| ... | @@ -4,6 +4,7 @@ import com.apple.erp.dto.request.LoginReq; | ... | @@ -4,6 +4,7 @@ import com.apple.erp.dto.request.LoginReq; |
| 4 | import com.apple.erp.dto.request.RefreshTokenReq; | 4 | import com.apple.erp.dto.request.RefreshTokenReq; |
| 5 | import com.apple.erp.dto.response.ApiRes; | 5 | import com.apple.erp.dto.response.ApiRes; |
| 6 | import com.apple.erp.dto.response.LoginRes; | 6 | import com.apple.erp.dto.response.LoginRes; |
| 7 | +import com.apple.erp.dto.response.RoleRes; | ||
| 7 | import com.apple.erp.dto.response.UserInfoRes; | 8 | import com.apple.erp.dto.response.UserInfoRes; |
| 8 | import com.apple.erp.entity.SysUser; | 9 | import com.apple.erp.entity.SysUser; |
| 9 | import com.apple.erp.service.SysUserService; | 10 | import com.apple.erp.service.SysUserService; |
| ... | @@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.*; | ... | @@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.*; |
| 23 | 24 | ||
| 24 | import javax.servlet.http.HttpServletRequest; | 25 | import javax.servlet.http.HttpServletRequest; |
| 25 | import javax.validation.Valid; | 26 | import javax.validation.Valid; |
| 27 | +import java.util.List; | ||
| 26 | 28 | ||
| 27 | /** | 29 | /** |
| 28 | * 认证控制器 | 30 | * 认证控制器 |
| ... | @@ -214,9 +216,34 @@ public class AuthController { | ... | @@ -214,9 +216,34 @@ public class AuthController { |
| 214 | if (authentication != null && authentication.isAuthenticated()) { | 216 | if (authentication != null && authentication.isAuthenticated()) { |
| 215 | String username = authentication.getName(); | 217 | String username = authentication.getName(); |
| 216 | 218 | ||
| 217 | - UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities()); | 219 | + try { |
| 218 | - ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo); | 220 | + // 获取用户详细信息 |
| 219 | - return ResponseEntity.ok(response); | 221 | + SysUser user = sysUserService.findByUsername(username); |
| 222 | + if (user != null) { | ||
| 223 | + // 获取用户角色信息 | ||
| 224 | + List<RoleRes> roles = sysUserService.getUserRoles(user.getUserId()); | ||
| 225 | + | ||
| 226 | + UserInfoRes userInfo = new UserInfoRes( | ||
| 227 | + user.getUsername(), | ||
| 228 | + user.getRealName(), | ||
| 229 | + roles, | ||
| 230 | + authentication.getAuthorities() | ||
| 231 | + ); | ||
| 232 | + ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo); | ||
| 233 | + return ResponseEntity.ok(response); | ||
| 234 | + } else { | ||
| 235 | + // 如果找不到用户信息,返回基本信息 | ||
| 236 | + UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities()); | ||
| 237 | + ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo); | ||
| 238 | + return ResponseEntity.ok(response); | ||
| 239 | + } | ||
| 240 | + } catch (Exception e) { | ||
| 241 | + log.error("获取用户详细信息失败: " + e.getMessage(), e); | ||
| 242 | + // 如果获取详细信息失败,返回基本信息 | ||
| 243 | + UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities()); | ||
| 244 | + ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo); | ||
| 245 | + return ResponseEntity.ok(response); | ||
| 246 | + } | ||
| 220 | } else { | 247 | } else { |
| 221 | ApiRes<UserInfoRes> response = ApiRes.error("未认证"); | 248 | ApiRes<UserInfoRes> response = ApiRes.error("未认证"); |
| 222 | return ResponseEntity.status(401).body(response); | 249 | return ResponseEntity.status(401).body(response); | ... | ... |
| ... | @@ -5,6 +5,7 @@ import lombok.Data; | ... | @@ -5,6 +5,7 @@ import lombok.Data; |
| 5 | import org.springframework.security.core.GrantedAuthority; | 5 | import org.springframework.security.core.GrantedAuthority; |
| 6 | 6 | ||
| 7 | import java.util.Collection; | 7 | import java.util.Collection; |
| 8 | +import java.util.List; | ||
| 8 | 9 | ||
| 9 | /** | 10 | /** |
| 10 | * 用户信息响应对象 | 11 | * 用户信息响应对象 |
| ... | @@ -20,6 +21,12 @@ public class UserInfoRes { | ... | @@ -20,6 +21,12 @@ public class UserInfoRes { |
| 20 | @Schema(description = "用户名", example = "admin") | 21 | @Schema(description = "用户名", example = "admin") |
| 21 | private String username; | 22 | private String username; |
| 22 | 23 | ||
| 24 | + @Schema(description = "真实姓名", example = "管理员") | ||
| 25 | + private String realName; | ||
| 26 | + | ||
| 27 | + @Schema(description = "用户角色列表") | ||
| 28 | + private List<RoleRes> roles; | ||
| 29 | + | ||
| 23 | @Schema(description = "用户权限列表") | 30 | @Schema(description = "用户权限列表") |
| 24 | private Collection<? extends GrantedAuthority> authorities; | 31 | private Collection<? extends GrantedAuthority> authorities; |
| 25 | 32 | ||
| ... | @@ -29,4 +36,11 @@ public class UserInfoRes { | ... | @@ -29,4 +36,11 @@ public class UserInfoRes { |
| 29 | this.username = username; | 36 | this.username = username; |
| 30 | this.authorities = authorities; | 37 | this.authorities = authorities; |
| 31 | } | 38 | } |
| 39 | + | ||
| 40 | + public UserInfoRes(String username, String realName, List<RoleRes> roles, Collection<? extends GrantedAuthority> authorities) { | ||
| 41 | + this.username = username; | ||
| 42 | + this.realName = realName; | ||
| 43 | + this.roles = roles; | ||
| 44 | + this.authorities = authorities; | ||
| 45 | + } | ||
| 32 | } | 46 | } | ... | ... |
| ... | @@ -72,7 +72,8 @@ | ... | @@ -72,7 +72,8 @@ |
| 72 | 72 | ||
| 73 | <div class="header-right"> | 73 | <div class="header-right"> |
| 74 | <div class="user-info"> | 74 | <div class="user-info"> |
| 75 | - <span class="welcome-text">欢迎,{{ userInfo?.username || '用户' }}</span> | 75 | + <span class="welcome-text" v-if="userInfoLoading">加载中...</span> |
| 76 | + <span class="welcome-text" v-else>欢迎,{{ userInfo?.username || '未知' }}</span> | ||
| 76 | <div class="user-actions"> | 77 | <div class="user-actions"> |
| 77 | <button class="logout-btn" @click="handleLogout">退出登录</button> | 78 | <button class="logout-btn" @click="handleLogout">退出登录</button> |
| 78 | </div> | 79 | </div> |
| ... | @@ -125,6 +126,7 @@ | ... | @@ -125,6 +126,7 @@ |
| 125 | import { ref, computed, onMounted, watch, nextTick } from 'vue' | 126 | import { ref, computed, onMounted, watch, nextTick } from 'vue' |
| 126 | import { useRouter, useRoute } from 'vue-router' | 127 | import { useRouter, useRoute } from 'vue-router' |
| 127 | import { logoutApi } from '../api/auth' | 128 | import { logoutApi } from '../api/auth' |
| 129 | +import { request } from '../utils/request' | ||
| 128 | 130 | ||
| 129 | const router = useRouter() | 131 | const router = useRouter() |
| 130 | const route = useRoute() | 132 | const route = useRoute() |
| ... | @@ -134,6 +136,26 @@ const sidebarCollapsed = ref(false) | ... | @@ -134,6 +136,26 @@ const sidebarCollapsed = ref(false) |
| 134 | 136 | ||
| 135 | // 用户信息 | 137 | // 用户信息 |
| 136 | const userInfo = ref<any>(null) | 138 | const userInfo = ref<any>(null) |
| 139 | +const userInfoLoading = ref(false) | ||
| 140 | + | ||
| 141 | +// 获取用户信息 | ||
| 142 | +const fetchUserInfo = async () => { | ||
| 143 | + try { | ||
| 144 | + userInfoLoading.value = true | ||
| 145 | + const response = await request.get('/api/auth/userinfo') | ||
| 146 | + userInfo.value = response | ||
| 147 | + console.log('顶部栏用户信息:', response) | ||
| 148 | + } catch (error) { | ||
| 149 | + console.error('获取用户信息失败:', error) | ||
| 150 | + // 如果获取失败,尝试从本地存储获取 | ||
| 151 | + const storedUserInfo = localStorage.getItem('userInfo') | ||
| 152 | + if (storedUserInfo) { | ||
| 153 | + userInfo.value = JSON.parse(storedUserInfo) | ||
| 154 | + } | ||
| 155 | + } finally { | ||
| 156 | + userInfoLoading.value = false | ||
| 157 | + } | ||
| 158 | +} | ||
| 137 | 159 | ||
| 138 | // 标签页容器引用 | 160 | // 标签页容器引用 |
| 139 | const tabContainer = ref<HTMLElement>() | 161 | const tabContainer = ref<HTMLElement>() |
| ... | @@ -371,9 +393,17 @@ watch(() => route.path, (newPath) => { | ... | @@ -371,9 +393,17 @@ watch(() => route.path, (newPath) => { |
| 371 | 393 | ||
| 372 | // 组件挂载时获取用户信息 | 394 | // 组件挂载时获取用户信息 |
| 373 | onMounted(() => { | 395 | onMounted(() => { |
| 374 | - const storedUserInfo = localStorage.getItem('userInfo') | 396 | + // 检查是否已登录 |
| 375 | - if (storedUserInfo) { | 397 | + const token = localStorage.getItem('token') |
| 376 | - userInfo.value = JSON.parse(storedUserInfo) | 398 | + if (token) { |
| 399 | + // 调用API获取用户信息 | ||
| 400 | + fetchUserInfo() | ||
| 401 | + } else { | ||
| 402 | + // 如果没有token,尝试从本地存储获取 | ||
| 403 | + const storedUserInfo = localStorage.getItem('userInfo') | ||
| 404 | + if (storedUserInfo) { | ||
| 405 | + userInfo.value = JSON.parse(storedUserInfo) | ||
| 406 | + } | ||
| 377 | } | 407 | } |
| 378 | }) | 408 | }) |
| 379 | </script> | 409 | </script> | ... | ... |
| ... | @@ -2,7 +2,8 @@ | ... | @@ -2,7 +2,8 @@ |
| 2 | <div class="dashboard-container"> | 2 | <div class="dashboard-container"> |
| 3 | <div class="dashboard-header"> | 3 | <div class="dashboard-header"> |
| 4 | <h2>系统概览</h2> | 4 | <h2>系统概览</h2> |
| 5 | - <p>欢迎回来,{{ userInfo?.username || '用户' }}!</p> | 5 | + <p v-if="loading">正在加载用户信息...</p> |
| 6 | + <p v-else>欢迎回来,{{ userInfo?.username || '未知' }}!</p> | ||
| 6 | </div> | 7 | </div> |
| 7 | 8 | ||
| 8 | <div class="dashboard-stats"> | 9 | <div class="dashboard-stats"> |
| ... | @@ -43,23 +44,25 @@ | ... | @@ -43,23 +44,25 @@ |
| 43 | <div class="dashboard-card"> | 44 | <div class="dashboard-card"> |
| 44 | <h3>系统信息</h3> | 45 | <h3>系统信息</h3> |
| 45 | <div class="info-grid"> | 46 | <div class="info-grid"> |
| 46 | - <div class="info-item"> | 47 | + <div class="info-item"> |
| 47 | <label>用户名:</label> | 48 | <label>用户名:</label> |
| 48 | - <span>{{ userInfo?.username || '未知' }}</span> | 49 | + <span v-if="loading">加载中...</span> |
| 49 | - </div> | 50 | + <span v-else>{{ userInfo?.username || '未知' }}</span> |
| 50 | - <div class="info-item"> | 51 | + </div> |
| 52 | + <div class="info-item"> | ||
| 51 | <label>角色:</label> | 53 | <label>角色:</label> |
| 52 | - <span>{{ userInfo?.roles?.[0]?.roleName || '普通用户' }}</span> | 54 | + <span v-if="loading">加载中...</span> |
| 53 | - </div> | 55 | + <span v-else>{{ getRoleName(userInfo) || '普通用户' }}</span> |
| 54 | - <div class="info-item"> | 56 | + </div> |
| 57 | + <div class="info-item"> | ||
| 55 | <label>登录时间:</label> | 58 | <label>登录时间:</label> |
| 56 | <span>{{ currentTime }}</span> | 59 | <span>{{ currentTime }}</span> |
| 57 | - </div> | 60 | + </div> |
| 58 | - <div class="info-item"> | 61 | + <div class="info-item"> |
| 59 | <label>系统版本:</label> | 62 | <label>系统版本:</label> |
| 60 | <span>v1.0.0</span> | 63 | <span>v1.0.0</span> |
| 61 | </div> | 64 | </div> |
| 62 | - </div> | 65 | + </div> |
| 63 | </div> | 66 | </div> |
| 64 | 67 | ||
| 65 | <div class="dashboard-card"> | 68 | <div class="dashboard-card"> |
| ... | @@ -78,26 +81,76 @@ | ... | @@ -78,26 +81,76 @@ |
| 78 | <script setup lang="ts"> | 81 | <script setup lang="ts"> |
| 79 | import { ref, onMounted } from 'vue' | 82 | import { ref, onMounted } from 'vue' |
| 80 | import { useRouter } from 'vue-router' | 83 | import { useRouter } from 'vue-router' |
| 84 | +import { request } from '@/utils/request' | ||
| 81 | 85 | ||
| 82 | const router = useRouter() | 86 | const router = useRouter() |
| 83 | const currentTime = ref('') | 87 | const currentTime = ref('') |
| 84 | const userInfo = ref<any>(null) | 88 | const userInfo = ref<any>(null) |
| 89 | +const loading = ref(false) | ||
| 90 | + | ||
| 91 | +// 获取角色名称 | ||
| 92 | +const getRoleName = (userInfo: any) => { | ||
| 93 | + // 优先使用roles字段中的角色信息 | ||
| 94 | + if (userInfo?.roles && Array.isArray(userInfo.roles) && userInfo.roles.length > 0) { | ||
| 95 | + return userInfo.roles[0].roleName || '普通用户' | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + // 如果没有roles字段,回退到authorities | ||
| 99 | + const authorities = userInfo?.authorities | ||
| 100 | + if (authorities && Array.isArray(authorities)) { | ||
| 101 | + // 查找ROLE_开头的权限 | ||
| 102 | + const roleAuthority = authorities.find(auth => | ||
| 103 | + auth.authority && auth.authority.startsWith('ROLE_') | ||
| 104 | + ) | ||
| 105 | + | ||
| 106 | + if (roleAuthority) { | ||
| 107 | + // 移除ROLE_前缀并转换为中文 | ||
| 108 | + const roleName = roleAuthority.authority.replace('ROLE_', '') | ||
| 109 | + const roleMap: { [key: string]: string } = { | ||
| 110 | + 'ADMIN': '管理员', | ||
| 111 | + 'USER': '普通用户', | ||
| 112 | + 'MANAGER': '经理', | ||
| 113 | + 'OPERATOR': '操作员' | ||
| 114 | + } | ||
| 115 | + return roleMap[roleName] || roleName | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + return '普通用户' | ||
| 120 | +} | ||
| 121 | + | ||
| 122 | +// 获取用户信息 | ||
| 123 | +const fetchUserInfo = async () => { | ||
| 124 | + try { | ||
| 125 | + loading.value = true | ||
| 126 | + const response = await request.get('/api/auth/userinfo') | ||
| 127 | + userInfo.value = response | ||
| 128 | + console.log('用户信息:', response) | ||
| 129 | + } catch (error) { | ||
| 130 | + console.error('获取用户信息失败:', error) | ||
| 131 | + // 如果获取失败,尝试从本地存储获取 | ||
| 132 | + const storedUserInfo = localStorage.getItem('userInfo') | ||
| 133 | + if (storedUserInfo) { | ||
| 134 | + userInfo.value = JSON.parse(storedUserInfo) | ||
| 135 | + } | ||
| 136 | + } finally { | ||
| 137 | + loading.value = false | ||
| 138 | + } | ||
| 139 | +} | ||
| 85 | 140 | ||
| 86 | onMounted(() => { | 141 | onMounted(() => { |
| 87 | // 获取当前时间 | 142 | // 获取当前时间 |
| 88 | currentTime.value = new Date().toLocaleString() | 143 | currentTime.value = new Date().toLocaleString() |
| 89 | 144 | ||
| 90 | - // 获取用户信息 | ||
| 91 | - const storedUserInfo = localStorage.getItem('userInfo') | ||
| 92 | - if (storedUserInfo) { | ||
| 93 | - userInfo.value = JSON.parse(storedUserInfo) | ||
| 94 | - } | ||
| 95 | - | ||
| 96 | // 检查是否已登录 | 145 | // 检查是否已登录 |
| 97 | const token = localStorage.getItem('token') | 146 | const token = localStorage.getItem('token') |
| 98 | if (!token) { | 147 | if (!token) { |
| 99 | router.push('/') | 148 | router.push('/') |
| 149 | + return | ||
| 100 | } | 150 | } |
| 151 | + | ||
| 152 | + // 获取用户信息 | ||
| 153 | + fetchUserInfo() | ||
| 101 | }) | 154 | }) |
| 102 | 155 | ||
| 103 | const logout = () => { | 156 | const logout = () => { | ... | ... |
-
Please register or login to post a comment