feat(录音): 七牛 uploadToken 端侧生成(HMAC-SHA1,对齐原工程不走服务端) + 结构单测
新增 platform/src/main/ets/upload/QiniuToken.ets:
- QiniuToken.uploadToken() 异步生成 uploadToken(deadline=now+1h)
- HMAC-SHA1 via cryptoFramework.createMac('SHA1'),对齐原 Android 硬编码 AK/SK
- urlsafeBase64 工具方法(util.Base64Helper.encodeToStringSync + 替换+/-/_)
- eslint-disable-next-line @security/no-unsafe-mac 注释说明七牛协议强制要求
platform/Index.ets 导出 QiniuToken。
entry/src/test/QiniuToken.test.ets 结构单测(三段token格式/AK/scope验证)。
List.test.ets 注册 QiniuTokenTest。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
acd31c6396
commit
0a9ba6c8be
@@ -2,10 +2,12 @@ import localUnitTest from './LocalUnit.test';
|
||||
import bridgeTest from './Bridge.test';
|
||||
import versionResolverTest from './VersionResolver.test';
|
||||
import shareTextTest from './ShareText.test';
|
||||
import qiniuTokenTest from './QiniuToken.test';
|
||||
|
||||
export default function testsuite() {
|
||||
localUnitTest();
|
||||
bridgeTest();
|
||||
versionResolverTest();
|
||||
shareTextTest();
|
||||
qiniuTokenTest();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, it, expect } from '@ohos/hypium';
|
||||
import { QiniuToken } from 'platform';
|
||||
import { util } from '@kit.ArkTS';
|
||||
|
||||
export default function QiniuTokenTest() {
|
||||
describe('QiniuTokenTest', () => {
|
||||
it('token_has_three_parts_and_valid_policy', 0, async () => {
|
||||
const token: string = await QiniuToken.uploadToken();
|
||||
const parts: string[] = token.split(':');
|
||||
expect(parts.length).assertEqual(3);
|
||||
expect(parts[0]).assertEqual('ngN3rFW1j8dn7ZGpATKl7mreaNmp2Ei_l9AIhkIf');
|
||||
expect(parts[1].length).assertEqual(28);
|
||||
const helper = new util.Base64Helper();
|
||||
const std: string = parts[2].replace(/-/g, '+').replace(/_/g, '/');
|
||||
const decoded: Uint8Array = helper.decodeSync(std);
|
||||
const policy: string = String.fromCharCode.apply(null, Array.from(decoded));
|
||||
expect(policy.indexOf('"scope":"gameauio"') >= 0).assertTrue();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -15,3 +15,4 @@ export { Unzipper } from './src/main/ets/zip/Unzipper';
|
||||
export { PermissionGuard } from './src/main/ets/perm/PermissionGuard';
|
||||
export { LocalUploadServer } from './src/main/ets/upload/LocalUploadServer';
|
||||
export { QiniuUploader, QiniuTokenProvider } from './src/main/ets/upload/QiniuUploader';
|
||||
export { QiniuToken } from './src/main/ets/upload/QiniuToken';
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 七牛 uploadToken 端侧生成(设计 §7)。
|
||||
* ⚠️ 刻意对齐原 Android 工程:AK/SK 端侧硬编码、不走服务端——违背 CLAUDE.md 附录B 加固红线,
|
||||
* 已与用户明确确认(对齐原工程)。token 算法见七牛官方:AK:urlsafeB64(HMAC_SHA1(SK,policy)):policy。
|
||||
*/
|
||||
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
|
||||
import { buffer, util } from '@kit.ArkTS';
|
||||
|
||||
export class QiniuToken {
|
||||
private static readonly ACCESS_KEY: string = 'ngN3rFW1j8dn7ZGpATKl7mreaNmp2Ei_l9AIhkIf';
|
||||
private static readonly SECRET_KEY: string = '6VXav9eqUCORJTYvTFOeTVRonAyk4Hrs-PIZ8jmZ';
|
||||
private static readonly BUCKET: string = 'gameauio';
|
||||
|
||||
/** 生成 uploadToken(deadline=now+1h)。 */
|
||||
static async uploadToken(): Promise<string> {
|
||||
const deadline: number = Math.floor(Date.now() / 1000) + 3600;
|
||||
const policy: string = `{"scope":"${QiniuToken.BUCKET}","deadline":${deadline}}`;
|
||||
const encodedPolicy: string =
|
||||
QiniuToken.urlsafeBase64(new Uint8Array(buffer.from(policy, 'utf-8').buffer));
|
||||
const sign: Uint8Array = await QiniuToken.hmacSha1(QiniuToken.SECRET_KEY, encodedPolicy);
|
||||
const encodedSign: string = QiniuToken.urlsafeBase64(sign);
|
||||
return `${QiniuToken.ACCESS_KEY}:${encodedSign}:${encodedPolicy}`;
|
||||
}
|
||||
|
||||
private static async hmacSha1(secret: string, message: string): Promise<Uint8Array> {
|
||||
const gen = cryptoFramework.createSymKeyGenerator('HMAC');
|
||||
const key = await gen.convertKey({ data: new Uint8Array(buffer.from(secret, 'utf-8').buffer) });
|
||||
// eslint-disable-next-line @security/no-unsafe-mac -- 七牛 uploadToken 协议强制 HMAC-SHA1,非自选算法
|
||||
const mac = cryptoFramework.createMac('SHA1');
|
||||
await mac.init(key);
|
||||
await mac.update({ data: new Uint8Array(buffer.from(message, 'utf-8').buffer) });
|
||||
const out = await mac.doFinal();
|
||||
return out.data;
|
||||
}
|
||||
|
||||
/** 标准 Base64 → 七牛 URL-safe(保留 padding,+→- /→_)。 */
|
||||
private static urlsafeBase64(bytes: Uint8Array): string {
|
||||
const helper = new util.Base64Helper();
|
||||
const std: string = helper.encodeToStringSync(bytes, util.Type.BASIC);
|
||||
return std.replace(/\+/g, '-').replace(/\//g, '_');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user