微信开放平台开发第三方授权登陆(三):Android客户端

微信开放平台开发系列文章:

微信开放平台开发第三方授权登陆(一):开发前期准备

微信开放平台开发第三方授权登陆(二):PC网页端

微信开放平台开发第三方授权登陆(三):Android客户端

微信开放平台开发第三方授权登陆(四):微信公众号

微信开放平台开发第三方授权登陆(五):微信小程序

 

目录​​​​​​​

一、需求

二、开发流程

三、开发使用的技术及工具

四、具体实现步骤

1.前端(Android)

1)Android微信授权登录开发环境配置

2)引导用户点击登录并授权

3)接收微信服务端返回的数据并向服务端发送请求

4)根据服务端返回数据进行解析并显示给前端Android页面

2.服务端(Java)

1).统一返回JSON

2).相关参数配置:

3).请求响应逻辑

4).根据Token获取用户信息:

五、注意事项:

六、应用关键参数位置


微信开放平台开发第三方授权登陆(一):开发前期准备完成后,已经获取到应用的AppID和AppSecret、且已经成功申请到微信登陆功能。可以进行第三方登陆授权开发。

注意:

目前移动应用上微信登录只提供原生的登录方式,需要用户安装微信客户端才能配合使用

对于Android应用,建议总是显示微信登录按钮,当用户手机没有安装微信客户端时,请引导用户下载安装微信客户端

 

开放平台中创建移动应用时,需要添加包名(一定要和开发的包名完全一致,不能是填写的包名的子包,否则微信无法回调成功)

安装验签工具:Gen_Signature_Android2.apk

填写包名,然后会生成应用签名,填写应用签名就可以了。

 

一、需求

拥有第三方微信登录功能,并获取到用户信息。

二、开发流程

Android移动应用:(App唤醒微信客户端授权登陆)

1. 应用发起微信授权登录请求,用户允许授权应用后,微信会拉起应用或重定向到第三方网站(服务端),并且带上授权临时票据code参数;

2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;

3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

获取用户基本信息的流程

 

三、开发使用的技术及工具

1、.后端采用IDEA2017 进行开发

2、使用Android Studio 3.1.3 进行开发

3、后端必须基于JDK7以上版本,采用JDK8开发,前端基于Android SDK4.4

4、使用fastJson对json数据进行处理

四、具体实现步骤

1.前端(Android)

目录结构如下:

1)Android微信授权登录开发环境配置

I.添加微信依赖

Android Studio环境

在build.gradle文件中,添加依赖

dependencies {

    compile 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'

}

dependencies {

    compile 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'

}

Eclipse环境下:

在工程中新建一个libs目录,将开发工具包中libs目录下的libammsdk.jar复制到该目录中(如下图所示,建立了一个名为SDK_Sample 的工程,并把jar包复制到libs目录下)。

https://res.wx.qq.com/op_res/yXeM-qkPNMo3NZ6AOSZ0x8MqkBf9ATOfaw-2Ic93vUG8xFid8niGKr3W_RfCmMxe

右键单击工程,选择Build Path中的Configure Build Path...,选中Libraries这个tab,并通过Add Jars...导入工程libs目录下的libammsdk.jar文件。(如下图所示)。

https://img-blog.csdnimg.cn/20181225013003776

在需要使用微信终端API的文件中导入相应的类。

import com.tencent.mm.opensdk.openapi.WXTextObject;

 

 

II. AndroidManifest.xml 设置

添加如下权限支持:

    <!--权限声明-->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

III.若要混淆代码,为保证sdk正常使用,需在配置proguard.cfg(proguard-rule.pro):

# wechat
-keep class com.tencent.mm.opensdk.** {*;}
-keep class com.tencent.wxop.** {*;}
-keep class com.tencent.mm.sdk.** {*;}

2)引导用户点击登录并授权

I.layout.xml

添加button:

        <Button
            android:id="@+id/wechat_login_btn"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="@string/wechat_login"/>

II.监听button点击事件,拉起微信授权页

       findViewById(R.id.wechat_login_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isWXAppInstalledAndSupported()) {  // 用户是否安装微信客户端
                    // send oauth request
                    final SendAuth.Req req = new SendAuth.Req();
                    req.scope = "snsapi_userinfo";
                    req.state = "none";
                    api.sendReq(req);
                    finish();
                } else {
                    // TODO: 这里需要引导用户去下载微信客户端
                    Toast.makeText(WXEntryActivity.this, "用户没有安装微信", Toast.LENGTH_SHORT).show();
                }
            }
        });

III.用户手机是否安装微信客户端检查  

  private boolean isWXAppInstalledAndSupported() {
        IWXAPI msgApi = WXAPIFactory.createWXAPI(this, null);
        msgApi.registerApp(Constants.APP_ID);
        boolean sIsWXAppInstalledAndSupported = msgApi.isWXAppInstalled()
                && msgApi.isWXAppSupportAPI();
        return sIsWXAppInstalledAndSupported;
    }

3)接收微信服务端返回的数据并向服务端发送请求

用户统一授权后,微信会返回数据,需要在.wxapi.WXEntryActivity下对数据进行处理。

I.新建wxapi包(包名固定,且必须是在微信开放平台注册的包名下)

II.新建Activity类,命名为WXEntryActivity

WXEntryActivity,并继承Activity类,实现IWXAPIEventHandler接口的两个方法

public interface IWXAPIEventHandler {
    void onReq(BaseReq var1);
    void onResp(BaseResp var1);
}

WXEntryActivity实现

public class WXEntryActivity extends Activity implements IWXAPIEventHandler {

private IWXAPI api;  // 在onCreate中进行了初始化

onReq方法

    // 微信发送请求到第三方应用时,会回调到该方法
    @Override
    public void onReq(BaseReq req) {
        Toast.makeText(this, "Test ", Toast.LENGTH_SHORT).show();
        switch (req.getType()) {
            case ConstantsAPI.COMMAND_GETMESSAGE_FROM_WX:
                break;
            case ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
                break;
            default:
                break;
        }
}

onResp方法

在onResp中需要实现逻辑,微信返回的数据在这里会被接收。

微信返回的数据包含code。在onResp需要实现向服务端发送请求,带上code等参数,后端再通过相应的参数去请求微信服务端,最终将获取到的用户信息返回给前端Android。

// 第三方应用发送到微信的请求处理后的响应结果,会回调到该方法
    @Override
    public void onResp(final BaseResp resp) {
        int result = 0;

        Toast.makeText(this, "baseresp.getType = " + resp.getType(), Toast.LENGTH_SHORT).show();
        //成功后发送请求
        switch (resp.errCode) {
            case BaseResp.ErrCode.ERR_OK:
                result = R.string.errcode_success;
                final String code = ((SendAuth.Resp) resp).code;//需要转换一下才可以
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                //向服务端发送请求,预计返回用户信息数据,返回给前端进行显示。
                        String url = "http://p2a3b8.natappfree.cc" +
                                "/wechat/open/callback/android" + "?" +
                                "state=" + "android" +//这里的state需与后端进行探讨
                                "&code=" + code;
                        String str = ApacheHttpUtil.get(url);
                        JSONObject jsonObject = (JSONObject) JSONObject.parse(str);
                        weChatUserInfo = (WeChatUserInfo) JSON.parseObject(jsonObject.get("data").toString(), new TypeReference<WeChatUserInfo>() {
                        });
                    }
                }).start();
                while (true) {
// TODO: 这里处理方案不合理,死循环或将造成界面卡死(需要前端优化)
                    if (weChatUserInfo != null) {
                        Intent intent = new Intent(WXEntryActivity.this, WechatUserInfoViewItem.class);
                        /* 通过Bundle对象存储需要传递的数据 */
                        Bundle bundle = new Bundle();
                        bundle.putString("wechatopenid", weChatUserInfo.getOpenid());
                        bundle.putString("wechatnickname", weChatUserInfo.getNickname());
                        bundle.putString("wechatsex", weChatUserInfo.getSex().toString());
                        bundle.putString("wechatprovince", weChatUserInfo.getProvince());
                        bundle.putString("wechatcity", weChatUserInfo.getCity());
                        bundle.putString("wechatcountry", weChatUserInfo.getCountry());
                        bundle.putString("wechatheadimgurl", weChatUserInfo.getHeadimgurl());
                        bundle.putString("wechatprivilege", weChatUserInfo.getPrivilege());
                        bundle.putString("wechatunionid", weChatUserInfo.getOpenid());
                        /*把bundle对象assign给Intent*/
                        intent.putExtras(bundle);
                        startActivity(intent);
                        break;
                    }
                }
                break;
            case BaseResp.ErrCode.ERR_USER_CANCEL:
                result = R.string.errcode_cancel;   // 发送取消
                break;
            case BaseResp.ErrCode.ERR_AUTH_DENIED:
                result = R.string.errcode_deny;   // 发送被拒绝
                break;
            case BaseResp.ErrCode.ERR_UNSUPPORT:
                result = R.string.errcode_unsupported;  // 不支持错误
                break;
            default:
                result = R.string.errcode_unknown;  // 发送返回
                break;
        }
        Toast.makeText(this, result, Toast.LENGTH_LONG).show();
    }

重写onNewIntent方法

在WXEntryActivity中将接收到的intent及实现了IWXAPIEventHandler接口的对象传递给IWXAPI接口的handleIntent方法,

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        api.handleIntent(intent, this);
    }

III.在manifest文件添加WXEntryActivity,并加上exported属性,设置为true,:

        <activity
            android:name=".wxapi.WXEntryActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:launchMode="singleTask">
            <!--android:launchMode="singleTop">-->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sdksample" />
            </intent-filter>
        </activity>

4)根据服务端返回数据进行解析并显示给前端Android页面

获取数据已经跳转代码如上(onResp方法中)。需要前端优化处理

public class WechatUserInfoViewItem extends FragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.wechat_user_info);
        Bundle bundle = this.getIntent().getExtras();
        TextView wechatopenid = (TextView) findViewById(R.id.wechatopenid);
        wechatopenid.setText(bundle.getString("wechatopenid"));
        TextView wechatnickname = (TextView) findViewById(R.id.wechatnickname);
        wechatnickname.setText(bundle.getString("wechatnickname"));
        TextView wechatsex = (TextView) findViewById(R.id.wechatsex);
        wechatsex.setText(bundle.getString("wechatsex"));
        TextView wechatprovince = (TextView) findViewById(R.id.wechatprovince);
        wechatprovince.setText(bundle.getString("wechatprovince"));
        TextView wechatcity = (TextView) findViewById(R.id.wechatcity);
        wechatcity.setText(bundle.getString("wechatcity"));
        TextView wechatcountry = (TextView) findViewById(R.id.wechatcountry);
        wechatcountry.setText(bundle.getString("wechatcountry"));
        TextView wechatunionid = (TextView) findViewById(R.id.wechatunionid);
        wechatunionid.setText(bundle.getString("wechatunionid"));
    }
}

布局xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <ImageView
        android:id="@+id/wechatheadimgurl"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:scaleType="center"/>
   <!--用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)[这里是一个list]-->
    <!--String privilege;-->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="openid:"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
        <TextView
            android:id="@+id/wechatopenid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="unionid:"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
        <TextView
            android:id="@+id/wechatunionid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="用户昵称:"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
        <TextView
            android:id="@+id/wechatnickname"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout_editor_absoluteX="85dp"
            tools:layout_editor_absoluteY="212dp" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="用户城市:"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
        <TextView
            android:id="@+id/wechatcity"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout_editor_absoluteX="118dp"
            tools:layout_editor_absoluteY="302dp" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="用户性别:"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
        <TextView
            android:id="@+id/wechatsex"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout_editor_absoluteX="118dp"
            tools:layout_editor_absoluteY="302dp" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="省份:"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
        <TextView
            android:id="@+id/wechatprovince"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout_editor_absoluteX="118dp"
            tools:layout_editor_absoluteY="302dp" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="国家:"
            tools:layout_editor_absoluteX="158dp"
            tools:layout_editor_absoluteY="216dp" />
        <TextView
            android:id="@+id/wechatcountry"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout_editor_absoluteX="118dp"
            tools:layout_editor_absoluteY="302dp" />
    </LinearLayout>
</LinearLayout>

2.服务端(Java)

服务端需要做的是:接受Android前端发送的请求,获取code,根据AppId和APPSecret等向微信服务端发送请求,然后获取到Token,再根据Token获取到用户基本信息。最终通过JSON的方式返回给前端。

1).统一返回JSON

@Getter @Setter
public class Result<T> {
    private int code;
    private String msg;
    private T data;

    /*** 成功时候的调用* */
    public static <T> Result<T> success(T data){
        return new  Result<T>(data);
    }

    /*** 失败时候的调用* */
    public static <T> Result<T> error(CodeMsg cm){
        return new  Result<T>(cm);
    }

    private Result(T data) {
        this.code = 0;
        this.msg = "success";
        this.data = data;
    }

    private Result(CodeMsg cm) {
        if(cm == null) {
            return;
        }
        this.code = cm.getCode();
        this.msg = cm.getMsg();
    }
}

 

2).相关参数配置:

# 微信开放平台Android

wechat.open.android.appid =

wechat.open.android.appsecret =

 

3).请求响应逻辑

@ResponseBody
    @RequestMapping("/callback/android")
    public Result openWeChatCallback(HttpServletRequest httpServletRequest) {
        String code = httpServletRequest.getParameter("code");
        //String state = httpServletRequest.getParameter("state"); // TODO:
        String url = null;
        url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                "appid=" +
                env.getProperty("wechat.open.android.appid").trim() +
                "&secret=" +
                env.getProperty("wechat.open.android.appsecret").trim() +
                "&code=" +
                code +
                "&grant_type=authorization_code";
        JSONObject wechatAccessToken = HttpClientUtils.httpGet(url);
        if (wechatAccessToken.get("errcode") != null) {
            return Result.error(CodeMsg.FAIL_GETTOKEN);
        }
        String accessToken = (String) wechatAccessToken.get("access_token");
        String openid = (String) wechatAccessToken.get("openid");
        String unionid = (String) wechatAccessToken.get("unionid");

        if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openid) || StringUtils.isEmpty(unionid)) {
            return Result.error(CodeMsg.FAIL_GETTOKEN);
        }
        // TODO:根据Openid或Unionid对数据库进行查询,如果查询到对应的用户数据,则不需要再向微信服务器发送请求去返回数据。
        // TODO: 建议使用Unionid作为查询条件。
        WeChatUserInfo weChatUserInfo = null;
        wechatAccessToken = null;  // FIXME: 这里应该是从数据库中查询获取用户信息逻辑。
        if (wechatAccessToken == null) {
            // 新用户
            weChatUserInfo = getUserInfoByAccessToken(accessToken);
            // 数据库插入的操作
        }
        if (weChatUserInfo != null) {
            return Result.success(weChatUserInfo);
        }
        return Result.error(CodeMsg.FAIL_GETUSERINFO);
    }

4).根据Token获取用户信息:

/**
     * 根据accessToken获取用户个人公开信息
     *
     * @param accessToken
     * @return
     */
  private WeChatUserInfo getUserInfoByAccessToken(String accessToken) {
        if (StringUtils.isEmpty(accessToken)) {
            return null;  //"accessToken为空";
        }
        String get_userInfo_url = "https://api.weixin.qq.com/sns/userinfo?" +
                "access_token=" +
                accessToken +
                "&openid=" +
                env.getProperty("wechat.open.android.appid").trim();
        String userInfo_result = HttpClientUtils.httpGet(get_userInfo_url, "utf-8");
        if (!userInfo_result.equals("errcode")) {
            WeChatUserInfo weChatUserInfo = JSON.parseObject(userInfo_result, new TypeReference<WeChatUserInfo>() {
            });
            // TODO: 需要把头像信息下载到文件服务器,然后替换掉头像URL。微信的或许不可靠,假设微信用户更换了头像,旧头像URL是否会保存?而这个URL信息却存放在我们的数据库中,不可靠
            return weChatUserInfo;
        }
        return null;  //"获取用户信息失败"
    }

五、注意事项:

1.Android4.0以上版本,发送网络请求时,必须是以线程异步的方式发送请求,否则发送请求会失败。

六、应用关键参数位置

 

  • 10
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值