原创地址:https: //blog.csdn.net/luoyan973387349/article/details/49815225 |
准备工作 |
1 .在微信开放平台https: //open.weixin.qq.com/注册成为开发者。 |
2 .在“管理中心”中创建一个移动应用,需“应用名称、简介、及 28 * 28 和 108 * 108 的PNG图片各一张,且大小不超过300k”,点击下一步,需“应用官网地址,应用签名及包名”等信息,然后即可提交审核。 |
说明: |
应用签名:可在微信开发平台的资源中心》》资源下载》》中下载“签名生成工具”,用户获取已经安装到手机的第三方应用的签名。输入应用包名,即可获得该应用的签名值。 |
3 .提交审核后,在 7 个工作日内腾讯将给出审核结果。(通常较快,几个小时就可反馈结果) |
微信登录接入 |
微信登录遵循协议Aouth2. 0 中的授权码模式 |
我们来看一下Aouth2. 0 中的授权码模式是怎么定义的: |
授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。 |
它的步骤如下: |
(A)用户访问客户端,后者将前者导向认证服务器。 |
(B)用户选择是否给予客户端授权。 |
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码。 |
(D)客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。 |
(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token) |
微信登录的官方文档将微信登录分为 3 个步骤: |
第一步.请求code |
{ |
// send oauth request |
Final SendAuth.Req req = new SendAuth.Req(); |
req.scope = "snsapi_userinfo" ; |
req.state = "wechat_sdk_demo_test" ; |
api.sendReq(req); |
} |
用这段代码向微信开放平台请求授权码code,可拉起微信并打开授权登录页(前提是你安装了微信应用并已登录,未登录的会引导你先登录) |
1 .如果微信授权页不显示,请检查你的APP签名是否和你在腾讯开放平台的APP签名一致,不一致可修改腾讯开放平台中的APP签名,修改后重装微信或清除微信数据后重试。 |
2 .在你的包名相应目录下新建一个wxapi目录,并在该wxapi目录下新增一个WXEntryActivity类,该类继承自Activity(例如应用程序的包名为net.sourceforge,则新的包名为:net.sourceforge.wxapi),此处应注意包名不要弄错,新增类的名字必须为WXEntryActivity。 |
返回说明 |
用户点击授权后,微信客户端会被拉起,跳转至授权界面,用户在该界面点击允许或取消,SDK通过SendAuth的Resp返回数据给调用方。回调WXEntryActivity中的onResp(BaseResp resp)方法,如下: |
@Override |
public void onResp(BaseResp resp) { |
int errorCode = resp.errCode; |
switch (errorCode) { |
case BaseResp.ErrCode.ERR_OK: |
//用户同意 |
String code = ((SendAuth.Resp) resp).code; |
break ; |
case BaseResp.ErrCode.ERR_AUTH_DENIED: |
//用户拒绝 |
break ; |
case BaseResp.ErrCode.ERR_USER_CANCEL: |
//用户取消 |
break ; |
default : |
break ; |
} |
ToastUtil.showMessageLong( this , resp.errStr); |
} |
客户端收到授权码后,向自己的服务器发起登录请求,并附带收到的授权码。 |
服务端收到登录请求,向微信开放平台请求获取access_token,微信开放平台返回Json字符串: |
第二步:通过code获取access_token(在自己服务器端做) |
获取第一步的code后,请求以下链接获取access_token: |
https: //api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code |
private String getAccessToken(String code) { |
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code" ; |
URI uri = URI.create(url); |
HttpClient client = new DefaultHttpClient(); |
HttpGet get = new HttpGet(uri); |
HttpResponse response; |
try { |
response = client.execute(get); |
if (response.getStatusLine().getStatusCode() == 200 ) { |
HttpEntity entity = response.getEntity(); |
BufferedReader reader = new BufferedReader( new InputStreamReader(entity.getContent(), "UTF-8" )); |
StringBuilder sb = new StringBuilder(); |
for (String temp = reader.readLine(); temp != null ; temp = reader.readLine()) { |
sb.append(temp); |
} |
JSONObject object = new JSONObject(sb.toString().trim()); |
accessToken = object.getString( "access_token" ); |
openID = object.getString( "openid" ); |
refreshToken = object.getString( "refresh_token" ); |
expires_in = object.getLong( "expires_in" ); |
return accessToken; |
} |
} catch (ClientProtocolException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (IOException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (IllegalStateException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (JSONException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} |
return null ; |
} |
参数说明 |
参数 是否必须 说明 |
appid 是 应用唯一标识,在微信开放平台提交应用审核通过后获得 |
secret 是 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得 |
code 是 填写第一步获取的code参数 |
grant_type 是 填authorization_code回说明** |
正确的返回: |
{ |
"access_token" : "ACCESS_TOKEN" , |
"expires_in" : 7200 , |
"refresh_token" : "REFRESH_TOKEN" , |
"openid" : "OPENID" , |
"scope" : "SCOPE" , |
"unionid" : "o6_bmasdasdsad6_2sgVt7hMZOPfL" |
} |
参数 说明 |
access_token 接口调用凭证 |
expires_in access_token 接口调用凭证超时时间,单位(秒) |
refresh_token 用户刷新access_token |
openid 授权用户唯一标识 |
scope 用户授权的作用域,使用逗号(,)分隔 |
unionid 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。 |
错误返回样例: |
{ "errcode" : 40029 , "errmsg" : "invalid code" } |
服务端收到返回的access_token,将access_token,expires_in,access_token是否有效 等数据返回给客户端,客户端成功登录 |
客户端可利用已有的access_token获取微信用户信息 |
第三步:通过access_token调用接口 |
获取access_token后,进行接口调用,有以下前提: |
access_token有效且未超时; |
微信用户已授权给第三方应用帐号相应接口作用域(scope)。 |
对于接口作用域(scope),能调用的接口有以下: |
授权作用域(scope) 接口 接口说明 |
snsapi_base /sns/oauth2/access_token 通过code换取 access_token、refresh_token和已授权scope |
/sns/oauth2/refresh_token 刷新或续期access_token使用 |
/sns/auth 检查access_token有效性 |
snsapi_userinfo /sns/userinfo 获取用户个人信息 |
其中snsapi_base属于基础接口,若应用已拥有其它scope权限,则默认拥有snsapi_base的权限。使用snsapi_base可以让移动端网页授权绕过跳转授权登录页请求用户授权的动作,直接跳转第三方网页带上授权临时票据(code),但会使得用户已授权作用域(scope)仅为snsapi_base,从而导致无法获取到需要用户授权才允许获得的数据和基础功能。 |
以获取用户信息为例: |
private void getUserInfo() { |
if (isAccessTokenIsInvalid() && System.currentTimeMillis() < expires_in) { |
String uri = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openID; |
HttpClient client = new DefaultHttpClient(); |
HttpGet get = new HttpGet(URI.create(uri)); |
try { |
HttpResponse response = client.execute(get); |
if (response.getStatusLine().getStatusCode() == 200 ) { |
BufferedReader reader = new BufferedReader( new InputStreamReader(response.getEntity().getContent(), "UTF-8" )); |
StringBuilder builder = new StringBuilder(); |
for (String temp = reader.readLine(); temp != null ; temp = reader.readLine()) { |
builder.append(temp); |
} |
JSONObject object = new JSONObject(builder.toString().trim()); |
String nikeName = object.getString( "nickname" ); |
} |
} catch (ClientProtocolException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (IOException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (JSONException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} |
} |
} |
微信重复登录 |
假设用户已经获得授权,则下次登录时只需要验证access_token是否有效,无效则重新获取授权,有效则无需重新获得授权。 |
1 .用户向自己的服务器请求登录,登录方式为微信登录,附带上次登录返回的的access_token |
2 .服务器收到用户的登录请求,向微信开放平台发送access_token是否有效的验证请求如下: |
private boolean isAccessTokenIsInvalid() { |
String url = "https://api.weixin.qq.com/sns/auth?access_token=" + accessToken + "&openid=" + openID; |
URI uri = URI.create(url); |
HttpClient client = new DefaultHttpClient(); |
HttpGet get = new HttpGet(uri); |
HttpResponse response; |
try { |
response = client.execute(get); |
if (response.getStatusLine().getStatusCode() == 200 ) { |
HttpEntity entity = response.getEntity(); |
BufferedReader reader = new BufferedReader( new InputStreamReader(entity.getContent(), "UTF-8" )); |
StringBuilder sb = new StringBuilder(); |
for (String temp = reader.readLine(); temp != null ; temp = reader.readLine()) { |
sb.append(temp); |
} |
JSONObject object = new JSONObject(sb.toString().trim()); |
int errorCode = object.getInt( "errcode" ); |
if (errorCode == 0 ) { |
return true ; |
} |
} |
} catch (ClientProtocolException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (IOException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (JSONException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} |
return false ; |
} |
返回说明 |
正确的Json返回结果: |
{ |
"errcode" : 0 , "errmsg" : "ok" |
} |
错误的Json返回示例: |
{ |
"errcode" : 40003 , "errmsg" : "invalid openid" |
} |
如果access_token有效,服务端将信息返回给客户端,客户端成功登录。 |
如果access_token无效,服务端向微信开放平台发送刷新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失效的后,需要用户重新授权。 |
private void refreshAccessToken() { |
String uri = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" + ShareUtil.APP_ID + "&grant_type=refresh_token&refresh_token=" |
+ refreshToken; |
HttpClient client = new DefaultHttpClient(); |
HttpGet get = new HttpGet(URI.create(uri)); |
try { |
HttpResponse response = client.execute(get); |
if (response.getStatusLine().getStatusCode() == 200 ) { |
BufferedReader reader = new BufferedReader( new InputStreamReader(response.getEntity().getContent(), "UTF-8" )); |
StringBuilder builder = new StringBuilder(); |
for (String temp = reader.readLine(); temp != null ; temp = reader.readLine()) { |
builder.append(temp); |
} |
JSONObject object = new JSONObject(builder.toString().trim()); |
accessToken = object.getString( "access_token" ); |
refreshToken = object.getString( "refresh_token" ); |
openID = object.getString( "openid" ); |
expires_in = object.getLong( "expires_in" ); |
} |
} catch (ClientProtocolException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (IOException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} catch (JSONException e) { |
// TODO Auto-generated catch block |
e.printStackTrace(); |
} |
} |
返回说明 |
正确的返回: |
{ |
"access_token" : "ACCESS_TOKEN" , |
"expires_in" : 7200 , |
"refresh_token" : "REFRESH_TOKEN" , |
"openid" : "OPENID" , |
"scope" : "SCOPE" |
} |
参数 说明 |
access_token 接口调用凭证 |
expires_in access_token接口调用凭证超时时间,单位(秒) |
refresh_token 用户刷新access_token |
openid 授权用户唯一标识 |
scope 用户授权的作用域,使用逗号(,)分隔 |
错误返回样例: |
{ |
"errcode" : 40030 , "errmsg" : "invalid refresh_token" |
} |
3 .服务端获取到新的access_token等信息,并返回给客户端,客户端成功登录或者重新获取授权。 |