spring mvc框架實現微信小程序登錄(前台+后台)


本文將詳細的介紹微信小程序的登錄流程以及在ssm框架下如何實現小程序用戶登錄(日后補上過濾器的配置。。。)

登錄流程概要

主要的登錄流程可以參考官方提供的一張流程圖:

 

1.微信前台頁面:

在微信版本更新之后,提高了安全機制,我們需要為用戶提供一個授權按鈕,讓用戶同意授權,頁面中的button必須包含 open-type="getUserInfo"這個屬性:

一般授權頁面如下:

    <form bindsubmit="bindSave">
      <wxc-button type="success" size='large' btn-style='margin-top:10%;' open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo"
        value="同意授權登錄"></wxc-button>
      <view style='width:100%;padding-left:30rpx;font-size: 30rpx;margin-top:50rpx;'>1. 同意當前小程序獲取我的微信頭像;</view>
      <view style='width:100%;padding-left:30rpx;font-size: 30rpx;margin-top:20rpx;'>2. 同意當前小程序獲取我的微信昵稱等其他信息;</view>
      <wxc-button type="danger" size='large' btn-style='margin-top:30rpx;' bindtap='rejectLogin' value="拒絕授權"></wxc-button>
    </form>

2.登錄前台實現

當用戶登錄時,我們先去判斷小程序緩存中用戶的token存不存在:

  -----如果存在,將token傳到后台去檢查redis緩存中的用戶信息是否失效:

    -----如果失效,前台將用戶信息刪除,重新調用登錄接口。

    -----未失效,返回原頁面,后台更新緩存失效時間。

  -----如果不存在,前台調用官方提供的小程序登錄接口:wx.login(),獲得它返回的code:

    -----code獲取成功,前台調用wx.getSetting()獲取授權以及用戶的授權配置:

      -----用戶授權成功,前台調用wx.getUserInfo()獲取encryptedData(密文)和 iv(偏移向量

         ------以上都獲取成功后,我們將codeencryptedData和iv全部傳到后台。

         (code用來換取openid和sessionKey,encryptedData和iv用來獲得並解密用戶信息)

           -----如果后台獲取用戶信息成功,返回token和用戶信息到前台,前台小程序在緩存中更新token和用戶信息。

           登錄成功。

     -----------------------任意一個步驟失敗,提示用戶無法登錄。

 

微信小程序前台登錄函數如下(下面這個函數較長,可以拆分,看的更清晰):

  login: function () {
    let that = this;
    let token = wx.getStorageSync("token");
    console.log("進入頁面登錄開始取token"+token);
    if (token) {
      console.log("如果token存在");
      wx.request({
        url: urlMangerData.api0_check_token,///wxma/user/check-token
        data: {
          token: token
        },
        success: function (res) {
          console.log("前台token存在,校驗token后返回值"+JSON.stringify(res));
          //如果后台緩存已經不在了
          if (!res.data.success) {
            console.log("如果后台緩存已經失效了,小程序清空token和userinfo和openid");
            wx.removeStorageSync("token");
            wx.removeStorageSync("userInfo");
            that.login();
          } else {
            console.log("后台緩存中的信息也存在,直接返回");
            // 回到原來的地方
            wx.navigateBack();
          }
        }
      });
    }
    console.log("如果token不存在");
    //小程序登錄,
    wx.login({
      success: function (res) {
        if(res.code){          
      wx.getSetting({
        success: res2 => {
          if (res2.authSetting["scope.userInfo"]) {
          wx.getUserInfo({
            success:datas=>{
              console.log("開始獲取code和一些加密過的用戶信息" + res.code);
              console.log("datas.encryptedData" + datas.encryptedData);
              wx.request({
                url: urlMangerData.api0_loginAuth,
                data: {
                  code: res.code,
                  encryptedData: datas.encryptedData,
                  iv: datas.iv
                },
                header: {
                  'content-type': 'application/json'
                },
                success: function (res) {
                  console.log("后台成功后得到返回值" + res.data.code);
                  if(res.data.code != 0) {
                    console.log("后台登錄錯誤")
                    wx.hideLoading();
                    wx.showModal({
                      title: "提示",
                      content: "無法登錄,請重試",
                      showCancel: false
                    });
                    return;
                  }
                  console.log("存儲用戶信息token" + res.data.token);
                  console.log("存儲用戶信息userInfo" + JSON.stringify(res.data.wxMaUserInfoExtends));
                  wx.setStorageSync("token", res.data.token);
                  wx.setStorageSync("userInfo", res.data.wxMaUserInfoExtends);
                  wx.setStorageSync("user_id", res.data.wxMaUserInfoExtends.user_id);
                  wx.navigateBack();           
              }
            });//request
            }
      })//getuserinfo
    }
  }
 })//getsetting
  }
}//success
})
}

再貼一下第一次登錄時,控制台打印信息:

3.登錄后台實現

當用戶登錄時,先檢查前台傳來的token在redis中是否存在:存在即更新redis中用戶信息的失效時間,不存在則返回登錄失效信息。

前台獲得失效信息后,正常去調用獲取用戶信息接口,傳入code,encryptedData和iv:

controller層:

    @RequestMapping(value = "/loginAuth", method = RequestMethod.GET, produces = "application/json; charset=utf-8") public String loginAuth(String code,String encryptedData,String iv) throws WxErrorException { if (StringUtils.isBlank(code)) { return "empty jscode"; } //處理登錄授權 獲得openid和sessionkey,生成userid 並將session存到緩存中
        WxMaAuthResult result = weixinMaService.dealLoginAuth(code,encryptedData,iv); return JsonUtils.objectToJson(result); }

service層:

    /** * 處理小程序登錄授權 */ @Override public WxMaAuthResult dealLoginAuth(String code,String data,String iv) { WxMaJscode2SessionResult session =new WxMaJscode2SessionResult(); //調微信官方接口獲得sesssion_key openid存到 session對象里
        try {
       //code換取sessionKey session
= wxMaService.getUserService().getSessionInfo(code); this.logger.info(session.getSessionKey()); this.logger.info(session.getOpenid()); } catch (WxErrorException e) { this.logger.error("獲得sessionKey失敗", e); } WxMaAuthResult wxMaAuthResult = new WxMaAuthResult();
     //該類用來存儲用戶信息 WxMaUserInfoExtends wxMaUserInfoExtends
= new WxMaUserInfoExtends(); WxMaUserInfo wxMaUserInfo = new WxMaUserInfo(); //暫時寫死失效時間 int expire=3600; String sessionKey = session.getSessionKey(); String rawData = StringEscapeUtils.unescapeHtml4(data); // 解密用戶信息 try {
       //sessionkey data iv 解密用戶信息 wxMaUserInfo
= this.wxMaService.getUserService().getUserInfo(sessionKey, data, iv); } catch (Exception e) { // TODO Auto-generated catch block logger.error("解密用戶信息失敗"); e.printStackTrace(); } BeanUtils.copyProperties(wxMaUserInfo, wxMaUserInfoExtends); String third_session = Base64UUID.ramdomID(); wxMaAuthResult.setToken(third_session); String user_id = Base64UUID.ramdomID(); wxMaUserInfoExtends.setUser_id(user_id); //通過openid獲取或新增用戶信息 if(wxMaUserMapper.countAny(session.getOpenid())>0 ){ //存在 數據庫更新 wxMaUserMapper.addWxMaUser(wxMaUserInfoExtends); //這里是將用戶信息存到redis wxMaAuthSessionStorage.addWxMaSession(expire,third_session,wxMaUserInfoExtends); //不把openId傳到前台 wxMaUserInfo.setOpenId(""); wxMaAuthResult.setIsReg(true); wxMaAuthResult.setSuccess(true); wxMaAuthResult.setWxMaUserInfoExtends(wxMaUserInfoExtends); } else { //不存在 數據庫保存信息 wxMaUserMapper.addWxMaUser(wxMaUserInfoExtends); wxMaAuthSessionStorage.addWxMaSession(expire,third_session,wxMaUserInfoExtends); wxMaUserInfo.setOpenId(""); wxMaAuthResult.setSuccess(true); wxMaAuthResult.setIsReg(false); wxMaAuthResult.setWxMaUserInfoExtends(wxMaUserInfoExtends); } return wxMaAuthResult; }

實體類:

public class WxMaUserInfoExtends{
    
    private String user_id;

      private String openId;
      private String nickName;
      private String gender;
      private String language;
      private String city;
      private String province;
      private String country;
      private String avatarUrl;
      private String unionId;
      private Date create_date;
    
    ...
}
public class WxMaAuthResult extends WxMaJscode2SessionResult{

    private String code;

    private String token;
    
    private int expiresIn = -1;
    
    Boolean isReg = false; //是否保存過用戶信息
    
    private boolean success;
    
    private WxMaUserInfoExtends wxMaUserInfoExtends;

    ...
    
}

注:還有一些是api提供的。

redis存取方法:

   @Autowired private JedisClient jedisClient; /** * 存放token和用戶信息 */
    //內部事務回滾不影響外部事務
    @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void addWxMaSession(int expire,String token,WxMaUserInfoExtends wxMaUserInfoExtends) {  String json = JsonUtils.objectToJson(wxMaUserInfoExtends); try { jedisClient.set(token, json); jedisClient.expire(token, expire); } catch (Exception e) { e.printStackTrace(); } }   //從緩存中獲取用戶信息 @Override public WxMaAuthResult getWxMaSessionBy3rdKey(String token) { WxMaAuthResult wxMaAuthResult = new WxMaAuthResult(); try { String json = jedisClient.get(token); if(json == null) { wxMaAuthResult.setSuccess(false); return wxMaAuthResult; } wxMaAuthResult.setSuccess(true); WxMaUserInfoExtends wxMaUserInfoExtends= JsonUtils.jsonToPojo(json, WxMaUserInfoExtends.class); wxMaAuthResult.setWxMaUserInfoExtends(wxMaUserInfoExtends); return wxMaAuthResult; } catch (Exception e) { // TODO Auto-generated catch block
 e.printStackTrace(); wxMaAuthResult.setSuccess(false); return wxMaAuthResult; } }

 

注:這里使用了binarywang(一個github上的開源開發者)提供的API,該API封裝了調用微信小程序官方接口的方法,並提供了一些小程序登錄信息和用戶信息的實體類,非常方便,有需要可以下載下來用:https://github.com/binarywang

后台大體流程:

1.我們用code去換取sessionKey和openId,再通過sessionKey,encryptedData和iv來解密用戶信息(解密過程已經封裝好了)。

2.生成一個隨機字符串,將它作為key(token),用戶信息作為value存入redis緩存並設置或更新失效時間。

3.返回登錄結果和用戶信息到前台。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM