todo: 使用融云imserve做登录(android)
使用融云做登录注册思路
- 注册界面需要name, email, password
- 考虑到融云注册用户的post格式
POST http://api.rong-api.com/user/getToken.json?userId=1690544550qqcom&name=Ironman
- 这里的userId可以使用用户的email,但是要截断
@
和.
符号,即1690544550@qq.com -> 1690544550qqcom
, 融云不让我传完整的邮箱地址,这里可能要考虑到奇怪的邮箱地址,如fuckerfucker@fucker.edu.cn
,我不确定截断后的是fuckerfuckerfuckereducn
能不能还原成邮箱地址 - 注册的name传入post 参数的name
- 融云注册返回格式
{
"code": 200,
"userId": "1690544550qqcom",
"token": "测试"
}
- 那android注册界面需要检查返回的response的
code==200
和token!=null
, 如果ok,注册用户成功 - 登录界面需要email和password, 这里
可能还要考虑到自己搭建服务后端来存取用户基本信息(email, password)
- 但是查到融云一条api是关于查询用户信息的
POST http://api.rong-api.com/user/info.json?userId=1690544550qqcom
接口返回
{
"code": 200,
"userName": "Ironman",
"userPortrait": "http://abc.com/myportrait.jpg",
"createTime": "2025-03-05 11:46:49"
}
- 现在考虑怎么
校验密码
那一块,校验密码能不能本地校验
,就是注册的时候拿到email和password存到手机本地数据库,到登录时,需要用userId请求融云用户信息,检查code==200, 密码就比对本地数据库的email, password一对,好像相对繁琐了 - 登录成功后跳转界面
- 完啦,注册的页面还要考虑到验证邮箱地址正确,就是发邮箱验证码,这不知道怎么入手
todo实现以上思路
1. 使用go语言简单搭建一个邮箱验证码服务,邮箱服务来自qq邮箱
- 后端地址: https://gitee.com/EEPPEE_admin/dont-want-to-write-code-anymore/tree/master/send_code_to_mail
- 配置自己的qqmail 授权码还有自己的发送邮箱地址
- 提供接口
### 发送验证码,这里的email是从adnroid客户端注册用户获取
POST http://localhost:8080/send-code?email=1876056356@qq.com
### 验证验证码, 738702 是验证码,有发送验证码获取,随机的,这里的email是从adnroid客户端注册用户获取
POST http://localhost:8080/verify-code?email=1876056356@qq.com&code=6271
2. 搭建data层调式邮箱验证码
- 先启动后端go run main.go
- 创建data/network/EmailVerifyCodeApiService.kt
// Base url:https://7f498cbc.r7.cpolar.top
interface EmailVerifyCodeApiService {
@POST("send-code")
fun sendVerificationCode(
@Query("email") email: String
): Call<VerifyCodeResponse>
@POST("verify-code")
fun verifyEmailCode(
@Query("email") email: String,
@Query("code") code: String
): Call<VerifyCodeResponse>
}
// 邮箱验证码的返回
data class VerifyCodeResponse(
val message: String
)
object EmailVerifyCodeApiClient {
// todo: 这个base url随时变动,根据你的cpolar地址来
private const val BASE_URL = "https://7f498cbc.r7.cpolar.top"
private var apiService: EmailVerifyCodeApiService? = null
val emailVerifyCodeApiService: EmailVerifyCodeApiService?
get() {
if (apiService == null) {
val client: OkHttpClient = OkHttpClient.Builder()
.build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
apiService = retrofit.create(EmailVerifyCodeApiService::class.java)
}
return apiService
}
}
- 测试点击发送邮件事件(这个简单界面代码就不给出了,就一个按钮,一个EditText,使用viewbinding)
binding.sendBtn.setOnClickListener {
val email = binding.emailEt.text.toString().trim()
// todo: 处理发送邮箱验证码
val apiService = EmailVerifyCodeApiClient.emailVerifyCodeApiService
val call = apiService?.sendVerificationCode(
email
)
call!!.enqueue(object : retrofit2.Callback<VerifyCodeResponse> {
override fun onResponse(
call: Call<VerifyCodeResponse>,
response: Response<VerifyCodeResponse>
) {
Log.d("注册", response.body().toString())
Log.d("注册", "发送邮箱验证码成功")
}
override fun onFailure(call: Call<VerifyCodeResponse>, t: Throwable) {
Log.d("注册", "发送验证码失败")
}
})
}
- 测试邮箱验证码发送通过
3. 调试融云api
- 创建data/network/RongCloudApiService.kt
// todo: 构建融云api服务, 接口里面的方法可以是suspend和普通函数
/*
普通函数 vs. suspend 函数
普通函数:
使用回调(Callback)或 Call 对象来处理异步请求。
适用于传统的异步编程模型。
代码相对复杂,需要处理回调和线程切换。
suspend 函数:
使用 Kotlin 协程来处理异步请求。
代码更简洁,更接近同步编程风格。
需要结合 CoroutineScope 或其他协程工具来调用。
示例代码
*/
interface RongCloudApiService {
@FormUrlEncoded
@POST("user/getToken.json")
fun getToken(
// 这是x-form形式
@Field("userId") userId: String, // todo: required
@Field("name") name: String = "",
@Field("portraitUri") portraitUri: String = ""
): Call<GetTokenResponse>
// @POST("user/info.json")
// fun getRongCloudUserInfo(
// @Query("userId") userId: String
// ): Response<RongCloudUserInfo>
@FormUrlEncoded
@POST("user/info.json")
fun getUserInfo(
@Field("userId") userId: String,
): Call<UserInfoResponse>
}
// user/getToken.json接口的返回
data class GetTokenResponse(
val code: Int,
val userId: String,
val token: String?
)
// user/info.json接口的返回
data class UserInfoResponse(
val code: Int,
val userName: String,
val userPortrait: String?,
val createTime: String
)
object RongCloudApiClient {
// 要求不能明文传输,改成https
private const val BASE_URL = "https://api.rong-api.com/"
private const val APP_KEY = "你自己的"
private const val NONCE = "1442907399"
private const val TIMESTAMP = "1741092643"
private const val SIGNATURE = "你自己的"
private var apiService: RongCloudApiService? = null
// todo: 获取实例方法
val rongIMApiService: RongCloudApiService?
get() {
if (apiService == null) {
val client: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(Interceptor { chain ->
val originalRequest: Request = chain.request()
val newRequest: Request = originalRequest.newBuilder()
.header("App-Key", APP_KEY)
.header("Nonce", NONCE)
.header("Timestamp", TIMESTAMP)
.header("Signature", SIGNATURE)
.header("Content-Type", "application/x-www-form-urlencoded")
.build()
chain.proceed(newRequest)
})
.build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
apiService = retrofit.create(RongCloudApiService::class.java)
}
return apiService
}
}
- 调试代码,点击一个注册按钮,观察Logcat
binding.registerBtn.setOnClickListener {
val email = binding.emailEt.text.toString().trim()
Log.d("注册", email)
val name = binding.nameEt.text.toString().trim()
Log.d("注册", name)
val password = binding.passwordEt.text.toString().trim()
Log.d("注册", password)
val mailVerifyCode = binding.mailVerifycodeEt.text.toString().trim()
Log.d("注册", mailVerifyCode)
if (name.isEmpty() || email.isEmpty() || password.isEmpty() || mailVerifyCode.isEmpty()) {
Toast.makeText(this, "请填充完整信息", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
// email 处理成 userId
val processedEmailAsUserId = EmailUtil.convertEmailToRongCloudId(email)
Log.d("注册", processedEmailAsUserId)
// todo 取到接口实例
val apiService = RongCloudApiClient.rongIMApiService
// todo 调用api service
val call = apiService?.getToken(
processedEmailAsUserId,
name
)
// todo call 进入队列
call!!.enqueue(object : retrofit2.Callback<GetTokenResponse> {
override fun onResponse(
call: Call<GetTokenResponse>,
response: retrofit2.Response<GetTokenResponse>
) {
Log.d("注册", response.body().toString())
//RongCloudTokenResponse(code=200, userId=13266263124qqcom, token=Seq1kNg6Uft4UOBPzi2gGqh30okEsnM0kPAf6ohCvgf1vll6L9v54A==@ptfq.cn.rongnav.com;ptfq.cn.rongcfg.com)
}
override fun onFailure(call: Call<GetTokenResponse>, t: Throwable) {
Log.d("注册", "获取token失败")
}
})
// todo new call 进入队列
val newcall = apiService?.getUserInfo(
processedEmailAsUserId
)
newcall!!.enqueue(object : retrofit2.Callback<UserInfoResponse> {
override fun onResponse(
call: Call<UserInfoResponse>,
response: Response<UserInfoResponse>
) {
Log.d("注册", response.body().toString())
// UserInfoResponse(code=200, userName=好, userPortrait=http://abc.com/myportrait.jpg, createTime=2025-03-05 16:51:19)
}
override fun onFailure(call: Call<UserInfoResponse>, t: Throwable) {
Log.d("注册", "获取用户信息失败")
}
})
}