用戶
 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

掃一掃,登錄網站

小程序社區 首頁 教程 查看內容

微信小程序后端開發流程

Rolan 2019-11-13 00:42

1、前端調用 wx.login 返回了code,然后調用wx.getUserInfo獲取到用戶的昵稱 頭像 2、服務端根據code去微信獲取openid, 接口地址:developers.weixin.qq.com/miniprogram…微信小程序后端接口開發controller層publi ...

1、前端調用 wx.login 返回了code,然后調用wx.getUserInfo獲取到用戶的昵稱 頭像 2、服務端根據code去微信獲取openid, 接口地址: developers.weixin.qq.com/miniprogram…

微信小程序后端接口開發

controller層

public class OauthController {

    @Autowired
    private WeChatService weChatService;

    /**
     * 微信授權用js_code換取openId
     * @param code
     * @return
     */
    @GetMapping("/code2Session")
    public BaseResponse code2Session(String code) {
        log.info("code2Session,code={}", code);
        if (StringUtil.isEmpty(code)) {
            return BaseResponse.buildFail("參數異常");
        }
        Code2SessionResponse res = weChatService.code2Session(code);
        log.info("code2Session,res={}", res);
        if (!res.isSuccess()) {
            return BaseResponse.buildFail(res.getErrCode(), res.getErrMsg());
        }
        return BaseResponse.buildSuccess(res);
    }


 /**
     * 解密獲取手機號
     * @param request
     * @param response
     * @param param
     * @return
     */
    public BaseResponse decryptGetPhone(HttpServletRequest request, HttpServletResponse response,
                                    @RequestBody OauthParam param) {
   
            if (!StringUtil.isEmpty(param.getOpenId())) {//微信授權登錄
                String sessionKey = weChatService.getSessionKey(param.getOpenId());
                if (StringUtil.isEmpty(sessionKey)) {
                    return BaseResponse.buildFail("會話不存在");
                }
                Sha1Utils sha = new Sha1Utils();
                // 獲取用戶信息
                log.debug("微信登陸 sessionKey = {}", sessionKey);
                String userInfoStr = sha.decryptWXAppletInfo(sessionKey, param.getEncryptedData(), param.getIv());
                if (StringUtil.isEmpty(userInfoStr)) {
                    return BaseResponse.buildFail("無法獲取用戶信息");
                }
                JSONObject json = JSONObject.parseObject(userInfoStr);
                //綁定微信的手機號
                String tel = json.getString("purePhoneNumber");
                Assert.isTrue(!StringUtils.isEmpty(tel), "無法獲取用戶手機號");
                BaseResponse baseResponse=new BaseResponse();
                baseResponse.setResultInfo(tel);
                baseResponse.setState(0);
                return baseResponse;
            }

    }
}
復制代碼

接口

public interface WeChatService {


    /**
     * 用code換取openid
     *
     * @param code
     * @return
     */
    Code2SessionResponse code2Session(String code);


    /**
     * 獲取憑證
     *
     * @return
     */
    String getAccessToken();


    /**
     * 獲取憑證
     *
     * @param isForce
     * @return
     */
    String getAccessToken(boolean isForce);


    String getSessionKey(String openId);

}
復制代碼

實現類

public class WeChatServiceImpl implements WeChatService {

    //獲取配置文件數據
    @Value("${wechat.miniprogram.id}")
    private String appId;

    @Value("${wechat.miniprogram.secret}")
    private String appSecret;

    @Reference
    private SysUserService sysUserService;


    @Override
    public Code2SessionResponse code2Session(String code) {
        String rawResponse = HttpClientUtil
                .get(String.format(WechatConstant.URL_CODE2SESSION, appId, appSecret, code));
        log.info("rawResponse====={}", rawResponse);
        Code2SessionResponse response = JSON.parseObject(rawResponse, Code2SessionResponse.class);
        if (response.isSuccess()) {
            cacheSessionKey(response);
        }
        return response;
    }

    private void cacheSessionKey(Code2SessionResponse response) {
        RedisCache redisCache = RedisCache.getInstance();
        String key = RedisCacheKeys.getWxSessionKeyKey(response.getOpenId());
        redisCache.setCache(key, 2147483647, response.getSessionKey());
    }

    @Override
    public String getAccessToken() {
        return getAccessToken(false);
    }

    @Override
    public String getAccessToken(boolean isForce) {
        RedisCache redisCache = RedisCache.getInstance();
        String accessToken = null;
        if (!isForce) {
            accessToken = redisCache.getCache(RedisCacheKeys.getWxAccessTokenKey(appId));
        }
        if (StringUtil.isNotEmpty(accessToken)) {
            return accessToken;
        }
        String rawResponse = HttpClientUtil
                .get(String.format(WechatConstant.URL_GET_ACCESS_TOKEN, appId, appSecret));
        AccessTokenResponse response = JSON.parseObject(rawResponse, AccessTokenResponse.class);
        log.info("getAccessToken:response={}", response);
        if (response.isSuccess()) {
            redisCache.setCache(RedisCacheKeys.getWxAccessTokenKey(appId), 7000, response.getAcessToken());
            return response.getAcessToken();
        }
        return null;
    }


    @Override
    public String getSessionKey(String openId) {
        RedisCache redisCache = RedisCache.getInstance();
        String key = RedisCacheKeys.getWxSessionKeyKey(openId);
        String sessionKey = redisCache.getCache(key);
        return sessionKey;
    }
}
復制代碼

用到的解密工具類

public class Sha1Utils {
    public static String decryptWXAppletInfo(String sessionKey, String encryptedData, String iv) {
        String result = null;
        try {
            byte[] encrypData = Base64.decodeBase64(encryptedData);
            byte[] ivData = Base64.decodeBase64(iv);
            byte[] sessionKeyB = Base64.decodeBase64(sessionKey);

            AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(sessionKeyB, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            byte[] doFinal = cipher.doFinal(encrypData);
            result = new String(doFinal);
            return result;
        } catch (Exception e) {
            //e.printStackTrace();
            log.error("decryptWXAppletInfo error",e);
        }
        return null;
    }

}
復制代碼

網絡請求工具類

public class HttpClientUtil {

    // utf-8字符編碼
    public static final String                        CHARSET_UTF_8          = "utf-8";

    // HTTP內容類型。
    public static final String                        CONTENT_TYPE_TEXT_HTML = "text/xml";

    // HTTP內容類型。相當于form表單的形式,提交數據
    public static final String                        CONTENT_TYPE_FORM_URL  = "application/x-www-form-urlencoded";

    // HTTP內容類型。相當于form表單的形式,提交數據
    public static final String                        CONTENT_TYPE_JSON_URL  = "application/json;charset=utf-8";

    // 連接管理器
    private static PoolingHttpClientConnectionManager pool;

    // 請求配置
    private static volatile RequestConfig requestConfig;

    private static CloseableHttpClient getNewHttpClient() {

        CloseableHttpClient httpClient = HttpClients.custom()
            // 設置連接池管理
            .setConnectionManager(pool)
            // 設置請求配置
            .setDefaultRequestConfig(getRequestConfig())
            // 設置重試次數
            .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)).build();

        return httpClient;
    }

    /**
     * 發送 post請求
     *
     * @param httpUrl
     *            地址
     */
    public static String post(String httpUrl) {
        // 創建httpPost
        HttpPost httpPost = new HttpPost(httpUrl);
        return request(httpPost);
    }

    public static byte[] postRaw(String httpUrl) {
        // 創建httpPost
        HttpPost httpPost = new HttpPost(httpUrl);
        return requestRaw(httpPost);
    }

    /**
     * 發送 get請求
     *
     * @param httpUrl
     */
    public static String get(String httpUrl) {
        // 創建get請求
        HttpGet httpGet = new HttpGet(httpUrl);
        return request(httpGet);
    }

    /**
     * 發送 post請求(帶文件)
     *
     * @param httpUrl
     *            地址
     * @param maps
     *            參數
     * @param fileLists
     *            附件
     */
    public static String post(String httpUrl, Map<String, String> maps, List<File> fileLists,
                              String fileName) {
        HttpPost httpPost = new HttpPost(httpUrl);// 創建httpPost
        MultipartEntityBuilder meBuilder = MultipartEntityBuilder.create();
        if (maps != null) {
            for (String key : maps.keySet()) {
                meBuilder.addPart(key, new StringBody(maps.get(key), ContentType.TEXT_PLAIN));
            }
        }
        if (fileLists != null) {
            for (File file : fileLists) {
                FileBody fileBody = new FileBody(file);
                meBuilder.addPart(fileName, fileBody);
            }
        }
        HttpEntity reqEntity = meBuilder.build();
        httpPost.setEntity(reqEntity);
        return request(httpPost);
    }

    public static String post(String httpUrl, Map<String, String> maps, List<File> fileLists) {
        return post(httpUrl, maps, fileLists, "file");
    }

    public static String post(String httpUrl, List<File> fileLists) {
        return post(httpUrl, Collections.emptyMap(), fileLists, "file");
    }

    /**
     * 發送 post請求
     *
     * @param httpUrl
     *            地址
     * @param params
     *            參數(格式:key1=value1&key2=value2)
     *
     */
    public static String post(String httpUrl, String params) {
        HttpPost httpPost = new HttpPost(httpUrl);// 創建httpPost
        try {
            // 設置參數
            if (params != null && params.trim().length() > 0) {
                StringEntity stringEntity = new StringEntity(params, "UTF-8");
                stringEntity.setContentType(CONTENT_TYPE_FORM_URL);
                httpPost.setEntity(stringEntity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return request(httpPost);
    }

    /**
     * 發送 post請求
     *
     * @param maps
     *            參數
     */
    public static String post(String httpUrl, Map<String, String> maps) {
        String param = convertStringParamter(maps);
        return post(httpUrl, param);
    }



    /**
     * 發送 post請求 發送json數據
     *
     * @param httpUrl
     *            地址
     * @param content
     *
     *
     */
    public static String post(String httpUrl, String content, String contentType) {
        //        HttpPost httpPost = new HttpPost(httpUrl);// 創建httpPost
        //        try {
        //            // 設置參數
        //            if (StringUtils.isNotEmpty(content)) {
        //                StringEntity stringEntity = new StringEntity(content, "UTF-8");
        //                stringEntity.setContentType(contentType);
        //                httpPost.setEntity(stringEntity);
        //            }
        //        } catch (Exception e) {
        //            e.printStackTrace();
        //        }
        //        return request(httpPost);
        return new String(postRaw(httpUrl, content, contentType), StandardCharsets.UTF_8);
    }

    public static byte[] postRaw(String httpUrl, String content, String contentType) {
        HttpPost httpPost = new HttpPost(httpUrl);// 創建httpPost
        try {
            // 設置參數
            if (StringUtils.isNotEmpty(content)) {
                StringEntity stringEntity = new StringEntity(content, "UTF-8");
                stringEntity.setContentType(contentType);
                httpPost.setEntity(stringEntity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return requestRaw(httpPost);
    }

    /**
     * 發送 post請求 發送json數據
     *
     * @param httpUrl
     *            地址
     * @param paramsJson
     *            參數(格式 json)
     *
     */
    public static String postJson(String httpUrl, String paramsJson) {
        return post(httpUrl, paramsJson, CONTENT_TYPE_JSON_URL);
    }

    public static byte[] postJsonRaw(String httpUrl, String paramsJson) {
        return postRaw(httpUrl, paramsJson, CONTENT_TYPE_JSON_URL);
    }

    /**
     * 發送 post請求 發送xml數據
     *
     * @param url   地址
     * @param paramsXml  參數(格式 Xml)
     *
     */
    public static String postXml(String url, String paramsXml) {
        return post(url, paramsXml, CONTENT_TYPE_TEXT_HTML);
    }

    /**
     * 將map集合的鍵值對轉化成:key1=value1&key2=value2 的形式
     *
     * @param parameterMap
     *            需要轉化的鍵值對集合
     * @return 字符串
     */
    public static String convertStringParamter(Map parameterMap) {
        StringBuilder parameterBuffer = new StringBuilder();
        if (parameterMap != null) {
            Iterator iterator = parameterMap.keySet().iterator();
            String key = null;
            String value = null;
            while (iterator.hasNext()) {
                key = (String) iterator.next();
                if (parameterMap.get(key) != null) {
                    value = (String) parameterMap.get(key);
                } else {
                    value = "";
                }
                parameterBuffer.append(key).append("=").append(value);
                if (iterator.hasNext()) {
                    parameterBuffer.append("&");
                }
            }
        }
        return parameterBuffer.toString();
    }

    /**
     * 發送請求
     *
     * @param request
     * @return
     */
    public static byte[] requestRaw(HttpRequestBase request) {

        CloseableHttpClient httpClient;
        CloseableHttpResponse response = null;
        // 響應內容
        //        String responseContent = null;
        byte[] rawResponse = null;
        try {
            // 創建默認的httpClient實例.
            httpClient = getNewHttpClient();
            // 配置請求信息
            request.setConfig(requestConfig);
            // 執行請求
            response = httpClient.execute(request);
            // 得到響應實例
            HttpEntity entity = response.getEntity();

            // 可以獲得響應頭
            // Header[] headers = response.getHeaders(HttpHeaders.CONTENT_TYPE);
            // for (Header header : headers) {
            // System.out.println(header.getName());
            // }

            // 得到響應類型
            // System.out.println(ContentType.getOrDefault(response.getEntity()).getMimeType());

            // 判斷響應狀態
            if (response.getStatusLine().getStatusCode() >= 300) {
                throw new Exception("HTTP Request is not success, Response code is "
                                    + response.getStatusLine().getStatusCode());
            }

            if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
                rawResponse = EntityUtils.toByteArray(entity);
                //                responseContent = EntityUtils.toString(entity, CHARSET_UTF_8);
                EntityUtils.consume(entity);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 釋放資源
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return rawResponse;
    }

    private static String request(HttpRequestBase req) {
        return new String(requestRaw(req), StandardCharsets.UTF_8);
    }

    private static RequestConfig getRequestConfig() {

        if (requestConfig == null) {
            synchronized (HttpClientUtil.class) {
                if (requestConfig == null) {
                    try {
                        //System.out.println("初始化HttpClientTest~~~開始");
                        SSLContextBuilder builder = new SSLContextBuilder();
                        builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
                        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                            builder.build());
                        // 配置同時支持 HTTP 和 HTPPS
                        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
                            .<ConnectionSocketFactory> create()
                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
                            .register("https", sslsf).build();
                        // 初始化連接管理器
                        pool = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
                        // 將最大連接數增加到200,實際項目最好從配置文件中讀取這個值
                        pool.setMaxTotal(200);
                        // 設置最大路由
                        pool.setDefaultMaxPerRoute(2);
                        // 根據默認超時限制初始化requestConfig
                        int socketTimeout = 10000;
                        int connectTimeout = 10000;
                        int connectionRequestTimeout = 10000;
                        requestConfig = RequestConfig.custom()
                            .setConnectionRequestTimeout(connectionRequestTimeout)
                            .setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout)
                            .build();

                    } catch (NoSuchAlgorithmException e) {
                        e.printStackTrace();
                    } catch (KeyStoreException e) {
                        e.printStackTrace();
                    } catch (KeyManagementException e) {
                        e.printStackTrace();
                    }

                    // 設置請求超時時間
                    requestConfig = RequestConfig.custom().setSocketTimeout(50000)
                        .setConnectTimeout(50000).setConnectionRequestTimeout(50000).build();
                }
            }
        }
        return requestConfig;
    }
}
復制代碼

常量

public interface WechatConstant {
    Integer OK_STATUS            = 0;
    String  URL_CODE2SESSION     = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";


    String  URL_GET_ACCESS_TOKEN     = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";


    String URL_GET_IMAGE = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s";
    
    
    /**
     * 給公眾號發送信息。參考https://mp.weixin.qq.com/advanced/tmplmsg?action=faq&token=708366329?=zh_CN
     */
    String  URL_SEND_TO_CHANNEL  = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
    String  URL_SEND_MESSAGE     = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s";
    
    /**
     * 發送模板消息。參考https://developers.weixin.qq.com/miniprogram/dev/api-backend/sendMiniTemplateMessage.html
     */
    String URL_SEND_TEMPLATE_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=%s";

    String  URL_QR_CODE_UNLIMTED = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";
    
    String  URL_QR_CODE = "https://api.weixin.qq.com/wxa/getwxacode?access_token=%s";

    /**
     * 獲取標簽下粉絲列表
     */
    String URL_ALL_FANS_OPENID = "https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=%s";
    /**
     * 獲取公眾號已創建的標簽
     */
    String URL_ALL_TAGS = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token=%s";

}
復制代碼

使用到的實體類

public class Code2SessionResponse implements Serializable {
    public static Integer RESPONSE_OK = 0;

    @JSONField(name = "openid")
    private String       openId;
    @JSONField(name = "session_key")
    private String       sessionKey;
    @JSONField(name = "unionid")
    private String       unionId;
    @JSONField(name = "errcode")
    private Integer      errCode;
    @JSONField(name = "errmsg")
    private String      errMsg;



    public boolean isSuccess() {
        return this.errCode == null || RESPONSE_OK.equals(this.errCode);
    }
}
復制代碼

總結:微信小程序的后端開發主要就是對用戶進行授權 , 1、前端調用 wx.login 返回了code,然后調用wx.getUserInfo獲取到用戶的昵稱 頭像 2.首先通過微信授權用js_code換取openId,來獲取openId,前端傳微信的參數 code字段 3.然后解密獲取手機號 前端需要傳openId encryptedData iv 等字段來獲取用戶的的授權手機號

這些信息都獲取后 接著就是調用后端的登陸接口,登陸接口如果只有授權登錄就是我們將接口參數為下圖最后三個字段為前端必填字段

主要步驟是根據前端的openId獲取sessionKey 然后根據sessionKey 和其他參數進行解密獲取用戶手機號

通過解密獲取授權登錄的手機號,然后根據自己的業務邏輯處理即可,這樣我們就可以根據授權的手機號進行授權登錄

鮮花
鮮花
雞蛋
雞蛋
分享至 : QQ空間
收藏
原作者: CoderZS 來自: 掘金
必中分分彩在线计划 黄色片床戏 桃乃木かな无码AV在线 吉尺明步作品封面番号 天津十一选五今天的好 七星彩开奖排列七 广西快乐十分开奖官网 德国赛车网站 5万理财3个月多少钱 大唐棋牌麻将微信群 七乐彩基本走势图表图 内蒙古十一选五 陕西十分快乐 海南4+1玩法,1是哪个 90ko足球比分网 北京澳客竞彩比分直播 山东十一选五推荐号码