有兴趣的可以看下我另一篇文章:QQ登录
首先到微信官网注册:https://open.weixin.qq.com/cgi-bin/frame?t=home/app_tmpl&lang=zh_CN
这里需要注意一点的是,app的签名。我们在android studio获取签名的格式如下:
MD5: 97:CF:28:B9:D6:8E:E1:2D:CB:78:4F:7B:C6:C6:E4:F8
但官网需要的应用签名必须小写且无符号,所以应该改为:
97cf28b9d68ee12dcb784f7bc6c6e4f8。
微信获取签名的工具:点击下载
1.首先在项目根目录下建一个
wxapi包以及WXEntryActivity类,包名和类名必须同上。
在manifest配置如下:
<!--微信登录配置-->
<activity
android:name=".wxapi.WXEntryActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true" />
</application>
2.在主界面添加如下代码:
/*微信登录*/
private void wxLogin() {
//通过WXAPIFactory工厂获取IWXApI的示例
IWXAPI api = WXAPIFactory.createWXAPI(this, Constant.APP_ID_WX, true);
//将应用的appid注册到微信
api.registerApp(Constant.APP_ID_WX);
SendAuth.Req req = new SendAuth.Req();
req.scope = "snsapi_userinfo";
// req.scope = "snsapi_login";//提示scope参数错误或者没有scope权;限用snsapi_base提示没权限
req.state = "wechat_sdk_微信登录";
//用于保持请求和回调的状态,授权请求后原样带回给第三方。
// 该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验。
api.sendReq(req);
}
3.WXEntryActivity详解:
public class WXEntryActivity extends AppCompatActivity implements IWXAPIEventHandler {
/**
* 微信登录相关
*/
private IWXAPI api;
private String tag = "WXEntryActivity";
public static String access_token = "access_token";
public static String openid = "openid";
public static final String ACTION_GETWX = "com.bs.utown.get_wx_msg";//用于广播的action
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通过WXAPIFactory工厂获取IWXApI的示例
api = WXAPIFactory.createWXAPI(this, Constant.APP_ID_WX, true);
//将应用的appid注册到微信
api.registerApp(Constant.APP_ID_WX);
Logs.d("------------------------------------");
//注意:
//第三方开发者如果使用透明界面来实现WXEntryActivity,需要判断handleIntent的返回值,如果返回值为false,
// 则说明入参不合法未被SDK处理,应finish当前透明界面,避免外部通过传递非法参数的Intent导致停留在透明界面,引起用户的疑惑
try {
boolean result = api.handleIntent(getIntent(), this);
if (!result) {
Logs.i("参数不合法,未被SDK处理,退出");
finish();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
api.handleIntent(data, this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
finish();
}
@Override
public void onReq(BaseReq baseReq) {
Logs.d(tag + "74 baseReq:" + JSON.toJSONString(baseReq));
}
@Override
public void onResp(BaseResp baseResp) {
Logs.w(tag + "78 " + JSON.toJSONString(baseResp));
Logs.i(tag + "79 " + baseResp.errStr + "," + baseResp.openId + "," + baseResp.transaction + "," + baseResp.errCode);
String code = ((SendAuth.Resp) baseResp).code;
String result = "";
switch (baseResp.errCode) {
case BaseResp.ErrCode.ERR_OK:
result = "发送成功";
/*现在请求获取数据 access_token https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code*/
OkHttpUtils.get().url("https://api.weixin.qq.com/sns/oauth2/access_token")
.addParams("appid", Constant.APP_ID_WX)
.addParams("secret", Constant.APP_SECRET_WX)
.addParams("code", code)
.addParams("grant_type", "authorization_code")
.build()
.execute(new StringCallback() {
@Override
public void onError(okhttp3.Call call, Exception e, int id) {
Logs.e(tag + "113 请求错误.." + e+" "+id+" \n "+call);
}
@Override
public void onResponse(String response, int id) {
Logs.v(tag + "118 response:" + response);
String tokenstr = null, openidstr = null;
try {
JSONObject jo = new JSONObject(response);
tokenstr = jo.getString(access_token);
openidstr = jo.getString(openid);
Logs.i(tag + " 116 " + tokenstr + " " + openidstr);
} catch (JSONException e) {
e.printStackTrace();
}
/*通过广播将access_token和openid返回登录界面*/
LocalBroadcastManager.getInstance(WXEntryActivity.this).sendBroadcast(new Intent(ACTION_GETWX).
putExtra(access_token, tokenstr).putExtra(openid, openidstr));
}
});
break;
case BaseResp.ErrCode.ERR_USER_CANCEL:
result = "发送取消";
break;
case BaseResp.ErrCode.ERR_AUTH_DENIED:
result = "发送被拒绝";
break;
case BaseResp.ErrCode.ERR_BAN:
result = "签名错误";
break;
default:
result = "发送返回";
break;
}
Toast.makeText(WXEntryActivity.this, result, Toast.LENGTH_LONG).show();
finish();
}
}
3.1首先来看,如果你的签名无效返回的数据如下:
WXEntryActivity78 {"errCode":-6,"openId":"","type":1}
我们首先来看这个
BaseResp.ErrCode
int ERR_BAN = -6;签名错误
其他几种错误格式
static int ERR_AUTH_DENIED
认证被否决
static int ERR_COMM
一般错误
static int ERR_OK
正确返回
static int ERR_SENT_FAILED
发送失败
static int ERR_UNSUPPORT
不支持错误
static int ERR_USER_CANCEL
用户取消
3.2SendAuth.Resp详解
我们可以看出继承自baseResp
{
"code":"0712DRee0kzwiz13fUce0900feRet",
"country":"CN",
"errCode":0,
"lang":"zh_CN",
"state":"wechat_sdk_微信登录",
"type":1,
"url":"wxb363a9ff53731258://oauth?code=0712DRee0kzwiz13fUce0900fe02DRet&ate=wechat_sdk_%E5%BE%AE%E4%BF%A1%E7%99%BB%E5%BD%95"
}
解释:
errCode:
//ERR_OK = 0(用户同意) ERR_AUTH_DENIED = -4(用户拒绝授权 ERR_USER_CANCEL = -2(用户取消)
code:用户换取access_token的code,仅在ErrCode为0时有效
state:第三方程序发送时用来标识其请求的唯一性的标志,由第三方程序调用sendReq时传入,由微信终端回传,state字符串长度不能超过1K
lang:微信客户端当前语言
country:微信用户当前国家信息
3.3获取access_token和openid
获取的地址:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数说明:
appid:应用唯一标识,在微信开放平台提交应用审核通过后获得
secret:应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
code:填写第一步获取的code参数
grant_type:固定值,写authorization_code
返回结果说明:
{
"access_token":"hSTsG7nq8e0yEFhOZFT-wAdTsjT9jC0AYvGRiqGwwR6Hko99o_mmYR8KO18kMxeDOz33d9tnBhMzu_NLsIha2HqvTm1OGPL1weBdvXZVFFc",
"expires_in":7200,
"refresh_token":"AeN69M27vttqCedxoIOSeY6cxvbt1N584HjEOclUXtNWxRaZWgmtfvn2jWIDX4tq5t-7Btlc1UkEyyFhV7HVIMXe-V6RPjoZdF525vLzev8",
"openid":"olmt4wfxS21G4VeeVX16_zUhZezY",
"scope":"snsapi_userinfo",
"unionid":"o5aWQwAa7niCIXhAIRBOwglIJ7UQ"
}
access_token:接口调用凭证
expires_in :access_token接口调用凭证超时时间,单位(秒)
refresh_token 用户刷新access_token
openid 授权用户唯一标识
scope 用户授权的作用域,使用逗号(,)分隔
3.4刷新或续期access_token使用
接口说明
access_token是调用授权关系接口的调用凭证,由于access_token有效期(目前为2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新,access_token刷新结果有两种:
1.若access_token已超时,那么进行refresh_token会获取一个新的access_token,新的超时时间;
2.若access_token未超时,那么进行refresh_token不会改变access_token,但超时时间会刷新,相当于续期access_token。
refresh_token拥有较长的有效期(30天),当refresh_token失效的后,需要用户重新授权,所以,请开发者在refresh_token即将过期时(如第29天时),进行定时的自动刷新并保存好它。
请求方法
使用/sns/oauth2/access_token接口获取到的refresh_token进行以下接口调用:
http请求方式: GET
参数说明
参数 是否必须 说明
appid 是 应用唯一标识
grant_type 是 填refresh_token
refresh_token 是 填写通过access_token获取到的refresh_token参数
4.接收数据
/*通过广播将access_token和openid返回登录界面*/ LocalBroadcastManager.getInstance(WXEntryActivity.this).sendBroadcast(new Intent(ACTION_GETWX). putExtra(access_token, tokenstr).putExtra(openid, openidstr));
我们可以看到我在WXEntryActivity中添加了用接收数据的广播,最主要是通过openid和access_token获取个人信息。
4.1首先在登录的界面添加接收的广播:
/*创建用于接收数据的广播*/
private BroadcastReceiver wxReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WXEntryActivity.ACTION_GETWX)) {
wx_access_token = intent.getStringExtra(WXEntryActivity.access_token);
wx_openid = intent.getStringExtra(WXEntryActivity.openid);
getWxUserInfo();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
/*注册微信广播*/
IntentFilter filter = new IntentFilter(WXEntryActivity.ACTION_GETWX);
LocalBroadcastManager.getInstance(this).registerReceiver(wxReceiver, filter);
4.2请求个人信息的方式如下:
/*获取WX的用户信息*/
private void getWxUserInfo() {
//https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
OkHttpUtils.get()
.url("https://api.weixin.qq.com/sns/userinfo")
.addParams("access_token", wx_access_token)
.addParams("openid", wx_openid)//openid:授权用户唯一标识
.build()
.execute(new StringCallback() {
@Override
public void onError(okhttp3.Call call, Exception e, int id) {
Logs.e(tag + "141获取错误 " + e + " " + id + " " + call);
}
@Override
public void onResponse(String response, int id) {
Logs.i(tag + "144 " + response + " " + id);
DialogNotileUtil.show(LoginActivity.this, response);
}
});
}
4.3返回的数据:
个人信息 {"openid":"###","nickname":"龙成斌","sex":1,"language":"zh_CN","city":"Nanchang","province":"Jiangxi","country":"CN",
"headimgurl":"http:\/\/thirdwx.qlogo.cn\/mmopen\/vi_32\/Q0j4TwGTfTKeKacq5Pqes8FqryJ4EnobB8nPXR2ibQ2ChWJIB3EIRW4cJ0HGAumMp9RNrlBK0PF8TdvzzOny6jA\/132",
"privilege":[],"unionid":"****"}