{"record":{"id":"eb00f083e27ecae8","repo":"Tencent/APIJSON","slug":"error-eb00f0","errorCode":null,"errorMessage":"{} = {} 的 {} 不允许 {} 用户的 {} 请求！","messagePattern":"(.+?) = (.+?) 的 (.+?) 不允许 (.+?) 用户的 (.+?) 请求！","errorType":"exception","errorClass":"IllegalAccessException","httpStatus":403,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java","lineNumber":395,"sourceCode":"\t\t\tif (requestId != null) {\n\t\t\t\tif (requestIdArray == null) {\n\t\t\t\t\trequestIdArray = createJSONArray();\n\t\t\t\t}\n\t\t\t\trequestIdArray.add(requestId);\n\t\t\t}\n\n\t\t\tif (requestIdArray == null) { // 可能是 @ 得到 || requestIdArray.isEmpty()) { // 请求未声明 key:id 或 key{}:[...] 条件，自动补全\n\t\t\t\tconfig.putWhere(visitorIdKey+\"{}\", parseArray(list), true); // key{}:[] 有效，SQLConfig<T, M, L> 里 throw NotExistException\n\t\t\t}\n\t\t\telse { // 请求已声明 key:id 或 key{}:[] 条件，直接验证\n\t\t\t\tfor (Object id : requestIdArray) {\n\t\t\t\t\tif (id == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (id instanceof Number) { // 不能准确地判断 Long，可能是 Integer\n\t\t\t\t\t\tif (((Number) id).longValue() <= 0 || list.contains(Long.valueOf(\"\" + id)) == false) { // Integer等转为 Long 才能正确判断，强转崩溃\n\t\t\t\t\t\t\tthrow new IllegalAccessException(visitorIdKey + \" = \" + id + \" 的 \" + table\n\t\t\t\t\t\t\t\t\t+ \" 不允许 \" + role + \" 用户的 \" + method.name() + \" 请求！\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (id instanceof String) {\n\t\t\t\t\t\tif (StringUtil.isEmpty(id) || list.contains(id) == false) {\n\t\t\t\t\t\t\tthrow new IllegalAccessException(visitorIdKey + \" = \" + id + \" 的 \" + table\n\t\t\t\t\t\t\t\t\t+ \" 不允许 \" + role + \" 用户的 \" + method.name() + \" 请求！\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthrow new UnsupportedDataTypeException(table + \".id 类型错误，类型必须是 Long/String！\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OWNER:\n\t\t\tif (config.getMethod() == POST) {\n\t\t\t\tList<String> c = config.getColumn();","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java#L377-L413","documentation":"For the CONTACT role, verifyRole() gathers the list of userIds the visitor is allowed to touch (their contact list) and validates every id the request supplies as a where-condition on the visitor id key. A Number id that is <= 0, or whose Long value is not contained in the permitted list, throws IllegalAccessException. The message names the visitor id key, the offending id, the table, role and method.","triggerScenarios":"A request with \"role\":\"CONTACT\" carries e.g. \"userId\": 123 in the where clause, but the visitor's contact list (rows linking visitor to contacts) does not contain 123 — or the id is 0/negative; the Number branch of the id loop throws.","commonSituations":"Stale client caching a userId whose contact relationship was deleted; front-end lets users type arbitrary ids; test fixtures that assume a contact relationship which was never inserted; Integer ids compared as Long so the code normalizes via Long.valueOf(\"\" + id) and the relationship table simply lacks that row.","solutions":["Ensure the referenced id belongs to the current visitor's contact list (insert/refresh the contact/relation row) before issuing the request.","Send the request with an appropriate role (e.g. OWNER with own id, or ADMIN if the caller is an admin) instead of CONTACT.","Remove the id condition and let APIJSON auto-complete it: when no key:id / key{}:[...] is declared, the code adds key{}: [permitted list] itself.","Guard on the client: fetch the permitted contact ids first and only send conditions built from that set."],"exampleFix":"// before\n{\n  \"User\": { \"id\": 123, \"role\": \"CONTACT\" },\n  \"tag\": \"User\"\n}\n// 123 not in visitor's contact list -> IllegalAccessException\n\n// after: omit the condition, let the server scope it\n{\n  \"User\": { \"role\": \"CONTACT\" },\n  \"tag\": \"User\"\n}","handlingStrategy":"validation","validationCode":"// before sending: fetch permitted contact ids and validate numeric conditions\nList<Long> permitted = contactService.listContactIds(visitorId);\nif (!permitted.contains(targetUserId) || targetUserId <= 0) {\n    clientError(\"target id not in your contacts\");\n}","typeGuard":"boolean isValidContactId(Object id, List<Object> permitted) { return id instanceof Number && ((Number) id).longValue() > 0 && permitted.contains(Long.valueOf(String.valueOf(id))); }","tryCatchPattern":"catch (IllegalAccessException e) when message contains \"不允许\" -> 403; tell the user the record is outside their contact scope; do not auto-retry.","preventionTips":["Build id conditions only from server-returned contact lists, not user input.","Prefer omitting the condition and letting the server scope results automatically."],"tags":["apijson","permissions","contact-role","row-level-security"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}