1. 程式人生 > >簡單三步獲取微信公眾號使用者授權

簡單三步獲取微信公眾號使用者授權

    之前做過幾次微信公眾號開發, 一直沒有記錄下開發過程, 為方便以後不用在去各種搜尋找資料, 在此記錄開發流程...

    首先, 開通微信公眾號, 電梯直達(公眾號申請). 


第二步, 繫結開發者許可權, 方便使用開發者工具除錯


一切準備就緒, 接下來分析微信公眾號授權流程, 下圖簡單介紹授權序列圖:


在沒有弄清楚該流程之前, 我一直有個疑惑, 到底該何時提示使用者授權呢? 也就是說微信伺服器與我們的業務伺服器在哪裡作為切入點.

這裡可以有兩種方式提示使用者授權:

  1. 訪問首頁授權
  2. 訪問指定介面授權
這裡以指定介面授權作為演示, 同時通過Spring Interceptor作為校驗切入點. 為方便起見, 開發一個LoginValidation
註解:
在需要的介面上指定 LoginValidation 註解即可, (在Class中註解表示該類所有介面都需要登入可以訪問,  也可在指定方法中註解)
指定需要攔截的地址, 以及攔截器規則開發:
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;
    }
}
接下來為微信伺服器提供回撥介面
@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() 通過簡單封裝, 構建一個微信授權的重定向連結, 該連結中包含以下幾部分, 
需要重點關注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();