简单三步获取微信公众号用户授权

    之前做过几次微信公众号开发, 一直没有记录下开发过程, 为方便以后不用在去各种搜索找资料, 在此记录开发流程...


    首先, 开通微信公众号, 电梯直达(公众号申请). 

简单三步获取微信公众号用户授权_第1张图片

第二步, 绑定开发者权限, 方便使用开发者工具调试

简单三步获取微信公众号用户授权_第2张图片

一切准备就绪, 接下来分析微信公众号授权流程, 下图简单介绍授权序列图:


简单三步获取微信公众号用户授权_第3张图片

在没有弄清楚该流程之前, 我一直有个疑惑, 到底该何时提示用户授权呢? 也就是说微信服务器与我们的业务服务器在哪里作为切入点.

这里可以有两种方式提示用户授权:

  1. 访问首页授权
  2. 访问指定接口授权
这里以指定接口授权作为演示, 同时通过Spring Interceptor作为校验切入点. 为方便起见, 开发一个 LoginValidation 注解:
简单三步获取微信公众号用户授权_第4张图片
在需要的接口上指定 LoginValidation 注解即可, ( 在Class中注解表示该类所有接口都需要登录可以访问,  也可在指定方法中注解)
简单三步获取微信公众号用户授权_第5张图片
指定需要拦截的地址, 以及拦截器规则开发:
public class LoginInterceptor extends HandlerInterceptorAdapter {

    private final Logger LOGGER = LoggerFactory.getLogger(getClass());

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String path = request.getServletPath();
        HandlerMethod method = (HandlerMethod) handler;
        Method originO = method.getMethod();
        Class clazz = originO.getDeclaringClass();

        LoginValidation classAnnotation = clazz.getAnnotation(LoginValidation.class);
        LoginValidation methonAnnotation = method.getMethodAnnotation(LoginValidation.class);
        boolean needLogin = (null != methonAnnotation || null != classAnnotation);
        boolean isGuest = (null == WebUtil.getUser());

        if (needLogin && isGuest) {
            ResponseBody responseBody = ((HandlerMethod) handler).getMethodAnnotation(ResponseBody.class);

            boolean isReqData = (null != responseBody);
            if (isReqData)
                path = "/"; // 如果是数据请求, 授权后返回首页
            redirectAuth(request, response, path);
            return false;
        }

        return true;
    }

    private void redirectAuth(HttpServletRequest request, HttpServletResponse response, String path) throws Exception {
        LOGGER.info("用户未登录, 跳转到授权页面");
        boolean normalReq = !isAjaxRequest(request);
        String redirectPath = "/wx/auth";
        if (normalReq) {
            response.sendRedirect(redirectPath);
        } else {
            Map map = Maps.newHashMap();
            map.put("flag", false);
            map.put("redirect", redirectPath);
            WebUtil.writeJSON(map);
        }
        WebUtil.setSessionAttr(Constants.SESSION_WECHAT_AUTH_BEFORE_PATH, path);
    }

    public boolean isAjaxRequest(HttpServletRequest request) {
        String header = request.getHeader("X-Requested-With");
        boolean isAjax = "XMLHttpRequest".equals(header) ? true : false;
        return isAjax;
    }
}
简单三步获取微信公众号用户授权_第6张图片
接下来为微信服务器提供回调接口
@Controller
@RequestMapping("/wx")
public class WeChatController {
    private static final String APP_ID = "wx1xxxxxxxxxxxxxxx";
    private static final String APP_SECURITY = "aa23461daxxxxxxxxxxxxxxxxxxxxxxxxx";

    @Autowired
    private UserService userService;

    /**
     * 构建授权页面
     */
    @RequestMapping("auth")
    public String auth();

    /**
     * 微信服务器回调接口
     * @param code 用于换区access_Token, 用来获取用户数据
     * @param state 自定义数据
     */
    @RequestMapping("callback")
    public String callback(String code, String state) 

}
一切准备接续, 接下来重点关注 WeChatController, 我们先来看看 auth 这个接口的实现
@RequestMapping("auth")
public String auth() {
    // 本地开发 WebUtil.getBaseUrl() 总是为 localhost
    // String callback = URLEncoder.encode(WebUtil.getBaseUrl() + "wx/callback", "UTF-8");
    String callback = StringUtil.encodeUrl("http://df52b8f6.ngrok.io/wx/callback", "UTF-8");
    String url = WeChatUtil.createUrl(WeChatUtil.newAuthorizeParam(APP_ID, callback, WeChatUtil.Scope.SNSAPI_USERINFO, null));
    return "redirect:" + url;
}
通过 WeChatUtil.createUrl() 通过简单封装, 构建一个微信授权的重定向链接, 该链接中包含以下几部分, 
简单三步获取微信公众号用户授权_第7张图片
需要重点关注Scope值:
  1. snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)
  2. snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息
接下来看看 callback 的实现:
/**
 * 微信服务器回调接口
 * @param code 用于换区access_Token, 用来获取用户数据
 * @param state 自定义数据
 */
@RequestMapping("callback")
public String callback(String code, String state) {
    AccessData accessData = WeChatUtil.accessToken(WeChatUtil.newAccessParam(code, APP_ID, APP_SECURITY));
    String openId = accessData.getOpenId();
    User user = userService.getByOpenId(openId);
    if (null == user) {
        WeChatUser wxUserInfo = accessData.getUser();
        user = userService.loginByWeChat(wxUserInfo);
    }
    WebUtil.setUser(user);
    String path = WebUtil.getSessionAttr(Constants.SESSION_WECHAT_AUTH_BEFORE_PATH);
    return "redirect:" + path;
}

WeChatUtil.accessToken() 通过回调参数 code 获取 AccessData 对象, 通过该对象可以获取到 openid 和 WeChatUser 数据.

  1. openid 为当前用户对当前公众号关联应用唯一识别码.
  2. WeChatUser 为用户公开信息, 可拿到昵称、性别、所在地等信息. Scope 必须为 WeChatUtil.Scope.SNSAPI_USERINFO (也就是snsapi_userinfo)

根据具体业务使用 WeChatUser 对象即可.


三步获取用户数据总结:

第一步, 构建授权重定向地址  WeChatUtil.createUrl(WeChatUtil.newAuthorizeParam(appId, callback, WeChatUtil.Scope, state))
第二步,获取用户授权 AccessData accessData = WeChatUtil.accessToken(WeChatUtil.newAccessParam(code, appId, security));
第三步,获取用户数据 WeChatUser wxUserInfo = accessData.getUser();

WeChatUtil相关代码: https://gitee.com/osby/third/tree/master/src/main/java/org/yong/third/wx

你可能感兴趣的:(spring,Java)