java實現微信小程序服務端(登陸)

                            

   微信小程序現在被普遍使用,微信小程序按照微信官網的定義來講就是:前端

   微信小程序是一種全新的鏈接用戶與服務的方式,它能夠在微信內被便捷地獲取和傳播,同時具備出色的使用體驗。java

   這就是微信小程序的魅力所在,有的時候咱們不須要去下載過多的app,只要打開微信,就能夠應用每個服務,甚至連註冊都變得簡單起來,具備特別貼近人心的用戶體驗。redis

   最近在作一個微信小程序的服務端,主要就是實現登陸、業務帳號和微信號綁定的基本功能,接下來總結一下如何實現微信小程序的服務端代碼。算法

   要去實現服務端的代碼,就要先去了解客戶端的請求。數據庫

   微信小程序開發平臺開發了很全面、功能很強大的開發API供咱們使用:json

   https://developers.weixin.qq.com/miniprogram/dev/api/小程序

   咱們來看下api中的登陸功能:微信小程序

   

 

  這個是微信小程序登陸的時序圖,很是明顯地告知了咱們微信小程序登陸的請求服務調用的過程api

  1.添加微信小程序固定信息跨域

  在註冊的時候,這個小程序的appId和appSecret已經知道了,還有固定的url,而後咱們能夠將其作爲配置文件放入後臺的代碼中去:

  

 

  2.根據小程序信息和code獲取openId和session_key

  在前端代碼中,每當咱們剛進入小程序的時候,都會去調用wx.login()方法,會獲得一個code,而後發送給咱們,而後,咱們經過code和這些已知的參數,去調用微信的接口,去獲取openId和session_key,做爲登陸態的標識

  

/**
     * 獲取微信小程序的session_key和openid
     *
     * @author hengyang4
     * @param code 微信前端login()方法返回的code
     * @return jsonObject
     *
     * */
    public JSONObject getSessionKeyAndOpenId(String code)throws Exception{
        //微信登陸的code值
        String wxCode = code;
        //讀取屬性文件
        ResourceBundle resourceBundle = ResourceBundle.getBundle("weixin");
        //服務器端調用接口的url
        String requestUrl = resourceBundle.getString("url");
        //封裝須要的參數信息
        Map<String,String> requestUrlParam = new HashMap<String,String>();
        //開發者設置中的appId
        requestUrlParam.put("appid",resourceBundle.getString("appId"));
        //開發者設置中的appSecret
        requestUrlParam.put("secret",resourceBundle.getString("appSecret"));
        //小程序調用wx.login返回的code
        requestUrlParam.put("js_code", wxCode);
        //默認參數
        requestUrlParam.put("grant_type", "authorization_code");

        JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl,requestUrlParam));
        return jsonObject;
    }

    /**
     * 向指定 URL 發送POST方法的請求
     *
     * @param url 發送請求的 URL
     * @return 所表明遠程資源的響應結果
     */
    public String sendPost(String url, Map<String, ?> paramMap) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";

        String param = "";
        Iterator<String> it = paramMap.keySet().iterator();

        while(it.hasNext()) {
            String key = it.next();
            param += key + "=" + paramMap.get(key) + "&";
        }

        try {
            URL realUrl = new URL(url);
            // 打開和URL之間的鏈接
            URLConnection conn = realUrl.openConnection();
            // 設置通用的請求屬性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 發送POST請求必須設置以下兩行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 獲取URLConnection對象對應的輸出流
            out = new PrintWriter(conn.getOutputStream());
            // 發送請求參數
            out.print(param);
            // flush輸出流的緩衝
            out.flush();
            // 定義BufferedReader輸入流來讀取URL的響應
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        //使用finally塊來關閉輸出流、輸入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }

  3. 將openId和session_key生成sessionId返回客戶端

             第三方服務器端拿到請求回來的session_key和openid,先留着,不能給客戶端;而後用操做系統提供的真正隨機數算法生成一個新的session,叫session_id

 · 因此,每次前端wx.login()後調用的服務端的controller咱們就應該這樣寫:

 @RequestMapping(value = "/loginByWeixin", produces = "application/json;charset=UTF-8")
    public String loginByWeixin(@RequestParam("code") String code) throws Exception{
        //獲得用戶的openId + sessionKey
        JSONObject jsonObject = getSessionKeyAndOpenId(code);
        log.info(jsonObject.toString());
        System.out.println(jsonObject);
        String openId = jsonObject.getString("openid");

        String sessionKey = jsonObject.getString("session_key");
        //組裝結果
        Map<String,Object> resMap = new HashMap<String,Object>();
        //判斷openId是否存在用戶表中
        UserInfo userInfo = userInfoMapper.selectByUserName(openId);
        if(ValidateUtil.isEmpty(userInfo)){
            //不存在該用戶關聯關係
            log.info("驗證是否綁定微信","未登陸");
            resMap.put("code",ErrorCode.ERR_WEIXIN_USER_EMPTY.getErrorCode());
            resMap.put("desc",ErrorCode.ERR_WEIXIN_USER_EMPTY.getErrorMessage());
        }else {
            //組裝結果
            log.info("驗證是否綁定微信", "用戶查詢成功");
            //0:操做成功
            resMap.put("code", ErrorCode.ERR_SUCCEED.getErrorCode());
            resMap.put("desc", ErrorCode.ERR_SUCCEED.getErrorMessage());
            resMap.put("userInfo", userInfo);
        }
        //將經過md5生成sessionId(通常是用個操做系統提供的真正隨機數算法生成新的session)
        String sessionId = MD5.EncodeByMd5(openId+sessionKey+ DateUtil.getNowTime());
        jedisCache.hashSet(sessionId,"ses",openId);
        resMap.put("sessionId",sessionId);
        return JSONConvertor.toJSON(resMap);

    }

  4. 使用過濾器過濾請求頭中含有session_id的請求

  而後咱們將session_id爲key,微信服務端返回的openId爲值,保存起來,這裏咱們用了redis去緩存這個session信息,openId爲咱們系統用戶與微信綁定的標識

  接下來,咱們每次請求的時候,都會將session_id放入請求頭中去,而後判斷在redis中是否有key爲該值得鍵值對,從而判斷用戶session是否失效

  咱們這裏使用了過濾器來攔截用戶請求

  

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException,ServletException{
        //設置跨域請求
        HttpServletResponse resp = (HttpServletResponse)response;
        resp.setHeader("Access-Control-Allow-Origin", "*");

        HttpServletRequest req = (HttpServletRequest)request;
        //獲取請求時的sessionId
        String sessionId = req.getHeader("sessionId");
        if(StrUtil.IsNullOrEmpty(sessionId)){
            //該請求不須要驗證session,直接經過
            log.info("sessionId過濾","該請求不須要過濾,經過");
            chain.doFilter(request,response);
            return;
        }else {
            //只有在緩存中存在該sessionId才能進行請求
            if (!jedisCache.existKey(sessionId)) {
                // 登陸信息已過時,請從新登陸
                log.info("sessionId過濾", "登陸信息失效,請從新登陸");
                response.getWriter().write("登陸信息失效,請從新登陸");
                return;
            }

            log.info("sessionId過濾", "session驗證成功");
            chain.doFilter(request, response);
        }
    }

  5. 在數據庫中創建用戶與微信用戶惟一標識的關聯關係

  每當用戶註冊或者登錄時,請求中都會含有session_id,而後咱們將session_id做爲key去redis中查找,獲得value值,也就是咱們以前存的openId,而後咱們將openId和用戶信息進行數據庫表中的關聯,以後,咱們調用登錄方法的時候,若是該openId有關聯的用戶信息,則不須要去登陸,直接給前端返回用戶信息便可,就是咱們以前的那段代碼:

  

   在這裏,咱們的微信小程序服務端代碼就基本實現了,能夠用於微信小程序與業務系統的登陸,微信小程序的功能還有不少,以後咱們有機會多去試一下其餘功能,感覺小程序的強大和快捷。

相關文章
相關標籤/搜索