1、介绍
1.1、OpenID是什么
openId是用户在当前微信公众号下的唯一标识,就是说通过这个openId,就能区分在这个公众号下具体是哪个用户。
1.2 、拓展:UnionID的作用
如果开发者拥有多个移动应用、网站应用和公众账号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为同一用户,对同一个微信开放平台下的不同应用(移动应用、网站应用和公众账号),unionid是相同的。
2、网页授权获取OpenID
2.1、网页授权流程
网页授权流程分为四步:
- 用户同意授权,获取code
- 通过code换取网页授权access_token(得到openid)
- 刷新access_token(如果需要)
- 拉取用户信息(需scope为 snsapi_userinfo)
如果只需要OpenID,只要1、2步就可以了。本文章只提供获取openId的示例代码,获取用户信息,可自行参考微信官方文档。
2.2、配置网页授权域名
微信处于安全考虑,获取微信OpenID或用户信息的网页地址,必须在微信公众号中配置(网页授权域名)。
公众号配置网页授权域名流程:
-
开发者需要先到公众平台官网中的 设置与开发 => 公众号设置 => 功能设置 => 网页授权域名 的配置选项中,修改网页授权域名;
-
填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;
-
授权回调域名配置规范为全域名,比如需要网页授权的域名为:
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、Java代码
3.1、maven引用
需要用到hutool
的HttpUtil
工具类,也可自己实现http
的get
方法。
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.27</version>
</dependency>
3.2、Controller类
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 微信公众号获取 OpenID Controller
*/
@Slf4j
@Controller
@Scope("prototype")
@RequestMapping("/wechat/")
public class WechatPublicGetOpenIDController {
@Resource
private WechatPublicGetOpenIDService wechatPublicGetOpenIDService;
//获取code
@RequestMapping(value = "/getCode", method = RequestMethod.GET)
public void getCode(HttpServletRequest request , HttpServletResponse response) throws IOException {
//获取code重定向地址
String pageUrl = wechatPublicGetOpenIDService.getCode(request);
response.sendRedirect(pageUrl);
}
/**
获取openId
@param code 微信code
@param state 获取微信code时,传的附加参数
**/
@RequestMapping(value = "/getOpenId",method = RequestMethod.GET)
public void getOpenId(@RequestParam("code") String code, @RequestParam("state") String state , HttpServletResponse response) throws IOException {
String pageUrl = wechatPublicGetOpenIDService.wechatCode(code , state);
//重定向回前端
response.sendRedirect(pageUrl);
}
}
3.3、Service类
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kz.tppd.common.enums.CommonErrorEnum;
import com.kz.tppd.common.exceptions.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Enumeration;
/**
* 微信公众号获取 OpenID service
*/
@Slf4j
@Service
public class WechatPublicGetOpenIDService {
/**
* 获取code重定向地址
* @return 地址
*/
public String getCode(HttpServletRequest request) throws UnsupportedEncodingException {
//校验是不是微信浏览器打开
String payMethod = getPayMethod(request);
if(payMethod == null){
throw new RuntimeException("请用微信");
}
String wechatPublicAppid = "微信公众号appId";
//state 用户信息(建议加密),回传时用到
String state = "";
//getOpenIdUrl 自己的获取openId公网地址(获取code成功后,会重定向回该地址))
String getOpeniIdUrl = "https://自己的网页授权域名/wechat/getOpenId";
return oauth2buildAuthorizationUrl(wechatPublicAppid , getOpeniIdUrl, state);
}
/**
* 构建认证url
* @param appId appId
* @param redirectURI 重定向地址
* @param state 用户信息,回传时用到
* @return 地址
*/
private String oauth2buildAuthorizationUrl(String appId, String redirectURI, String state) throws UnsupportedEncodingException {
return String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=%s#wechat_redirect",
appId, URLEncoder.encode(redirectURI , "UTF-8"), StringUtils.trimToEmpty(state));
}
/**
* 获取支付方式
* @param request http request
* @return 支付方式
*/
private String getPayMethod(HttpServletRequest request){
String userAgent = null;
Enumeration<String> em = request.getHeaderNames();
while (em.hasMoreElements()) {
String name = em.nextElement();
String value = request.getHeader(name);
if("user-agent".equals(name)){
userAgent = value;
break;
}
}
if(StringUtils.isBlank(userAgent)){
return null;
}
if(userAgent.contains("MicroMessenger")){
return "WECHAT";
} else if(userAgent.contains("AlipayClient")){
return "ALIPAY";
}
log.info("userAgent:{}" , userAgent);
return null;
}
/**
* 静默授权-微信code
* @param code 微信code
* @param state url中传的加密参数
* @return 地址
*/
public String wechatCode(String code, String state){
//获取微信openId
String openId = getWechatOpenId(code);
//state为,获取code时,自己设置的附加参数
return "前端获取OpenID后的成功页面";
}
/**
* 获取微信openId
* @param code 微信code
* @return 微信openId
*/
private String getWechatOpenId(String code){
String wechatPublicAppid = "微信公众号appId";
String wechatPublicAppSecret = "微信公众号开发密钥";
String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
, wechatPublicAppid , wechatPublicAppSecret , code);
log.info("微信获取openId 请求地址:{}" , url);
String httpReponse = HttpUtil.get(url);
log.info("微信获取openId 返回参数:{}" , httpReponse);
if(StringUtils.isBlank(httpReponse)){
throw new RuntimeException("微信获取openId返回参数为空");
}
JSONObject jsonObject = JSON.parseObject(httpReponse);
//返回失败
if(jsonObject.containsKey("errcode")){
throw new BaseException(CommonErrorEnum.BUSINESS_ERROR.getCode() , jsonObject.getLong("errcode") + ":" + jsonObject.getString("errmsg"));
}
String openId = jsonObject.getString("openid");
if(StringUtils.isBlank(openId)){
throw new BaseException(CommonErrorEnum.BUSINESS_ERROR.getCode() , "微信返回的openId为空");
}
return openId;
}
}
3.4、测试
在微信公众号中,重定向访问如下地址:
https://自己的网页授权域名/wechat/getCode
必须访问网页授权域名,不然没有权限。