{"record":{"id":"a00a3a5422e58f1c","repo":"flipped-aurora/gin-vue-admin","slug":"error-a00a3a","errorCode":null,"errorMessage":"用户不具备该角色权限","messagePattern":"用户不具备该角色权限","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_api_token.go","lineNumber":30,"sourceCode":")\n\ntype ApiTokenService struct{}\n\nfunc (apiVersion *ApiTokenService) CreateApiToken(ctx context.Context, apiToken system.SysApiToken, days int) (string, error) {\n\tvar user system.SysUser\n\tif err := global.GVA_DB.WithContext(ctx).Preload(\"Authorities\").Where(\"id = ?\", apiToken.UserID).First(&user).Error; err != nil {\n\t\treturn \"\", errors.New(\"用户不存在\")\n\t}\n\n\thasAuth := false\n\tfor _, auth := range user.Authorities {\n\t\tif auth.AuthorityId == apiToken.AuthorityID {\n\t\t\thasAuth = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !hasAuth && user.AuthorityId != apiToken.AuthorityID {\n\t\treturn \"\", errors.New(\"用户不具备该角色权限\")\n\t}\n\n\tj := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一不同的部分是过期时间\n\n\texpireTime := time.Duration(days) * 24 * time.Hour\n\tif days == -1 {\n\t\texpireTime = 100 * 365 * 24 * time.Hour\n\t}\n\n\tbf, _ := utils.ParseDuration(global.GVA_CONFIG.JWT.BufferTime)\n\n\tclaims := sysReq.CustomClaims{\n\t\tBaseClaims: sysReq.BaseClaims{\n\t\t\tUUID:        user.UUID,\n\t\t\tID:          user.ID,\n\t\t\tUsername:    user.Username,\n\t\t\tNickName:    user.NickName,\n\t\t\tAuthorityId: apiToken.AuthorityID,","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_api_token.go#L12-L48","documentation":"CreateApiToken only issues tokens for authority (role) IDs the user actually holds. After preloading Authorities, it checks both the many-to-many authorities and the user's default AuthorityId; if apiToken.AuthorityID matches neither, it returns \"用户不具备该角色权限\". This prevents minting a token with privileges the user never had.","triggerScenarios":"Requesting a token with an AuthorityID that is not among the user's Authorities and is not the user's default AuthorityId — e.g. asking for authority 9528 when the target user only has 8881.","commonSituations":"Automated scripts copying a role ID from another user; role was revoked from the account after a script was written; typo or stale constant for the authority ID; DB where the user_authority join rows were wiped by a migration.","solutions":["Grant the desired role to the user (role management page or insert into user_authority), then retry.","Request the token with an AuthorityID the user already holds (check the user's role list).","If the user should have the role by default, set sys_users.authority_id to the requested AuthorityID as well."],"exampleFix":"// before\nsvc.CreateApiToken(ctx, system.SysApiToken{UserID: 5, AuthorityID: 9528}, 30) // user 5 has 888 only\n\n// after: grant role first, then mint\nglobal.GVA_DB.WithContext(ctx).Exec(\"INSERT INTO user_authority (sys_user_id, sys_authority_authority_id) VALUES (5, 9528)\")\ntoken, err := svc.CreateApiToken(ctx, system.SysApiToken{UserID: 5, AuthorityID: 9528}, 30)","handlingStrategy":"validation","validationCode":"const user = (await getUserById({ id: form.userId })).data.user\nconst holds = user.authorities.some(a => a.authorityId === form.authorityId) || user.authorityId === form.authorityId\nif (!holds) throw new Error('该用户不具备角色 ' + form.authorityId + '，不能为其签发此角色 token')","typeGuard":null,"tryCatchPattern":"try {\n  const token = await createApiToken(form)\n} catch (e) {\n  if (String(e?.msg).includes('用户不具备该角色权限')) {\n    ElMessage.error('请先在角色管理中为该用户授予目标角色，再签发 token')\n  } else { throw e }\n}","preventionTips":["Always fetch the user's current role list before minting a token for a specific authority.","Re-verify role assignments in scripts after any role-management change (roles may have been revoked).","Use constants/config for authority IDs and validate them against the role table, not free-typed numbers.","Prefer minting tokens with the user's default authorityId when a specific role is not required."],"tags":["authorization","role","api-token","jwt"],"backgroundTag":"insufficient-role-permission","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}