【微信公众号开发】【8】网页授权获取用户基本信息(OAuth 2.0)
前言:
1,在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。
请注意,这里填写的是域名,而不是URL,因此请勿加 http:// 等协议头。(例:xxx.com)
2,scope参数
(1)以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
(2)以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
(3)对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知
3,code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期
正文:
官方流程:
1、引导用户进入授权页面同意授权,获取code
2、通过code换取网页授权access_token(与基础支持中的access_token不同)
3、如果需要,开发者可以刷新网页授权access_token,避免过期
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
而我的流程为:获取code —code换取openID—openID获取用户基本信息(官方的我还没试过,我的流程目前也是没问题的)
//微信公众号 OAuth2.0网页授权 地址 public static String OAuth_Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=123#wechat_redirect"; @RequestMapping(value = "home", method = RequestMethod.GET) public String home(ServletResponse response, Model model, String code) throws IOException { HttpServletResponse httpres = (HttpServletResponse) response; String openId = CookieHelper.getByName(Config.OPEN_ID);//获取cookie中的openId if(StringUtils.isEmpty(openId) && StringUtils.isEmpty(code)){ httpres.sendRedirect(String.format(OAuth_Url, appId, String.format(Config.domain()+"/studio/doctorHome/%s/%s/%s", platform, type, doctorId)));//获取code }else{ WeixinUserInfo wxInfo = getWxInfo(code);//WeixinUserInfo类第七章有 unionId = wxInfo.getUnionId(); openId = wxInfo.getOpenId(); } return "home"; } public static WeixinUserInfo getWxInfo(String code) throws IOException { WeixinUserInfo wxInfo = new WeixinUserInfo(); //获取用户的微信信息 String openId = CookieHelper.getByName(OPEN_ID); if(!StringUtils.isEmpty(code)){ openId = WeixinUtil.getOpenIdByCode(appid, appSecret, code);//code换取openID } String access_token = WeixinUtil.getAccessToken(fileName, appid, appSecret);//该方法第二章有 wxInfo = WeixinUtil.getUserInfo(access_token, openId);//openID获取用户基本信息,该方法第七章有 //存cookie CookieHelper.addCookie(OPEN_ID, wxInfo.getOpenId(), Config.COOKIE_AGE); return wxInfo; }
WeixinUtil.getOpenIdByCode
public static String getOpenIdByCode(String appId, String appSecret, String code) { String openID = ""; String openid_info_url = "https://api.weixin.qq.com/sns/oauth2/access_token"; String jsonStrOpenId = HttpRequest.sendGet(openid_info_url, "appid=" + appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code"); JSONObject OpenIdJson = JSONObject.fromObject(jsonStrOpenId); openID = (String) OpenIdJson.get("openid"); return openID; }
5,优化——拦截器获取openId,这样代码比较简洁
拦截器
@Bean
public FilterRegistrationBean<WxFilter> buildAFilter() {
FilterRegistrationBean<WxFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setOrder(2);
filterRegistrationBean.setFilter(new WxFilter());
filterRegistrationBean.setName("WxFilter");
filterRegistrationBean.addUrlPatterns("/wx/*");
return filterRegistrationBean;
}
拦截实现
public class WxFilter implements Filter { private static Logger logger = LoggerFactory.getLogger(WxFilter.class); private WxBaseService wxBaseService; private RedisDao redisDao; public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException { logger.info("enter wx filter"); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String openId = this.getOpenId(request, response); if(openId != null){ //只有在需要向微信获取code时才会为null if (!redisDao.hasKey(openId)) { //视情况而定,这里其实就是以openId为主键,将一些常用信息查询出来,存到Redis,便于以后使用 //建议主键为前缀+openId,例:studio_xxxx logger.info("在Filter中把用户信息存储到redis中"); BaseUserInfo user = this.wxBaseService.BaseUserInfo(openId); redisDao.setKey(openId, new ObjectMapper().writeValueAsString(user)); } chain.doFilter(request, response); } } //填充用户信息 private String getOpenId(HttpServletRequest request, HttpServletResponse response) throws IOException { String url = HttpHelper.getParamUrl(request); String code = request.getParameter("code"); String openId = CookieHelper.getByName(Config.COOKIE_OPEN_ID); //获取cookie //cookie失效或者点击菜单进入系统,重新获取用户的微信信息 //点击菜单进入系统(在首页重新拉取数据)——因为用户切换微信号,可能会存在缓存 if(StringUtils.isEmpty(openId) || url.contains("wx/studioDoctor/article/index?frommenu") || url.contains("wx/qrcode_rank/index?frommenu")) { if(StringUtils.isEmpty(code)){ logger.info("WxFilter redirect"); url = String.format(WxConfig.OAUTH_URL, Config.APP_ID, url); response.sendRedirect(url); return null; //redirect执行过程中会往下走,故返回空值 }else{ openId = WeixinUtil.getOpenIdByCode(Config.APP_ID, Config.APP_SECRET, code); request.setAttribute(Config.COOKIE_OPEN_ID, openId); CookieHelper.addCookie(response, Config.COOKIE_OPEN_ID, openId, Config.COOKIE_AGE); } } logger.info("WxFilter openId: " + openId); return openId; } //拦截器中服务层方法访问不到,需做处理 public void init(FilterConfig config) throws ServletException { ServletContext context = config.getServletContext(); ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context); wxBaseService = (WxBaseService) ctx.getBean(WxBaseService.class); redisDao = (RedisDao) ctx.getBean(RedisDao.class); } public void destroy() { } }