1.首先在开发前阅读微信提供的API文档(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842)
阅读完的我们就知道在获取用户授权信息的时候需要设置回调域名:
(1)、在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;
(2)、授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com无法进行OAuth2.0鉴权
(3)、如果公众号登录授权给了第三方开发者来进行管理,则不必做任何设置,由第三方代替公众号实现网页授权即可
设置域名:
接下来在功能设置内找到回调域名设置
在设置里面有详细的设置规则(在设置回调域名时,域名的服务协议不用带比如百度来说他的域名时http://baidu.com设置的时候只需设置baidu.com就行,回调域名是我们请求微信接口微信会将相应信息回馈给我们设置的这个域名下的方法)
首先我们需要知道获取网页授权的三种方式:
(1)、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
(2)、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
(3)、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
在用户访问我的公众号时我设置了引导页也就是欢迎页面(广告页),当访问这个页面时我去请求微信获取code
private static Logger log = LoggerFactory.getLogger(SilentAuthorizationController.class);
//请求微信获取code 的url
private static final String OAUTH="https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
@RequestMapping("/index")
public void getSilentAuthorization(HttpServletRequest req,HttpServletResponse res) throws IOException, ServletException{
log.info("进入授权页面");
String state=req.getParameter("uid");
String url=OAuthService.getOauthUrl(WechatGlobalConfig.REDIRECT_URI,"UTF-8","snsapi_base"); res.sendRedirect(url);
}
其中“WechatGlobalConfig.REDIRECT_URI”为我回调url,微信会将它的返回信息发送到我这个方法,
@RequestMapping("/wechatBack")
public ModelAndView regainOpenIdBack(HttpServletRequest req,HttpServletResponse res){
String str="";
try {
AccessTokenOAuth accessTokenOAuth=new AccessTokenOAuth();
//第二步:通过code换取网页授权access_token
accessTokenOAuth=OAuthService.getOAuthAccessToken(req.getParameter("code"))
if(accessTokenOAuth!=null){
str=accessTokenOAuth.getOpenid();
log.info("线下支付获取openId成功:"+str);
}else{
log.info("线下支付获取accessTokenOAuth失败");
}
} catch (Exception e) {
// TODO: handle exception
CreateExceptionMessage.getMessage(e);
log.error("重新获取用户openId进入异常:"+e);
}
ModelAndView m=new ModelAndView("redirect:/wechat/index.html");//跳到公众号首页
m.addObject("openId", str);
return m;
}
通过以下三个参数和grant_type(authorization_code)这四个参数换取openId
appid 公众号的唯一标识
secret 公众号的appsecret
code 填写第一步获取的code参数
package com.deshangshidai.wechat.subscribe.servlet;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.log4j.Logger;
import com.deshangshidai.exceptions.CreateExceptionMessage;
import com.deshangshidai.utils.MapUtils;
import com.deshangshidai.wechat.subscribe.token.AccessTokenOAuth;
import com.deshangshidai.wechat.subscribe.util.HttpUtils;
import com.deshangshidai.wechat.subscribe.util.WechatGlobalConfig;
import net.sf.json.JSONObject;
public class OAuthService {
public static Logger log = Logger.getLogger(OAuthService.class);
/**
* 网页授权第一步wechat oauth url
*/
public static String OAUTH = WechatGlobalConfig.OAUTH;
/**
* 获取oauth网页认证的token
*/
public static String GET_ACCESS_TOKEN_OAUTH = WechatGlobalConfig.GET_ACCESS_TOKEN_OAUTH;
/**
* 时间格式化工具
*/
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 获得Code的URL
* @param redirectUrl 跳转的url
* @param charset 字符集格式
* @param scope OAUTH scope
* @return oauth url
*/
public static String getOauthUrl(String redirectUrl, String charset, String scope) {
String url = "";
try {
url = OAUTH.replace("APPID", WechatGlobalConfig.APP_ID).replace("REDIRECT_URI", URLEncoder.encode(redirectUrl, charset)).replace("SCOPE", scope);
log.info("获取验证token"+url);
} catch (UnsupportedEncodingException e) {
log.error("获取验证tokenurl失败:"+e);
CreateExceptionMessage.getMessage(e);
return "500.html";
}
return url;
}
/**
*通过code 获取Access_Token(oAuth认证,此access_token与基础支持的access_token不同)
*
* @param code 用户授权后得到的code
* @return AccessTokenOAuth对象
*/
public static AccessTokenOAuth getOAuthAccessToken(String code) {
log.info("进入code方法获取用户信息");
AccessTokenOAuth accessTokenOAuth = null;
long currentTime = System.currentTimeMillis();
StringBuilder info = new StringBuilder("当前时间:").append(format.format(new Date(currentTime)));
String url = GET_ACCESS_TOKEN_OAUTH.replace("APPID", WechatGlobalConfig.APP_ID).replace("SECRET", WechatGlobalConfig.APP_SECREAT).replace("CODE", code);
JSONObject jsonObject = HttpUtils.httpRequest(url, "POST", null);
if (null != jsonObject) {
if(!MapUtils.jsonIsAnyBlank(jsonObject, "errcode")){
info.append("获取access_token失败...\nerrcode:" + jsonObject.getInt("errcode") + ",errmsg:" + jsonObject.getString("errmsg"));
}else {
log.info("将获取到的信息存入accessTokenOAuth对象");
log.info("openid的值"+jsonObject.getString("openid"));
accessTokenOAuth = new AccessTokenOAuth();
accessTokenOAuth.setAccessToken(jsonObject.getString("access_token");
accessTokenOAuth.setExpiresIn(jsonObject.getInt("expires_in"));
accessTokenOAuth.setRefreshToken(jsonObject.getString("refresh_token"));
accessTokenOAuth.setOpenid(jsonObject.getString("openid"));
accessTokenOAuth.setScope(jsonObject.getString("scope"));
}
}else{
log.info("jsonObject为空");
}
log.info(info);
info = null;
return accessTokenOAuth;
}
}
accessTokenOAuth实体类
package com.deshangshidai.wechat.subscribe.token;
/**
* 网页授权接口调用凭证 OAuth2.0
* @author txw
* @version 1.0
*/
public class AccessTokenOAuth {
/**
* 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
private String accessToken;
/**
* access_token接口调用凭证超时时间,单位(秒)
*/
private int expiresIn;
/**
* 用户刷新access_token
*/
private String refreshToken;
/**
* 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
*/
private String openid;
/**
* 用户授权的作用域,使用逗号(,)分隔
*/
private String scope;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public AccessTokenOAuth(String accessToken, int expiresIn, String refreshToken, String openid, String scope) {
super();
this.accessToken = accessToken;
this.expiresIn = expiresIn;
this.refreshToken = refreshToken;
this.openid = openid;
this.scope = scope;
}
public AccessTokenOAuth() {
super();
}
@Override
public String toString() {
return "AccessTokenOAuth [accessToken=" + accessToken + ", expiresIn=" + expiresIn + ", refreshToken=" + refreshToken + ", openid=" + openid
+ ", scope=" + scope +"]";
}
}
第三步:刷新access_token(如果需要)
由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。
请求方法
获取第二步的refresh_token后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
参数 是否必须 说明
appid 是 公众号的唯一标识
grant_type 是 填写为refresh_token
refresh_token 是 填写通过access_token获取到的refresh_token参数
返回说明
正确时返回的JSON数据包如下:
{ "access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE" }
参数 描述
access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
expires_in access_token接口调用凭证超时时间,单位(秒)
refresh_token 用户刷新access_token
openid 用户唯一标识
scope 用户授权的作用域,使用逗号(,)分隔
错误时微信会返回JSON数据包如下(示例为code无效错误):
{"errcode":40029,"errmsg":"invalid code"}
第四步:拉取用户信息(需scope为 snsapi_userinfo)
如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。
请求方法
http:GET(请使用https协议) https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
参数说明
参数 描述
access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
openid 用户的唯一标识
lang 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
返回说明
正确时返回的JSON数据包如下:
{ "openid":" OPENID",
" nickname": NICKNAME,
"sex":"1",
"province":"PROVINCE"
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
"privilege":[ "PRIVILEGE1" "PRIVILEGE2" ],
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
参数 描述
openid 用户的唯一标识
nickname 用户昵称
sex 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
province 用户个人资料填写的省份
city 普通用户个人资料填写的城市
country 国家,如中国为CN
headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
privilege 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
unionid 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
错误时微信会返回JSON数据包如下(示例为openid无效):
{"errcode":40003,"errmsg":" invalid openid "}
附:检验授权凭证(access_token)是否有效
请求方法
http:GET(请使用https协议) https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID
参数说明
参数 描述
access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
openid 用户的唯一标识
返回说明
正确的JSON返回结果:
{ "errcode":0,"errmsg":"ok"}
错误时的JSON返回示例:
{ "errcode":40003,"errmsg":"invalid openid"}