美文网首页
微信移动应用扫描登陆记录

微信移动应用扫描登陆记录

作者: 游戏中的龙 | 来源:发表于2018-06-28 09:01 被阅读0次

    功能概述

    扫码登录能力,指的是开发者可在移动应用内使用此能力,拉取二维码,用户使用微信客户端扫描二维码后可以登录此移动应用。此能力可被应用在多设备登录、智能硬件、电视盒子等场景。

    第一步:

    很简单,就是先创建应用,集成微信SDK。

    第二步:

    开始写代码,首先APP通过IDiffDevOAuth.auth()接口发起授权,然后在OAuthListener.onAuthGotQrcode()回调接口中获取二维码,在APP中展示二维码,最后用户通过微信扫码,授权。

        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)

    参数说明

    参数 是否必须 说明
    appId 应用唯一标识
    scope 应用授权作用域,拥有多个作用域用逗号(,)分隔,APP所拥有的scope
    noncestr 一个随机的尽量不重复的字符串,用来使得每次的signature不同
    timestamp 时间戳
    signature 签名
    listener 授权流程,回调接口

    appId:自己上应用上查看。
    scope:snsapi_userinfo,官方demo中的snsapi_login和snsapi_base没用,使用后会报一般错误。
    noncestr:使用MD5加密一串随机值

        private fun getNonceStr(): String {
            val r = Random(System.currentTimeMillis())
            return EncryptUtils.encryptMD5ToString((Constant.WXAPPID + 
            r.nextInt(10000) + System.currentTimeMillis()).toByteArray())
        }
    

    timestamp:获取当前时间戳就行

        private fun getTimestamp(): String {
            return System.currentTimeMillis().toString()
        }
    

    signature:官方说明:生成签名之前必须先获取对应的sdk_ticket。
    sdk_ticket是用于生成签名的临时票据。正常情况下,sdk_ticket的有效期为7200秒,通过access_token来获取。由于获取sdk_ticket的api调用次数非常有限,频繁刷新sdk_ticket会导致api调用受限,影响自身业务,开发者需在自己的服务存储与更新sdk_ticket。

    1. 先获取token,grant_type参数固定填client_credential,另两个参数在微信开放平台应用上获取。
        /**
         * 微信获取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"}

    image.png
    官方地址
    1. 获取到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
    }
    
    1. 成功拿到ticket后,按签名规则生成签名。
      签名生成规则如下:
      参与签名的字段包括第三方appid,noncestr(随机字符串), 有效的sdk_ticket, timestamp(时间戳) 。
      对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。即signature=sha1(string1)。
      示例:
      appid=appid
      noncestr=noncestr
      sdk_ticket=-p3A5zVP95IuafPhzA6lRR95_F9nZEBfJ_n4E9t8ZFWKJTDPOwccVQhHCwDBmvLkayF_jh-m9HOExhumOziDWA
      timestamp=1417508194
      1.对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
      appid=appid&noncestr=noncestr&sdk_ticket=-p3A5zVP95IuafPhzA6lRR95_F9nZEBfJ_n4E9t8ZFWKJTDPOwccVQhHCwDBmvLkayF_jh-m9HOExhumOziDWA&timestamp=1417508194
      2.对string1进行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&timestamp=$timestamp"
            return EncryptUtils.encryptSHA1ToString(str)
        }
    

    注意这里是用SHA1加密的

    1. 最后调用接口
     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 {
                /**
                 * 用户点击授权后,回调改接口
                 */
                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 -> "登录失败,网络错误"
                        OAuthErrCode.WechatAuth_Err_JsonDecodeErr -> "json解码失败"
                        OAuthErrCode.WechatAuth_Err_Cancel -> "用户取消"
                        OAuthErrCode.WechatAuth_Err_Timeout -> "登录失败,超时错误"
                        else -> ""
                    }
                    Timber.tag("OAuthListener").i(tips)
                }
    
                /**
                 * auth之后返回的二维码接口
                 *
                 * @param qrcodeImgPath 废弃
                 * @param imgBuf 二维码图片数据
                 */
                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)
                        }
                    }
                }
    
                /**
                 * 用户扫描二维码之后,回调改接口
                 */
                override fun onQrcodeScanned() {
                    Timber.tag("OAuthListener").i("onQrcodeScanned")
                }
    
            })
            Timber.tag("OAuthListener").i("authRet->$authRet")
        }
    

    相关文章

      网友评论

          本文标题:微信移动应用扫描登陆记录

          本文链接:https://www.haomeiwen.com/subject/mnmiyftx.html