热土用户介绍
什么是热土用户?
热土用户(RTUser)是由热土工作室开发的一套统一用户认证与授权系统,旨在为开发者提供便捷、安全、可靠的用户登录解决方案。通过热土用户,您的软件可以快速接入统一的用户账号体系,无需自行搭建复杂的用户管理系统。
安全认证
支持state参数防CSRF攻击,Token有效期管理完善
快速接入
提供完善的SDK和详细的接入文档,30分钟内完成基础接入
数据存储
支持用户数据段存储,轻松实现跨设备数据同步
多端支持
支持桌面应用、Web应用等多种应用类型接入
系统架构
所有API请求通过代理层转发,确保安全性和稳定性。代理地址:https://appapi.rtstudio.top/
核心流程
用户发起登录
应用打开授权登录页面,用户输入账号密码
授权确认
用户确认授权,系统生成访问Token
Token回调
Token通过回调返回给应用
获取用户信息
应用使用Token调用API获取用户信息
产品优势
为什么选择热土用户?
统一账号体系
用户只需一个热土账号,即可登录所有接入热土用户的软件,无需在每个软件单独注册账号。
安全可靠
采用业界标准的OAuth授权流程,支持state参数防CSRF攻击,完善的Token有效期管理机制。
开箱即用
提供完整的SDK、详细的接入文档和示例代码,开发者可快速完成接入,无需搭建复杂的用户系统。
数据同步
通过数据段功能,用户数据可在不同设备间同步,提升用户体验。
可视化管理
开发者可通过仪表盘管理软件Token、查看授权用户、发布Beta测试等。
已验证与未验证软件
| 特性 | 已验证软件 | 未验证软件 |
|---|---|---|
| 登录提示 | 显示软件名称和开发者信息 | 显示风险警告 |
| 数据段功能 | 支持(最多10个) | 不支持 |
| 用户信任度 | 高 | 较低 |
| 仪表盘显示 | 已授权应用 | 未授权应用 |
适用场景
典型的应用场景
桌面应用
各类Windows/Mac桌面软件,通过本地HTTP服务器接收登录回调,实现用户认证。
Web应用
网站和Web应用,通过URL重定向接收Token回调,快速实现用户登录。
开发工具
IDE插件、命令行工具等开发者工具,快速接入用户体系。
游戏与娱乐
游戏、娱乐类软件,利用数据段实现设置保存。
不适合的场景
- 需要高度定制化认证流程的应用
- 对用户数据有特殊合规要求的金融、医疗类应用
- 需要完全自主控制用户数据的应用
接入指南
快速接入流程
开发阶段
集成SDK或自行实现登录流程
- 桌面应用:启动本地HTTP服务器监听回调
- Web应用:配置回调URL
- 调用API获取用户信息
测试阶段
使用测试账号验证登录流程
- 验证Token有效性
- 测试用户信息获取
- 测试数据段功能(如需要)
上线阶段
发布软件,用户可使用热土账号登录
登录URL参数说明
| 参数 | 必填 | 说明 |
|---|---|---|
port |
是 | 桌面应用:本地端口;Web应用:回调网址 |
type |
是 | token(已验证)或 name(未验证) |
v |
是 | 软件Token或软件名称 |
state |
是 | 防CSRF的随机字符串(建议16字节) |
env |
可选 | app(桌面)或 web(Web应用) |
https://apilogin.rtstudio.top/?port=3456&type=token&v=your_token&state=random_state&env=app
https://apilogin.rtstudio.top/?port=yourapp.com/callback&type=token&v=your_token&state=random_state&env=web
登录URL详解
URL基本结构
热土用户的登录授权URL遵循以下基本结构:
https://apilogin.rtstudio.top/?[参数列表]
所有参数通过URL查询字符串(Query String)传递,参数之间使用 & 符号分隔。
完整参数说明
| 参数名 | 必填 | 类型 | 说明 |
|---|---|---|---|
port |
必填 | string |
回调接收地址 桌面应用:本地HTTP服务器端口(如 3456)Web应用:完整的回调URL路径(如 yourapp.com/callback)
|
type |
必填 | string |
软件标识类型token - 已验证软件,使用软件Token标识name - 未验证软件,使用软件名称标识
|
v |
必填 | string |
软件标识值 当 type=token 时:填写软件Token当 type=name 时:填写软件名称
|
state |
必填 | string |
防CSRF攻击的随机字符串 建议使用16字节以上的随机值 回调时会原样返回,用于验证请求来源 |
env |
可选 | string |
应用环境类型app - 桌面应用(默认)web - Web应用影响登录页面的UI展示和回调处理方式 |
完整URL示例
桌面应用(已验证软件)
https://apilogin.rtstudio.top/?port=3456&type=token&v=abc123def456&state=a1b2c3d4e5f6g7h8&env=app
桌面应用(未验证软件)
https://apilogin.rtstudio.top/?port=3456&type=name&v=我的测试软件&state=random_state_123&env=app
type=name 时,登录页面会显示风险警告提示,建议申请已验证认证。
Web应用
https://apilogin.rtstudio.top/?port=https://yourapp.com/auth/callback&type=token&v=abc123def456&state=random_state&env=web
port 参数需要填写完整的回调URL,用户登录成功后会重定向到此地址。
回调URL详解
用户完成登录授权后,系统会将结果通过回调URL返回给您的应用。
桌面应用回调
系统会向您的本地HTTP服务器发送GET请求:
http://127.0.0.1:3456/?type=success&v=USER_ACCESS_TOKEN&state=YOUR_STATE
http://127.0.0.1:3456/?type=error&v=错误信息&state=YOUR_STATE
| 回调参数 | 说明 |
|---|---|
type |
success 表示成功,error 表示失败 |
v |
成功时为用户访问Token,失败时为错误信息 |
state |
您在登录URL中传入的state值,用于验证 |
Web应用回调
系统会将用户重定向到您指定的回调URL:
https://yourapp.com/auth/callback?type=success&v=USER_ACCESS_TOKEN&state=YOUR_STATE
您需要在回调页面解析URL参数,获取Token并验证state一致性。
State参数安全说明
state 参数是防止CSRF(跨站请求伪造)攻击的关键机制,请务必正确使用:
- 每次登录请求生成新的随机state值
- 建议使用16字节以上的随机字符串
- 在接收回调时,验证返回的state与发送时一致
- 验证失败时应拒绝此次登录
State生成示例
func generateState() (string, error) {
b := make([]byte, 16)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
import secrets
def generate_state():
return secrets.token_hex(16) # 生成32字符的随机字符串
Token使用说明
获取到用户访问Token后,您可以:
- 调用
/verify-access-token验证Token有效性 - 调用
/get-user-by-token获取用户基本信息 - 调用数据段相关API(仅已验证软件)
URL编码注意事项
当参数值包含特殊字符时,需要进行URL编码:
| 场景 | 原始值 | 编码后 |
|---|---|---|
| 软件名称含中文 | 我的测试软件 | %E6%88%91%E7%9A%84%E6%B5%8B%E8%AF%95%E8%BD%AF%E4%BD |
| 回调URL含特殊字符 | app.com/auth?type=login | app.com%2Fauth%3Ftype%3Dlogin |
const softwareName = '我的测试软件';
const encodedName = encodeURIComponent(softwareName);
const loginUrl = `https://apilogin.rtstudio.top/?port=3456&type=name&v=${encodedName}&state=random_state`;
开发准备
获取软件Token
- 软件名称
- 开发者信息
- 软件功能描述
- 软件截图(如有)
- 下载链接(如有)
发送邮件至 miles@rtstu.com
邮件主题:软件认证申请 - [软件名称]
审核周期:1-3个工作日
审核通过后将收到包含软件Token的邮件
环境要求
| 应用类型 | 要求 |
|---|---|
| 桌面应用 | 能够启动本地HTTP服务器、打开浏览器 |
| Web应用 | 能够处理URL重定向和查询参数 |
API地址
API概览
API列表
| 接口 | 方法 | 说明 | 权限要求 |
|---|---|---|---|
/verify-software-token |
POST | 验证软件Token | 无 |
/verify-access-token |
POST | 验证用户Token | 无 |
/get-user-by-token |
POST | 获取用户信息 | 有效Token |
/data-segments |
POST | 创建数据段 | 已验证软件 |
/data-segments |
PUT | 修改数据段 | 已验证软件 |
/data-segments |
DELETE | 删除数据段 | 已验证软件 |
/data-segments/read |
POST | 读取数据段 | 已验证软件 |
通用响应格式
{
"success": true,
"message": "操作成功",
"data": {
// 具体数据
}
}
{
"success": false,
"message": "错误描述"
}
认证相关API
验证软件Token
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
token |
string | 是 | 软件Token |
响应示例
{
"success": true,
"data": {
"id": 1,
"name": "测试软件",
"developer": "测试开发者"
}
}
验证用户Token
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
token |
string | 是 | 用户授权Token |
响应示例
{
"success": true,
"data": {
"id": 1,
"user_id": 1,
"software_id": 1,
"expires_at": "2027-03-22T12:00:00Z"
}
}
获取用户信息
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
token |
string | 是 | 用户授权Token |
响应示例
{
"success": true,
"data": {
"id": 1,
"uuid": "...",
"username": "testuser",
"email": "test@example.com",
"email_verified": true,
"is_beta_user": false
}
}
数据段相关API
数据段功能设计用于存储用户偏好设置、软件配置、非关键业务数据等非敏感信息。
严禁使用数据段存储以下类型的数据:
- VIP/会员状态、订阅权限等核心业务判定数据
- 付费信息、充值记录、消费数据
- 用户等级、积分、特权等可影响业务逻辑的数据
- 密码、密钥、证书等安全凭证
- 个人身份信息(身份证、银行卡等)
原因:数据段存储在用户可访问的系统中,用户可能查看或修改这些数据。将VIP状态等关键数据存储在数据段中,将导致用户可以自行"解锁"VIP功能,造成业务风险。
正确做法:VIP状态、付费信息等核心业务数据应由您的后端服务器独立管理,通过您自己的API进行验证,不应依赖热土用户系统的数据段功能。
数据段说明
创建数据段
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
email |
string | 是 | 用户邮箱 |
token |
string | 是 | 用户授权Token |
name |
string | 是 | 数据段名称(唯一标识) |
content |
string | 是 | 数据段内容(JSON字符串) |
响应示例
{
"success": true,
"message": "数据段创建成功",
"data": {
"id": 1,
"name": "user_settings",
"content": "{\"theme\": \"dark\"}"
}
}
读取数据段
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
email |
string | 是 | 用户邮箱 |
token |
string | 是 | 用户授权Token |
name |
string | 是 | 数据段名称 |
响应示例
{
"success": true,
"data": {
"id": 1,
"name": "user_settings",
"content": "{\"theme\": \"dark\", \"language\": \"zh-CN\"}",
"created_at": "2026-03-22T12:00:00Z",
"updated_at": "2026-03-22T12:00:00Z"
}
}
修改数据段
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
email |
string | 是 | 用户邮箱 |
token |
string | 是 | 用户授权Token |
name |
string | 是 | 数据段名称 |
content |
string | 是 | 新的数据段内容 |
删除数据段
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
email |
string | 是 | 用户邮箱 |
token |
string | 是 | 用户授权Token |
name |
string | 是 | 数据段名称 |
适合存储的数据示例
✅ 用户偏好设置
{"theme": "dark", "language": "zh-CN", "fontSize": 14}
✅ 软件窗口布局
{"width": 800, "height": 600, "x": 100, "y": 100}
✅ 游戏非关键数据
{"ImageQuality": 5, "controls": "default"}
不适合存储的数据示例
❌ VIP状态(错误示例)
{"isVIP": true, "vipLevel": 3, "vipExpires": "2027-12-31"}
⚠️ 用户可以自行修改为VIP状态,绕过付费验证
❌ 用户积分(错误示例)
{"points": 9999, "level": 10, "achievements": ["all"]}
⚠️ 用户可以自行修改积分和等级
Go SDK
完整登录流程实现
以下是完整的Go语言SDK实现代码,包含登录流程、Token验证、用户信息获取等功能。
// main.go
package main
import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
"os/exec"
"runtime"
"time"
)
const apiBaseURL = "https://appapi.rtstudio.top"
var loginType string
var value string
type verifyResponse struct {
Success bool `json:"success"`
Data any `json:"data"`
Message string `json:"message"`
}
func main() {
flag.StringVar(&loginType, "type", "", "登录类型: token 或 name")
flag.StringVar(&value, "v", "", "软件token或软件名称")
flag.Parse()
if loginType != "token" && loginType != "name" {
log.Fatal("-type 必须是 'token' 或 'name'")
}
if value == "" {
log.Fatal("-v 参数不能为空")
}
token, err := startLoginFlow(loginType, value)
if err != nil {
log.Fatalf("登录失败: %v", err)
}
userResult, err := getUserByToken(token)
if err != nil {
log.Fatalf("获取用户信息失败: %v", err)
}
if userResult.Success {
log.Printf("登录成功,用户: %+v", userResult.Data)
}
}
// startLoginFlow 启动登录流程
func startLoginFlow(loginType, value string) (string, error) {
state, err := generateState()
if err != nil {
return "", fmt.Errorf("生成state失败: %v", err)
}
port, err := findAvailablePort()
if err != nil {
return "", fmt.Errorf("无法找到可用端口: %v", err)
}
tokenChan := make(chan string, 1)
errChan := make(chan error, 1)
server := &http.Server{
Addr: fmt.Sprintf(":%d", port),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
callbackType := query.Get("type")
callbackValue := query.Get("v")
callbackState := query.Get("state")
if callbackType == "success" {
if callbackState != state {
errChan <- fmt.Errorf("state验证失败")
http.Error(w, "state验证失败", http.StatusBadRequest)
return
}
tokenChan <- callbackValue
w.Write([]byte(`✅ 登录完成
请关闭此窗口
`))
go func() {
time.Sleep(500 * time.Millisecond)
server.Close()
}()
} else if callbackType == "error" {
errChan <- fmt.Errorf("登录失败: %s", callbackValue)
go server.Close()
}
})
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
errChan <- fmt.Errorf("HTTP服务器错误: %v", err)
}
}()
time.Sleep(100 * time.Millisecond)
loginURL := fmt.Sprintf("https://apilogin.rtstudio.top/?port=%d&type=%s&v=%s&state=%s&env=app",
port, loginType, value, state)
if err := openBrowser(loginURL); err != nil {
return "", fmt.Errorf("打开浏览器失败: %v", err)
}
select {
case token := <-tokenChan:
return token, nil
case err := <-errChan:
return "", err
case <-time.After(5 * time.Minute):
server.Close()
return "", fmt.Errorf("登录超时")
}
}
func generateState() (string, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
func findAvailablePort() (int, error) {
listener, err := net.Listen("tcp", ":0")
if err != nil {
return 0, err
}
defer listener.Close()
return listener.Addr().(*net.TCPAddr).Port, nil
}
func openBrowser(url string) error {
switch runtime.GOOS {
case "windows":
return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
case "darwin":
return exec.Command("open", url).Start()
default:
return exec.Command("xdg-open", url).Start()
}
}
func getUserByToken(token string) (*verifyResponse, error) {
reqBody := map[string]string{"token": token}
jsonBody, _ := json.Marshal(reqBody)
resp, err := http.Post(apiBaseURL+"/get-user-by-token", "application/json", bytes.NewBuffer(jsonBody))
if err != nil {
return nil, fmt.Errorf("API请求失败: %v", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result verifyResponse
json.Unmarshal(body, &result)
return &result, nil
}
使用方法
# 已验证软件
go run main.go -type token -v your_software_token
# 未验证软件
go run main.go -type name -v your_software_name
其他语言
Python示例
import requests
import webbrowser
import http.server
import socketserver
import threading
import secrets
import time
API_BASE_URL = "https://appapi.rtstudio.top"
def get_available_port():
with socketserver.TCPServer(("localhost", 0), None) as s:
return s.server_address[1]
def login(login_type, value):
state = secrets.token_hex(16)
port = get_available_port()
token = None
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
query = parse_qs(urlparse(self.path).query)
if query.get('type') == ['success'] and query.get('state') == [state]:
token = query.get('v', [None])[0]
self.send_response(200)
self.end_headers()
self.wfile.write(b'登录完成
')
server = socketserver.TCPServer(("localhost", port), Handler)
thread = threading.Thread(target=server.serve_forever)
thread.start()
url = f"https://apilogin.rtstudio.top/?port={port}&type={login_type}&v={value}&state={state}&env=app"
webbrowser.open(url)
time.sleep(30) # 等待登录
server.shutdown()
return token
def get_user_by_token(token):
resp = requests.post(f"{API_BASE_URL}/get-user-by-token", json={"token": token})
return resp.json()
更多SDK
更多语言的SDK正在开发中,如有需要请联系我们获取。
常见问题
软件Token用于标识软件身份,由热土工作室颁发;用户Token是用户登录后生成的访问令牌,用于获取用户信息。
用户Token有效期为1年,过期后需重新登录授权。
发送邮件至 miles@rtstu.com,包含软件名称、开发者信息、功能描述等,审核周期1-3个工作日。
不可以。数据段不适合存储VIP状态、付费信息等核心业务数据,因为这些数据用户可能查看或修改,会导致业务风险。VIP状态等应由您的后端独立管理。
state参数用于防止CSRF攻击,建议使用16字节以上的随机字符串,回调时验证state一致性。
联系我们
成为开发者
- 自主创建和管理软件Token
- 发布Beta测试软件
- 查看授权用户数据统计
- 使用开发者控制台(id.rtstu.com)
加入热土工作室QQ群
私信群主申请加入
入群后,私信群主表达您希望成为开发者的意愿。
必要条件
个人开发者
- 拥有一个热土用户账号
- 拥有对外展示作品的公开渠道
- 支持的渠道:X(Twitter)、Bilibili、GitHub、个人网站、博客等
- 不支持:仅限微信朋友圈、私密社群等非公开渠道
团队/组织
- 满足个人开发者的所有条件
- 拥有团队/组织的官方账号(非个人账号)
- 例如:Bilibili团队官方账号、GitHub组织账号等
- 有明确的团队负责人作为主要联系人
提交材料并确认协议
向群主提交以下材料:
- 您的热土用户账号(邮箱或用户名)
- 展示渠道链接(如Bilibili主页、GitHub主页等)
- 团队需额外提供:团队名称、团队官方账号链接、团队负责人信息
材料审核通过后,需确认开发者协议,即可获得开发者权限。
登录开发者控制台
完成以上步骤后,访问 id.rtstu.com 并选择「开发者」,即可开始管理您的软件。
软件认证申请(邮箱方式)
软件认证申请 - [您的软件名称]
- 软件名称
- 开发者/团队名称
- 软件功能描述(简要)
- 软件截图(如有)
- 下载链接(如有)
miles@rtstu.com
1-3个工作日,审核通过后将收到包含软件Token的邮件回复。