微信公众号开发—入门系列(四)

在实际开发过程中,我们经常需要将微信用户的基本信息做持久化操作,存入数据库,以便做业务需求处理。以下内容是本人最近学习整理的内容,如有不足之处,敬请指正。

网页授权是通过OAuth2.0来完成网页授权的,是安全可靠的。接下来,主要介绍网页授权获取用户基本信息的方法。

一、什么是OAuth2.0

       OAuth是一个开放协议,允许用户让第三方应用以安全且标准的方式获取该用户在某一网站、移动或桌面应用上存储的私密的资源(如用户个人信息、照片、视频、联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth 2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0关注客户端开发者的简易性,同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。

    OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要分享他们的访问许可或他们数据的所有内容。(来自于网络)

二、微信公众平台OAuth2.0授权详细步骤

           1. 用户关注微信公众账号。
           2. 微信公众账号提供用户请求授权页面URL。
           3. 用户点击授权页面URL,将向服务器发起请求
           4. 服务器询问用户是否同意授权给微信公众账号(scope为snsapi_base时无此步骤)
           5. 用户同意(scope为snsapi_base时无此步骤,不弹出授权页面,直接跳转,只能获取用户openid)
           6. 服务器将code参数通过回调传给微信公众账号
           7. 微信公众账号获得code参数
           8. 微信公众账号通过code参数向服务器请求Access Token
           9. 服务器返回Access Token和OpenID给微信公众账号
          10. 微信公众账号通过Access Token向服务器请求用户信息(scope为snsapi_base时无此步骤)

          11. 服务器将用户信息回送给微信公众账号(scope为snsapi_base时无此步骤)

 三、配置授权回调页面域名:请参考微信开发文档



 四、案例

功能:用户关注微信公众号后,点击菜单授权到我们的网页

思路:点击菜单跳到授权网页,同时获取微信授权到页面的code,使用code,再去换取openid、access_token,再用access_token获取用户基本信息。

代码实现:

1、网页授权获取的用户信息类

public class SNSUserInfo implements Serializable{
            private static final long serialVersionUID = 8450724674277117086L;
    // 用户标识
    private String openid;
    // 用户昵称
    private String nickname;
    // 性别(1是男性,2是女性,0是未知)
    private int sex;  
    // 国家
    private String country;   
    // 省份
    private String province;
    // 城市
    private String city;
    // 用户头像链接
    private String headimgurl;
    // 用户特权信息

    private List<String> privilegelist;

}

2、授权Oauth2Token类

public class WeixinOauth2Token {
    // 网页授权接口调用凭证
    private String access_token;
    // 凭证有效时长
    private int expires_in;
    // 用于刷新凭证
    private String refresh_token;
    // 用户标识
    private String openid;
    // 用户授权作用域

    private String scope;

}

3、WeiXinConfig 微信相关的配置类

@Component
public class WeiXinConfig {
@Value("${wx.appid}")
private String wx_appid;
@Value("${wx.appsecret}")
private String wx_appsecret;

}

4、WeixinUtil 微信相关的工具类(封装了获取openid、access_token、用户基本信息的方法)

/**
 * Created by hadoop on 2015/9/25.
 */
public class WeixinUtil {
// 获取普通openid
public static String getOpenId(String appId, String appSecret, String code) {
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", appId)
.replace("SECRET", appSecret).replace("CODE", code);
String respJson = null;
try {
respJson = HttpUtils.fromUrl(requestUrl);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String openid = "";
Map<String, String> res = JSON.parseObject(respJson, Map.class);
openid = res.get("openid");
return openid;
}

public static String getAccessToken(String appId, String appSecret) {
String accesstoken = String.valueOf(JedisBisUtil.get("accesstoken"));
if (StringUtils.isEmpty(accesstoken)
|| "null".equals(accesstoken.toLowerCase())) {
String requestUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";
requestUrl = requestUrl.replace("APPID", appId);
requestUrl = requestUrl.replace("SECRET", appSecret);
try {
String res = HttpUtils.fromUrl(requestUrl);
Map<String, String> map = JSON.parseObject(res, Map.class);
accesstoken = String.valueOf(map.get("access_token"));
JedisBisUtil.put("accesstoken", accesstoken);
JedisBisUtil.expire("accesstoken", 3600);// 3600秒失效 最大7200
return accesstoken;
} catch (Exception e) {
ExceptionLogger.error(e);
}
}
return accesstoken;
}

public static void main(String[] args) {
String token = getAccessToken("wx4e16b59e15dba8f7",
"0113347de37589c99893e4a7fd973b9c");
System.out.println(token);
}

public static WeixinOauth2Token getOauth2AccessToken(String appId,
String appSecret, String code) {
WeixinOauth2Token wat = null;
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", appId);
requestUrl = requestUrl.replace("SECRET", appSecret);
requestUrl = requestUrl.replace("CODE", code);
try {
String res = HttpUtils.fromUrl(requestUrl);
Map<String, String> map = JSON.parseObject(res, Map.class);
if (null != map && !map.isEmpty()) {
try {
if (!map.containsKey("errcode")) {
wat = new WeixinOauth2Token();
wat.setAccess_token(map.get("access_token"));
wat.setExpires_in(Integer.parseInt(String.valueOf(map
.get("expires_in"))));
wat.setRefresh_token(map.get("refresh_token"));
wat.setOpenid(map.get("openid"));
wat.setScope(map.get("scope"));
} else {
String errorCode = map.get("errcode");
if ("40163".equals(errorCode)) {
throw new BusinessException("链接已过期");
}


}
} catch (Exception e) {
wat = null;
ExceptionLogger.error(e);
}
}


} catch (IOException e) {
// TODO Auto-generated catch block
ExceptionLogger.error(e);
throw new BusinessException(e.getMessage());
}
return wat;
}


// 获取用户基本信息
public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace(
"OPENID", openId);
// 通过网页授权获取用户信息
String res;
SNSUserInfo snsUserInfo = null;
try {
res = HttpUtils.fromUrl(requestUrl);
snsUserInfo = JSON.parseObject(res, SNSUserInfo.class);
} catch (IOException e1) {
// TODO Auto-generated catch block
ExceptionLogger.error(e1);
}
return snsUserInfo;
}

// 获取普通TICKET
public static String getTicket(String accessToken) {
String requestUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="
+ accessToken + "&type=jsapi";
try {
String res = HttpUtils.fromUrl(requestUrl);
Map<String, String> map = JSON.parseObject(res, Map.class);
return String.valueOf(map.get("ticket"));
} catch (Exception e) {
ExceptionLogger.error(e);
}
return null;
}

}

5、前端将授权的code通过ajax传到后台controller,在调用封装好的方法,先获取access_token,在用access_token获取用户基本信息(红色标记为关键代码)

@Controller
@RequestMapping(value = "/index",produces = "application/json;charset=UTF-8")
public class WxController  extends BaseController {

@Autowired
private WeiXinConfig config;
@RequestMapping(value = "/getUserByCode")
@ResponseBody
public Map<String, Object> getUserByCode(String code) {
Map<String, Object> map = new HashMap<String, Object>();
if (StringUtils.isEmpty(code)) {
throw new BusinessException("请重新进入页面");
}  
SNSUserInfo wxUser = (SNSUserInfo) JedisBisUtil.get("wxUser:code:"
+ code);
if (wxUser == null||StringUtils.isEmpty(wxUser.getOpenid())) {
WeixinOauth2Token authToken = WeixinUtil.getOauth2AccessToken(
config.getWx_appid(), config.getWx_appsecret(), code);

if (authToken == null) {
throw new BusinessException("请重新进入页面");
}  
wxUser = WeixinUtil.getSNSUserInfo(authToken.getAccess_token(),
authToken.getOpenid());

if (wxUser == null) {
throw new BusinessException("请重新进入页面");

JedisBisUtil.put("wxUser:code:"+code, wxUser);
JedisBisUtil.put("token:openid:"+authToken.getOpenid(), authToken.getAccess_token());
JedisBisUtil.expire("user:code:"+code, 1800);
JedisBisUtil.expire("wxUser:code:"+code, 1800);
JedisBisUtil.expire("token:openid:"+authToken.getOpenid(), 1800);
}
map.put("wxUser", wxUser);
return map;
}
}

ps:网页授权到页面上的code,只能使用一次,之后就失效了。


猜你喜欢

转载自blog.csdn.net/qq_15901351/article/details/80877050