功能概述
掃碼登錄能力裆装,指的是開發(fā)者可在移動應(yīng)用內(nèi)使用此能力,拉取二維碼排作,用戶使用微信客戶端掃描二維碼后可以登錄此移動應(yīng)用。此能力可被應(yīng)用在多設(shè)備登錄亚情、智能硬件妄痪、電視盒子等場景。
第一步:
很簡單楞件,就是先創(chuàng)建應(yīng)用衫生,集成微信SDK。
第二步:
開始寫代碼土浸,首先APP通過IDiffDevOAuth.auth()接口發(fā)起授權(quán)罪针,然后在OAuthListener.onAuthGotQrcode()回調(diào)接口中獲取二維碼,在APP中展示二維碼黄伊,最后用戶通過微信掃碼泪酱,授權(quán)。
lateinit var oauth: IDiffDevOAuth
override fun onCreate(savedInstanceState: Bundle?) {
//初始化oauth
oauth = DiffDevOAuthFactory.getDiffDevOAuth()
}
override fun onDestroy() {
super.onDestroyView()
oauth.removeAllListeners()
oauth.detach()
}
初始化完成后还最,就要掉接口了墓阀。
接口
IDiffDevOAuth
boolean auth(String appId, String scope, String noncestr, String timestamp, String signature, OAuthListener listener)
參數(shù)說明
參數(shù) | 是否必須 | 說明 |
---|---|---|
appId | 是 | 應(yīng)用唯一標(biāo)識 |
scope | 是 | 應(yīng)用授權(quán)作用域,擁有多個作用域用逗號(,)分隔拓轻,APP所擁有的scope |
noncestr | 是 | 一個隨機(jī)的盡量不重復(fù)的字符串斯撮,用來使得每次的signature不同 |
timestamp | 是 | 時間戳 |
signature | 是 | 簽名 |
listener | 是 | 授權(quán)流程,回調(diào)接口 |
appId:自己上應(yīng)用上查看悦即。
scope:填snsapi_userinfo吮成,官方demo中的snsapi_login和snsapi_base沒用橱乱,使用后會報一般錯誤。
noncestr:使用MD5加密一串隨機(jī)值
private fun getNonceStr(): String {
val r = Random(System.currentTimeMillis())
return EncryptUtils.encryptMD5ToString((Constant.WXAPPID +
r.nextInt(10000) + System.currentTimeMillis()).toByteArray())
}
timestamp:獲取當(dāng)前時間戳就行
private fun getTimestamp(): String {
return System.currentTimeMillis().toString()
}
signature:官方說明:生成簽名之前必須先獲取對應(yīng)的sdk_ticket粱甫。
sdk_ticket是用于生成簽名的臨時票據(jù)泳叠。正常情況下,sdk_ticket的有效期為7200秒茶宵,通過access_token來獲取危纫。由于獲取sdk_ticket的api調(diào)用次數(shù)非常有限,頻繁刷新sdk_ticket會導(dǎo)致api調(diào)用受限乌庶,影響自身業(yè)務(wù)种蝶,開發(fā)者需在自己的服務(wù)存儲與更新sdk_ticket。
- 先獲取token,grant_type參數(shù)固定填client_credential瞒大,另兩個參數(shù)在微信開放平臺應(yīng)用上獲取螃征。
/**
* 微信獲取Token
*/
@GET("https://api.weixin.qq.com/cgi-bin/token")
Observable<WXTokenEntity> getWXToken(
@Query("grant_type") String type,//固定值client_credential
@Query("appid") String appID,
@Query("secret") String appSecret
);
正常返回
{"access_token":"ACCESS_TOKEN","expires_in":7200}
錯誤返回(該示例為AppID無效錯誤)
{"errcode":40013,"errmsg":"invalid appid"}
官方地址
- 獲取到token之后,再請求sdk_ticket透敌,type固定值2
/**
* 微信獲取Ticket
*/
@GET("https://api.weixin.qq.com/cgi-bin/ticket/getticket")
Observable<WXTicketEntity> getTicket(
@Query("access_token") String token,
@Query("type") int type
);
成功返回
{
"errcode":0,
"errmsg":"ok",
"ticket":"-p3A5zVP95IuafPhzA6lRR95_F9nZEBfJ_n4E9t8ZFWKJTDPOwccVQhHCwDBmvLkayF_jh-m9HOExhumOziDWA",
"expires_in":7200
}
- 成功拿到ticket后盯滚,按簽名規(guī)則生成簽名。
簽名生成規(guī)則如下:
參與簽名的字段包括第三方appid酗电,noncestr(隨機(jī)字符串), 有效的sdk_ticket, timestamp(時間戳) 魄藕。
對所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1撵术。這里需要注意的是所有參數(shù)名均為小寫字符背率。對string1作sha1加密,字段名和字段值都采用原始值嫩与,不進(jìn)行URL 轉(zhuǎn)義寝姿。即signature=sha1(string1)。
示例:
appid=appid
noncestr=noncestr
sdk_ticket=-p3A5zVP95IuafPhzA6lRR95_F9nZEBfJ_n4E9t8ZFWKJTDPOwccVQhHCwDBmvLkayF_jh-m9HOExhumOziDWA
timestamp=1417508194
1.對所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)后蕴纳,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1:
appid=appid&noncestr=noncestr&sdk_ticket=-p3A5zVP95IuafPhzA6lRR95_F9nZEBfJ_n4E9t8ZFWKJTDPOwccVQhHCwDBmvLkayF_jh-m9HOExhumOziDWA×tamp=1417508194
2.對string1進(jìn)行sha1簽名会油,得到signature:
429eaaa13fd71efbc3fd344d0a9a9126835e7303
private fun getSignature(noncestr: String, timestamp: String, sdk_ticket: String): String {
val str = "appid=${Constant.WXAPPID}&noncestr=$noncestr&sdk_ticket=$sdk_ticket×tamp=$timestamp"
return EncryptUtils.encryptSHA1ToString(str)
}
注意這里是用SHA1加密的
- 最后調(diào)用接口
override fun auth(ticket: String) {
val noncestr = getNonceStr()
val timestamp = getTimestamp()
val signature = getSignature(noncestr, timestamp, ticket)
val authRet = oauth.auth(Constant.WXAPPID, "snsapi_userinfo", noncestr,
timestamp, signature, object : OAuthListener {
/**
* 用戶點(diǎn)擊授權(quán)后,回調(diào)改接口
*/
override fun onAuthFinish(errCode: OAuthErrCode?, authCode: String?) {
Timber.tag("OAuthListener").i("onAuthFinish,OAuthErrCode->$errCode ,authCode->$authCode")
val tips: String = when (errCode) {
OAuthErrCode.WechatAuth_Err_OK -> "登錄成功,code=$authCode"
OAuthErrCode.WechatAuth_Err_NormalErr -> "登錄失敗,一般錯誤"
OAuthErrCode.WechatAuth_Err_NetworkErr -> "登錄失敗,網(wǎng)絡(luò)錯誤"
OAuthErrCode.WechatAuth_Err_JsonDecodeErr -> "json解碼失敗"
OAuthErrCode.WechatAuth_Err_Cancel -> "用戶取消"
OAuthErrCode.WechatAuth_Err_Timeout -> "登錄失敗古毛,超時錯誤"
else -> ""
}
Timber.tag("OAuthListener").i(tips)
}
/**
* auth之后返回的二維碼接口
*
* @param qrcodeImgPath 廢棄
* @param imgBuf 二維碼圖片數(shù)據(jù)
*/
override fun onAuthGotQrcode(qrcodeImgPath: String?, imgBuf: ByteArray?) {
Timber.tag("OAuthListener").i("imgBuf->$imgBuf")
if (imgBuf != null) {
val bitmap = BitmapFactory.decodeByteArray(imgBuf, 0, imgBuf.size)
onUiThread {
img_wx.setImageBitmap(bitmap)
}
}
}
/**
* 用戶掃描二維碼之后翻翩,回調(diào)改接口
*/
override fun onQrcodeScanned() {
Timber.tag("OAuthListener").i("onQrcodeScanned")
}
})
Timber.tag("OAuthListener").i("authRet->$authRet")
}