ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ContentProvider权限设置实战:从AndroidManifest到TaoToken配置的完整避坑指南

ContentProvider权限设置实战:从AndroidManifest到TaoToken配置的完整避坑指南 1. ContentProvider 权限设置到底在解决什么问题ContentProvider 是 Android 四大组件里最容易被权限配置坑到的一个。它本质上是跨进程数据访问的入口一旦android:exported和android:permission没配对要么外部应用访问直接抛SecurityException要么数据被任意应用裸奔读取。我在实际项目里见过两种极端一种是把exported全设成true又不加权限等于把数据库挂在公网上另一种是权限声明了一堆结果调用方uses-permission没加排查半天以为是 URI 写错了。这篇聚焦的场景很具体你在AndroidManifest.xml里声明了一个 Provider希望部分应用能读、部分应用不能读同时把访问链路统一到 TaoToken 的 Key/API 通道上做鉴权和调用管理。适合已经写过 Provider、但被android:exported、android:readPermission、path-permission、grantUriPermissions这几个属性绕晕的 Android 开发者。下面从清单声明规则讲到可复制的配置片段再到验证请求和报错排查每一步都能直接跟做。先明确一个判断顺序后面所有配置都围绕它展开判断项作用配错后果android:exported是否允许其他应用访问false 时外部调用直接失败android:permission整体读写权限无权限方访问抛 SecurityExceptionandroid:readPermission/writePermission分离读写权限只读场景被要求写权限path-permission按 URI 路径细分权限全路径开放越权访问grantUriPermissions是否允许临时授权传递传递权限失效2. TaoToken 前置统一 Key 与 API 通道的准备在写 Provider 权限之前先把调用侧的通道准备好。TaoToken 在这里的角色是统一管理模型调用和 API Key 的入口Provider 的访问鉴权逻辑可以复用同一套 Key 体系避免每个应用各写一套权限校验。你需要先拿到一个可用的 API Key再把它写进配置文件。打开控制台创建 Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteKey 管理页面在这里后续轮换和吊销都在这操作https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite如果你打算把 Provider 的访问封装成长期运行的编码/Agent 任务用 Coding Plan 更合适https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteAPI 基础地址统一用https://taotoken.net/api拿到 Key 之后不要硬编码进代码走配置文件。下面两段配置分别对应settings.json和config.toml你可以按项目实际技术栈选一个。3. 可复制配置AndroidManifest 与 TaoToken 配置骨架3.1 AndroidManifest 中 Provider 的完整声明先看一个带读写分离和路径级权限的 Provider 声明。注意android:exportedtrue是外部访问的前提android:permission是整体兜底权限permission android:namecom.example.app.permission.READ_PROVIDER android:labelAllow read content provider android:protectionLevelnormal / permission android:namecom.example.app.permission.WRITE_PROVIDER android:labelAllow write content provider android:protectionLevelnormal / provider android:name.PrivProvider android:authoritiescom.example.app.PrivProvider android:exportedtrue android:readPermissioncom.example.app.permission.READ_PROVIDER android:writePermissioncom.example.app.permission.WRITE_PROVIDER android:grantUriPermissionstrue path-permission android:pathPrefix/hello android:readPermissioncom.example.app.permission.READ_PROVIDER / grant-uri-permission android:pathPrefix/hello / /provider几个关键点必须说清楚。android:exportedtrue只决定「能不能被外部访问」不决定「访问时要不要权限」两者是独立的。android:permission如果同时设了会覆盖读写权限的默认行为所以更推荐用readPermission和writePermission分开控制。path-permission一旦声明就只对匹配的路径生效其他路径走 Provider 级别的权限。调用方应用需要在自己的清单里声明对应权限否则访问会直接失败uses-permission android:namecom.example.app.permission.READ_PROVIDER /3.2 settings.json 配置片段如果你用 JSON 管理调用配置把 TaoToken 的 Key 和 Provider 访问参数放进去{ taotoken: { api_base: https://taotoken.net/api, api_key: sk-your-key-here, timeout_ms: 15000 }, provider: { authority: com.example.app.PrivProvider, read_permission: com.example.app.permission.READ_PROVIDER, write_permission: com.example.app.permission.WRITE_PROVIDER, grant_uri_permissions: true } }3.3 config.toml 配置片段TOML 版本更适合需要注释和分组的场景[taotoken] api_base https://taotoken.net/api api_key sk-your-key-here timeout_ms 15000 [provider] authority com.example.app.PrivProvider read_permission com.example.app.permission.READ_PROVIDER write_permission com.example.app.permission.WRITE_PROVIDER grant_uri_permissions true [provider.paths.hello] path_prefix /hello read_permission com.example.app.permission.READ_PROVIDER注意api_key不要提交到版本库用环境变量或本地local.properties注入配置文件里只留占位符。4. 验证请求与成功结果配置写完必须验证否则你永远不知道是权限没生效还是 URI 写错了。分两步走。4.1 用 adb 验证 Provider 是否可访问先确认 Provider 已经注册成功adb shell content query --uri content://com.example.app.PrivProvider/hello如果返回No result found说明 Provider 通了但没数据如果返回SecurityException说明权限没配对。带权限查询需要调用方有对应权限测试时可以先临时用--user指定。4.2 用代码验证读写权限分离在调用方应用里写一段最小验证代码确认读权限生效、写权限被拦截ContentResolver resolver getContentResolver(); Uri uri Uri.parse(content://com.example.app.PrivProvider/hello); // 读操作应成功 Cursor cursor resolver.query(uri, new String[]{id, name}, null, null, null); if (cursor ! null) { while (cursor.moveToNext()) { String name cursor.getString(cursor.getColumnIndexOrThrow(name)); Log.d(ProviderTest, read ok: name); } cursor.close(); } // 写操作未声明写权限时应抛 SecurityException try { ContentValues values new ContentValues(); values.put(name, test); resolver.insert(uri, values); Log.d(ProviderTest, write ok); } catch (SecurityException e) { Log.e(ProviderTest, write blocked as expected: e.getMessage()); }成功结果应该是读操作正常返回数据写操作抛出SecurityException并打印write blocked as expected。如果写操作也成功了说明writePermission没生效回去检查清单里是不是漏了android:writePermission。4.3 验证 TaoToken 通道连通性Provider 权限验证完之后确认调用通道也通curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer sk-your-key-here \ -H Content-Type: application/json \ -d {model:gpt-4o-mini,messages:[{role:user,content:ping}]}返回带choices字段的 JSON 就说明 Key 和通道都正常。想直接在网页里试模型对话用这个入口https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite5. 本篇常见错误排查5.1 exported 设成 false 后外部访问失败报错通常是Unable to find provider info或SecurityException: Permission Denial。原因就是android:exportedfalse时只有同应用或同用户 ID 的组件能访问。如果你确实需要外部访问改成true并配上权限如果只是同应用访问保持false更安全。5.2 声明了权限但调用方仍报 SecurityException排查顺序先看调用方清单有没有uses-permission再看权限名是否完全一致大小写敏感最后看protectionLevel。normal级别安装即授予signature级别要求同签名dangerous需要运行时申请。很多坑是权限名写错一个字母比如com.example.app.permission.READ_PROVIDER写成com.example.app.READ_PROVIDER。5.3 path-permission 和 grantUriPermissions 冲突一旦声明了grant-uri-permission全局的android:grantUriPermissions属性就失效了无论设 true 还是 false都只允许声明的路径被临时授权。如果你发现传递权限后部分 URI 访问不了检查是不是只声明了/hello前缀而实际访问的是/world。5.4 临时授权传递后仍无法访问检查三点Provider 是否设了android:grantUriPermissionstrueIntent 是否带了FLAG_GRANT_READ_URI_PERMISSION被调用 Activity 是否在授权有效期内访问。三者缺一不可。Intent intent new Intent(this, ReadProviderActivity.class); intent.setData(Uri.parse(content://com.example.app.PrivProvider/hello/1)); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent);5.5 权限配置改了但没生效Android 会缓存权限信息改完清单后必须卸载重装不能只覆盖安装。用adb install -r有时不会刷新权限建议adb uninstall后再装。6. 把权限链路和调用通道统一起来Provider 权限配置的核心就三件事exported决定开不开门permission决定谁能进path-permission决定能进哪个房间。把这三层配清楚再配合 TaoToken 的统一 Key 通道做调用侧鉴权整个访问链路就闭环了。接入文档在这里里面有完整的鉴权和参数说明https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite如果你在配android:exported和android:permission时遇到报错先去 API Keys 页面确认 Key 状态正常再对照接入文档检查请求头格式https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite长期跑编码任务或 Agent 场景Coding Plan 能省掉每次手动配 Key 的麻烦https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite最后留一个我踩过的坑path-permission的pathPattern通配符写法是/hello/.*那个点不能省写成/hello/*匹配行为会不一样。这个细节文档里不显眼但排查起来很费时间。
返回列表