191 行
4.3 KiB
Markdown
191 行
4.3 KiB
Markdown
|
|
# iOS 授权管理(License SDK)
|
|||
|
|
|
|||
|
|
**模块**:`LicenseSDK`(包含在 `XuqmSDK` 中)· **最低 iOS 版本**:iOS 16
|
|||
|
|
|
|||
|
|
License SDK 用于设备授权注册与验证,Android / iOS / RN / Flutter 共用同一个 AppKey,设备数量统一计算。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 快速接入
|
|||
|
|
|
|||
|
|
### 1. 添加依赖
|
|||
|
|
|
|||
|
|
License 模块已内置于 `XuqmSDK`,无需额外安装包:
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
// Package.swift 或 Xcode → File → Add Package Dependencies
|
|||
|
|
// URL: https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK
|
|||
|
|
import XuqmSDK
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 放置 License 文件
|
|||
|
|
|
|||
|
|
从租户平台下载 `.xuqmlicense` 加密文件,放入 App Bundle:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
MyApp/Resources/xuqm/license.xuqm
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在 Xcode 中将该文件添加到 Target → Build Phases → Copy Bundle Resources,确保它出现在 App Bundle 中。SDK 会自动读取 `Bundle.main` 中的 `xuqm/license.xuqm`。
|
|||
|
|
|
|||
|
|
### 3. 检查授权
|
|||
|
|
|
|||
|
|
SDK **无需手动初始化**,首次调用 `checkLicense()` 时自动读取并解密 License 文件:
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
import XuqmSDK
|
|||
|
|
|
|||
|
|
Task {
|
|||
|
|
let result = await LicenseSDK.shared.checkLicense()
|
|||
|
|
switch result {
|
|||
|
|
case .success(let reason):
|
|||
|
|
print("授权通过:\(reason)")
|
|||
|
|
case .error(let message):
|
|||
|
|
print("授权失败:\(message)")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## API 说明
|
|||
|
|
|
|||
|
|
### checkLicense
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
func checkLicense(userInfo: LicenseUserInfo? = nil) async -> LicenseResult
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**内部逻辑**:
|
|||
|
|
1. 检查本地缓存(默认 10 分钟有效期)
|
|||
|
|
2. 缓存有效 → 直接返回 `.success`
|
|||
|
|
3. 缓存过期或无缓存:
|
|||
|
|
- 有 Token → 调用验证接口
|
|||
|
|
- 无 Token 或验证失败 → 调用注册接口
|
|||
|
|
4. 网络异常且缓存曾成功 → 返回 `.success("Offline - cached ok")`
|
|||
|
|
5. 网络异常且无缓存 → 返回 `.error`
|
|||
|
|
|
|||
|
|
### getStatus
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
func getStatus() -> LicenseStatus // .ok / .denied / .unknown
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
同步查询当前状态(不发起网络请求)。
|
|||
|
|
|
|||
|
|
### getDeviceId
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
func getDeviceId() -> String?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### clear
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
func clear()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
清除所有本地授权数据(token、deviceId、状态)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 携带用户信息
|
|||
|
|
|
|||
|
|
注册时可携带业务用户信息,方便在租户平台进行设备管理:
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
let userInfo = LicenseUserInfo(
|
|||
|
|
userId: "user_001",
|
|||
|
|
name: "张三",
|
|||
|
|
email: "zhangsan@company.com"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
let result = await LicenseSDK.shared.checkLicense(userInfo: userInfo)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 设备唯一码
|
|||
|
|
|
|||
|
|
| 优先级 | 来源 | 说明 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| 1 | `UIDevice.identifierForVendor` | 同一 App 卸载重装不变(同 Vendor)|
|
|||
|
|
| 2 | 首次生成 UUID 存入 Keychain | identifierForVendor 不可用时 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 数据存储
|
|||
|
|
|
|||
|
|
| 数据 | 存储方式 | 说明 |
|
|||
|
|
|------|----------|------|
|
|||
|
|
| deviceId | Keychain(Security framework)| 加密持久化 |
|
|||
|
|
| token | Keychain | 加密持久化 |
|
|||
|
|
| 授权状态 | UserDefaults(Suite: `xuqm_license`)| 非敏感 |
|
|||
|
|
| statusTime | UserDefaults | 缓存有效期判断 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 离线模式
|
|||
|
|
|
|||
|
|
- 首次激活需要网络连接
|
|||
|
|
- 激活成功后,10 分钟缓存内可离线使用
|
|||
|
|
- 网络异常时,若历史缓存成功,继续返回授权通过
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 手动初始化(高级用法)
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
// 不使用 License 文件,手动指定 AppKey
|
|||
|
|
LicenseSDK.shared.initialize(
|
|||
|
|
appKey: "your_app_key",
|
|||
|
|
baseUrl: "https://auth.dev.xuqinmin.com",
|
|||
|
|
deviceName: "我的 iPhone"
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 完整示例
|
|||
|
|
|
|||
|
|
```swift
|
|||
|
|
import XuqmSDK
|
|||
|
|
|
|||
|
|
@main
|
|||
|
|
struct MyApp: App {
|
|||
|
|
var body: some Scene {
|
|||
|
|
WindowGroup {
|
|||
|
|
ContentView()
|
|||
|
|
.task { await checkLicense() }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func checkLicense() async {
|
|||
|
|
let result = await LicenseSDK.shared.checkLicense(
|
|||
|
|
userInfo: LicenseUserInfo(userId: "user_001")
|
|||
|
|
)
|
|||
|
|
switch result {
|
|||
|
|
case .success:
|
|||
|
|
break // 授权通过,正常使用
|
|||
|
|
case .error(let msg):
|
|||
|
|
// 提示用户,限制功能入口
|
|||
|
|
print("License 验证失败: \(msg)")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 常见问题
|
|||
|
|
|
|||
|
|
**Q: checkLicense 返回 error 怎么办?**
|
|||
|
|
检查:
|
|||
|
|
- License 文件是否已添加到 Build Phases → Copy Bundle Resources
|
|||
|
|
- 文件路径是否为 `xuqm/license.xuqm`(大小写敏感)
|
|||
|
|
- 设备是否有网络连接(首次激活需要网络)
|
|||
|
|
- License 是否已过期或被管理员禁用
|
|||
|
|
|
|||
|
|
**Q: 不同平台可以用同一个 License 吗?**
|
|||
|
|
可以,所有平台共用同一个 AppKey,设备数量统一计算。
|