加密库示例 KMP OpenHarmony跨平台安全方案
本文介绍了一个基于Kotlin Multiplatform(KMP)和OpenHarmony平台的URL处理工具库。该库提供完整的URL处理能力,包括验证、解析、编码/解码、查询参数处理和URL拼接等功能。通过KMP技术,代码可编译到JVM、JS和OpenHarmony/ArkTS平台,实现跨平台复用。核心功能模块包含URL验证、解析、编码解码、查询参数提取、拼接和规范化等。Kotlin实现的核心


项目概述
URL(统一资源定位符)处理是现代应用开发中的基础需求。无论是在 Web 应用、移动应用还是桌面应用中,都需要进行各种 URL 操作,如解析、验证、编码、拼接等。然而,不同的编程语言和平台对 URL 处理的实现方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。
本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的 URL 处理工具库。这个工具库提供了一套完整的 URL 处理能力,包括 URL 解析、验证、编码、拼接、查询参数提取等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。
技术架构
多平台支持
- Kotlin/JVM: 后端服务和桌面应用
- Kotlin/JS: Web 应用和浏览器环境
- OpenHarmony/ArkTS: 鸿蒙操作系统应用
核心功能模块
- URL 验证: 检查 URL 字符串的有效性
- URL 解析: 提取 URL 的各个组成部分
- URL 编码: 对 URL 中的特殊字符进行编码
- URL 解码: 对编码的 URL 进行解码
- 查询参数处理: 提取和操作 URL 查询参数
- URL 拼接: 安全地拼接 URL 和路径
- URL 规范化: 标准化 URL 格式
Kotlin 实现
核心 URL 处理类
// 文件: src/commonMain/kotlin/UrlProcessor.kt
/**
* URL 处理工具类
* 提供 URL 解析、验证、编码等功能
*/
class UrlProcessor {
/**
* 验证 URL 是否有效
* @param url URL 字符串
* @return 是否有效
*/
fun isValidUrl(url: String): Boolean {
return try {
val pattern = "^(https?|ftp)://[\\w\\-]+(\\.[\\w\\-]+)+([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?$".toRegex()
pattern.matches(url)
} catch (e: Exception) {
false
}
}
/**
* 解析 URL
* @param url URL 字符串
* @return URL 组成部分映射
*/
fun parseUrl(url: String): Map<String, String> {
val result = mutableMapOf<String, String>()
try {
// 提取协议
val protocolMatch = "^([a-z]+)://".toRegex().find(url)
if (protocolMatch != null) {
result["protocol"] = protocolMatch.groupValues[1]
}
// 移除协议部分
val withoutProtocol = url.replaceFirst("^[a-z]+://".toRegex(), "")
// 提取主机和路径
val hostPathMatch = "^([^/?#]+)([^?#]*)(.*)".toRegex().find(withoutProtocol)
if (hostPathMatch != null) {
result["host"] = hostPathMatch.groupValues[1]
result["path"] = hostPathMatch.groupValues[2]
val remaining = hostPathMatch.groupValues[3]
// 提取查询字符串和片段
val queryFragmentMatch = "^\\?([^#]*)#?(.*)".toRegex().find(remaining)
if (queryFragmentMatch != null) {
result["query"] = queryFragmentMatch.groupValues[1]
result["fragment"] = queryFragmentMatch.groupValues[2]
} else {
val fragmentMatch = "^#(.*)".toRegex().find(remaining)
if (fragmentMatch != null) {
result["fragment"] = fragmentMatch.groupValues[1]
}
}
}
} catch (e: Exception) {
// 返回空映射
}
return result
}
/**
* URL 编码
* @param text 要编码的文本
* @return 编码后的文本
*/
fun encodeUrl(text: String): String {
return text.map { char ->
when {
char.isLetterOrDigit() || char in "-_.~" -> char.toString()
else -> "%${char.code.toString(16).padStart(2, '0').uppercase()}"
}
}.joinToString("")
}
/**
* URL 解码
* @param text 编码后的文本
* @return 解码后的文本
*/
fun decodeUrl(text: String): String {
var result = text
val pattern = "%([0-9A-Fa-f]{2})".toRegex()
while (pattern.containsMatchIn(result)) {
result = result.replace(pattern) { matchResult ->
val hex = matchResult.groupValues[1]
hex.toInt(16).toChar().toString()
}
}
return result
}
/**
* 提取查询参数
* @param url URL 字符串
* @return 查询参数映射
*/
fun getQueryParams(url: String): Map<String, String> {
val params = mutableMapOf<String, String>()
val queryStart = url.indexOf('?')
if (queryStart != -1) {
val queryEnd = url.indexOf('#', queryStart)
val queryString = if (queryEnd != -1) {
url.substring(queryStart + 1, queryEnd)
} else {
url.substring(queryStart + 1)
}
queryString.split("&").forEach { param ->
val parts = param.split("=")
if (parts.size == 2) {
params[parts[0]] = decodeUrl(parts[1])
} else if (parts.size == 1) {
params[parts[0]] = ""
}
}
}
return params
}
/**
* 添加查询参数
* @param url URL 字符串
* @param key 参数键
* @param value 参数值
* @return 添加参数后的 URL
*/
fun addQueryParam(url: String, key: String, value: String): String {
val separator = if (url.contains("?")) "&" else "?"
return "$url$separator$key=${encodeUrl(value)}"
}
/**
* 拼接 URL 和路径
* @param baseUrl 基础 URL
* @param path 路径
* @return 拼接后的 URL
*/
fun joinUrl(baseUrl: String, path: String): String {
val cleanBase = baseUrl.trimEnd('/')
val cleanPath = path.trimStart('/')
return "$cleanBase/$cleanPath"
}
/**
* 获取 URL 的主机名
* @param url URL 字符串
* @return 主机名
*/
fun getHostname(url: String): String {
val parsed = parseUrl(url)
return parsed["host"] ?: ""
}
/**
* 获取 URL 的路径
* @param url URL 字符串
* @return 路径
*/
fun getPath(url: String): String {
val parsed = parseUrl(url)
return parsed["path"] ?: ""
}
/**
* 规范化 URL
* @param url URL 字符串
* @return 规范化后的 URL
*/
fun normalizeUrl(url: String): String {
var normalized = url.trim()
// 移除末尾的斜杠(除非是根路径)
if (normalized.endsWith("/") && !normalized.endsWith("://")) {
normalized = normalized.dropLast(1)
}
// 转换为小写
normalized = normalized.lowercase()
return normalized
}
}
Kotlin 实现的核心特点
Kotlin 实现中的 URL 处理功能充分利用了 Kotlin 标准库的字符串处理能力。URL 验证使用了正则表达式来检查 URL 的格式。URL 解析使用了正则表达式来提取 URL 的各个组成部分。
URL 编码和解码功能使用了字符映射的方式。编码时,特殊字符被转换为 %HEX 格式。解码时,%HEX 格式被转换回原始字符。
查询参数处理使用了字符串分割和映射的方式。参数值在提取时会自动解码,确保正确处理包含特殊字符的参数值。
URL 拼接功能确保了基础 URL 和路径的正确组合,避免了双斜杠或缺少斜杠的问题。规范化功能使得不同格式的 URL 可以被统一处理。
JavaScript 实现
编译后的 JavaScript 代码
// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
// (由 Kotlin 编译器自动生成)
/**
* UrlProcessor 类的 JavaScript 版本
* 通过 Kotlin/JS 编译器从 Kotlin 源代码生成
*/
class UrlProcessor {
/**
* 验证 URL 是否有效
* @param {string} url - URL 字符串
* @returns {boolean} 是否有效
*/
isValidUrl(url) {
const pattern = /^(https?|ftp):\/\/[\w\-]+(\.[\w\-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?$/;
return pattern.test(url);
}
/**
* 解析 URL
* @param {string} url - URL 字符串
* @returns {Object} URL 组成部分
*/
parseUrl(url) {
const result = {};
try {
// 提取协议
const protocolMatch = url.match(/^([a-z]+):\/\//);
if (protocolMatch) {
result.protocol = protocolMatch[1];
}
// 移除协议部分
const withoutProtocol = url.replace(/^[a-z]+:\/\//, '');
// 提取主机和路径
const hostPathMatch = withoutProtocol.match(/^([^/?#]+)([^?#]*)(.*)/);
if (hostPathMatch) {
result.host = hostPathMatch[1];
result.path = hostPathMatch[2];
const remaining = hostPathMatch[3];
// 提取查询字符串和片段
const queryFragmentMatch = remaining.match(/^\?([^#]*)#?(.*)/);
if (queryFragmentMatch) {
result.query = queryFragmentMatch[1];
result.fragment = queryFragmentMatch[2];
} else {
const fragmentMatch = remaining.match(/^#(.*)/);
if (fragmentMatch) {
result.fragment = fragmentMatch[1];
}
}
}
} catch (e) {
// 返回空对象
}
return result;
}
/**
* URL 编码
* @param {string} text - 要编码的文本
* @returns {string} 编码后的文本
*/
encodeUrl(text) {
return text.split('').map(char => {
if (/[a-zA-Z0-9\-_.~]/.test(char)) {
return char;
} else {
return '%' + char.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0');
}
}).join('');
}
/**
* URL 解码
* @param {string} text - 编码后的文本
* @returns {string} 解码后的文本
*/
decodeUrl(text) {
let result = text;
const pattern = /%([0-9A-Fa-f]{2})/g;
result = result.replace(pattern, (match, hex) => {
return String.fromCharCode(parseInt(hex, 16));
});
return result;
}
/**
* 提取查询参数
* @param {string} url - URL 字符串
* @returns {Object} 查询参数
*/
getQueryParams(url) {
const params = {};
const queryStart = url.indexOf('?');
if (queryStart !== -1) {
const queryEnd = url.indexOf('#', queryStart);
const queryString = queryEnd !== -1 ?
url.substring(queryStart + 1, queryEnd) :
url.substring(queryStart + 1);
queryString.split('&').forEach(param => {
const parts = param.split('=');
if (parts.length === 2) {
params[parts[0]] = this.decodeUrl(parts[1]);
} else if (parts.length === 1) {
params[parts[0]] = '';
}
});
}
return params;
}
/**
* 添加查询参数
* @param {string} url - URL 字符串
* @param {string} key - 参数键
* @param {string} value - 参数值
* @returns {string} 添加参数后的 URL
*/
addQueryParam(url, key, value) {
const separator = url.includes('?') ? '&' : '?';
return `${url}${separator}${key}=${this.encodeUrl(value)}`;
}
/**
* 拼接 URL 和路径
* @param {string} baseUrl - 基础 URL
* @param {string} path - 路径
* @returns {string} 拼接后的 URL
*/
joinUrl(baseUrl, path) {
const cleanBase = baseUrl.replace(/\/$/, '');
const cleanPath = path.replace(/^\//, '');
return `${cleanBase}/${cleanPath}`;
}
/**
* 获取 URL 的主机名
* @param {string} url - URL 字符串
* @returns {string} 主机名
*/
getHostname(url) {
const parsed = this.parseUrl(url);
return parsed.host || '';
}
/**
* 获取 URL 的路径
* @param {string} url - URL 字符串
* @returns {string} 路径
*/
getPath(url) {
const parsed = this.parseUrl(url);
return parsed.path || '';
}
/**
* 规范化 URL
* @param {string} url - URL 字符串
* @returns {string} 规范化后的 URL
*/
normalizeUrl(url) {
let normalized = url.trim();
// 移除末尾的斜杠(除非是根路径)
if (normalized.endsWith('/') && !normalized.endsWith('://')) {
normalized = normalized.slice(0, -1);
}
// 转换为小写
normalized = normalized.toLowerCase();
return normalized;
}
}
JavaScript 实现的特点
JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的正则表达式提供了强大的字符串匹配和替换能力。
String.fromCharCode 用于将字符代码转换为字符。charCodeAt 用于获取字符的代码。这些方法在 URL 编码和解码中起到关键作用。
JavaScript 的 replace 方法支持正则表达式和回调函数,这使得 URL 解码的实现非常简洁。
ArkTS 调用代码
OpenHarmony 应用集成
// 文件: kmp_ceshiapp/entry/src/main/ets/pages/UrlProcessorPage.ets
import { UrlProcessor } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';
@Entry
@Component
struct UrlProcessorPage {
@State selectedOperation: string = 'parse';
@State inputUrl: string = '';
@State result: string = '';
@State resultTitle: string = '';
@State parameter: string = '';
private urlProcessor = new UrlProcessor();
private operations = [
{ name: '验证 URL', value: 'validate' },
{ name: '解析 URL', value: 'parse' },
{ name: 'URL 编码', value: 'encode' },
{ name: 'URL 解码', value: 'decode' },
{ name: '提取参数', value: 'params' },
{ name: '规范化', value: 'normalize' },
{ name: '获取主机名', value: 'hostname' },
{ name: '获取路径', value: 'path' }
];
build() {
Column() {
// 标题
Text('🔗 URL处理工具库')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(20)
.backgroundColor('#1A237E')
.textAlign(TextAlign.Center)
Scroll() {
Column() {
// 操作选择
Column() {
Text('选择操作')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 12 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.operations, (op: { name: string; value: string }) => {
Button(op.name)
.layoutWeight(1)
.height(40)
.margin({ right: 8, bottom: 8 })
.backgroundColor(this.selectedOperation === op.value ? '#1A237E' : '#E0E0E0')
.fontColor(this.selectedOperation === op.value ? '#FFFFFF' : '#333333')
.fontSize(12)
.onClick(() => {
this.selectedOperation = op.value;
this.result = '';
this.resultTitle = '';
})
})
}
.width('100%')
}
.width('95%')
.margin({ top: 16, left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
// URL 输入区域
Column() {
Text('输入 URL')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 8 })
TextInput({ placeholder: '输入要处理的 URL', text: this.inputUrl })
.onChange((value) => this.inputUrl = value)
.width('100%')
.height(80)
.padding(12)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.fontSize(12)
.margin({ bottom: 12 })
if (this.selectedOperation === 'encode' || this.selectedOperation === 'decode') {
TextInput({ placeholder: '输入要处理的文本', text: this.parameter })
.onChange((value) => this.parameter = value)
.width('100%')
.height(50)
.padding(12)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.fontSize(12)
}
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
// 操作按钮
Row() {
Button('✨ 处理')
.layoutWeight(1)
.height(44)
.backgroundColor('#1A237E')
.fontColor('#FFFFFF')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.borderRadius(6)
.onClick(() => this.executeOperation())
Blank()
.width(12)
Button('🔄 清空')
.layoutWeight(1)
.height(44)
.backgroundColor('#F5F5F5')
.fontColor('#1A237E')
.fontSize(14)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.onClick(() => {
this.inputUrl = '';
this.parameter = '';
this.result = '';
this.resultTitle = '';
})
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
// 结果显示
if (this.resultTitle) {
Column() {
Text(this.resultTitle)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(12)
.backgroundColor('#1A237E')
.borderRadius(6)
.textAlign(TextAlign.Center)
.margin({ bottom: 12 })
Scroll() {
Text(this.result)
.fontSize(12)
.fontColor('#333333')
.fontFamily('monospace')
.textAlign(TextAlign.Start)
.width('100%')
.padding(12)
.selectable(true)
}
.width('100%')
.height(300)
.backgroundColor('#F9F9F9')
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
}
}
.width('100%')
}
.layoutWeight(1)
.width('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
private executeOperation() {
if (!this.inputUrl.trim()) {
this.resultTitle = '❌ 错误';
this.result = '请输入 URL';
return;
}
try {
switch (this.selectedOperation) {
case 'validate':
const isValid = this.urlProcessor.isValidUrl(this.inputUrl);
this.resultTitle = isValid ? '✅ URL 有效' : '❌ URL 无效';
this.result = `URL: ${this.inputUrl}\n状态: ${isValid ? '有效' : '无效'}`;
break;
case 'parse':
const parsed = this.urlProcessor.parseUrl(this.inputUrl);
this.resultTitle = '📋 URL 解析结果';
this.result = Object.entries(parsed)
.map(([key, value]) => `${key}: ${value}`)
.join('\n');
break;
case 'encode':
const text = this.parameter || this.inputUrl;
this.resultTitle = '🔐 URL 编码结果';
this.result = `原文本: ${text}\n编码后: ${this.urlProcessor.encodeUrl(text)}`;
break;
case 'decode':
const encoded = this.parameter || this.inputUrl;
this.resultTitle = '🔓 URL 解码结果';
this.result = `编码文本: ${encoded}\n解码后: ${this.urlProcessor.decodeUrl(encoded)}`;
break;
case 'params':
const params = this.urlProcessor.getQueryParams(this.inputUrl);
this.resultTitle = '📍 查询参数';
if (Object.keys(params).length === 0) {
this.result = '没有查询参数';
} else {
this.result = Object.entries(params)
.map(([key, value]) => `${key}: ${value}`)
.join('\n');
}
break;
case 'normalize':
this.resultTitle = '✨ 规范化结果';
this.result = `原 URL: ${this.inputUrl}\n规范化: ${this.urlProcessor.normalizeUrl(this.inputUrl)}`;
break;
case 'hostname':
this.resultTitle = '🏠 主机名';
this.result = `主机名: ${this.urlProcessor.getHostname(this.inputUrl)}`;
break;
case 'path':
this.resultTitle = '📂 路径';
this.result = `路径: ${this.urlProcessor.getPath(this.inputUrl)}`;
break;
}
} catch (e) {
this.resultTitle = '❌ 处理出错';
this.result = `错误: ${e}`;
}
}
}
ArkTS 集成的关键要点
在 OpenHarmony 应用中集成 URL 处理工具库需要考虑多种 URL 操作和用户体验。我们设计了一个灵活的 UI,能够支持不同的 URL 处理操作。
操作选择界面使用了 Flex 布局和 FlexWrap 来实现响应式的按钮排列。当用户选择不同的操作时,输入区域会动态显示相应的输入字段。
对于需要额外参数的操作(如 URL 编码和解码),我们提供了额外的输入框。结果显示使用了可选择的文本,这样用户可以轻松复制处理结果。
对于查询参数提取,我们进行了检查,如果没有查询参数则显示相应的提示信息。所有结果都进行了适当的格式化,以提高可读性。
工作流程详解
URL 处理的完整流程
- 操作选择: 用户在 ArkTS UI 中选择要执行的 URL 操作
- URL 输入: 用户输入要处理的 URL
- 参数输入: 根据选择的操作输入必要的参数
- 处理执行: 调用 UrlProcessor 的相应方法
- 结果展示: 将处理结果显示在 UI 中
跨平台一致性
通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,URL 处理的逻辑和结果都是完全相同的。
实际应用场景
Web 爬虫和数据采集
在 Web 爬虫中,需要解析和处理大量的 URL。这个工具库提供了必要的 URL 处理功能。
API 客户端
在构建 API 客户端时,需要动态构建 URL 和处理查询参数。这个工具库提供了这些功能。
链接分享应用
在链接分享应用中,需要验证、规范化和处理 URL。这个工具库提供了这些功能。
网络监控工具
在网络监控工具中,需要分析和处理 URL。这个工具库提供了必要的分析功能。
性能优化
缓存解析结果
在频繁处理相同 URL 时,可以缓存解析结果以避免重复计算。
批量处理
在处理大量 URL 时,应该考虑使用批量处理的方式以提高效率。
安全性考虑
URL 验证
在处理用户输入的 URL 时,应该进行验证,确保 URL 的有效性和安全性。
编码处理
在进行 URL 编码时,应该确保特殊字符被正确处理,避免安全漏洞。
总结
这个 KMP OpenHarmony URL处理工具库展示了如何使用现代的跨平台技术来处理常见的 URL 操作任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。
URL 处理是网络应用开发中的基础技能。通过使用这样的工具库,开发者可以快速、可靠地处理各种 URL 操作,从而提高开发效率和代码质量。
在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更复杂的 URL 验证规则、支持国际化域名等高级特性。同时,定期进行安全审计,确保 URL 处理的安全性。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)