java開發小程序登錄功能


整體流程如下:

- 1.獲取code

- 2.用步驟1獲取到的臨時code換取用戶唯一標識OpenId和會話密鑰 session_key

- 3.獲取用戶手機號,進行登錄

請求地址:GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

請求參數:

請求參數:

屬性 類型 默認值 必填 說明
appid string 小程序 appId
secret string 小程序 appSecret
js_code string 登錄時獲取的 code
grant_type string 授權類型,此處只需填寫 authorization_code

返回值:

屬性 類型 說明
openid string 用戶唯一標識
session_key string 會話密鑰
unionid string 用戶在開放平台的唯一標識符,在滿足 UnionID 下發條件的情況下會返回,詳見 UnionID 機制說明
errcode number 錯誤碼
errmsg string 錯誤信息

開發前准備(必須)

appid:wx4c1d53****7a671

appsecret:fc9f99c08dcb2a0****eb26376c65eae

小程序代碼:

login.js

// pages/login/login.wxml.js
const app = getApp();
Page({

  /**
   * 頁面的初始數據
   */
  data: {
      // 判斷小程序的API,回調,參數,組件等是否在當前版本可用。
      canIUse: wx.canIUse('button.open-type.getPhoneNumber'),
      wechat: '微信快捷登錄',
      isShow: false
  },

  /**
   * 生命周期函數--監聽頁面加載
   */
  onLoad: function (options) {
      // 從緩存中取手機號
      console.log("獲取手機號!")
      try {
        var value = wx.getStorageSync('phoneNumber')
        if (value) { // 說明已登錄 跳轉 頁面
          console.log("獲取緩存:"+value)
          wx.navigateTo({
            url: '../login?param=' + value
          })
        }else{// 未登錄 顯示 微信授權頁面
          this.setData({
            isShow: true
          })
        }
      } catch (e) {

      }
      // 解決第一次獲取手機號失敗問題
      wx.login({
        success: res => {
          if(res.code){
            console.log("code->", res.code)
          }
        }
      })
    },

      // 0.獲取手機號授權
  getPhoneNumber: function(e) {
    // 用戶拒絕授權
    if(e.detail.errMsg == "getPhoneNumber:fail user deny") {
      wx.showToast({
        icon: "none",
        title: '請允許獲取手機號,否則功能不可用!',
      })
      return
    }
  //  console.log("e->detail", e.detail)
    /// 用戶允許授權
    // console.log("iv->", e.detail.iv); //包括敏感數據在內的完整用戶信息的加密數據,需要解密
    // console.log("encryptedData->", e.detail.encryptedData); //加密算法的初始向量,解密需要用到

    /// 獲取手機號
    // 1.獲取臨時登錄憑證code
    wx.login({
      success: res => {
        if(res.code){
          this.code = res.code;
          console.log("code->", res.code)
          console.log("encryptedData->", encodeURIComponent(e.detail.encryptedData))
          console.log("iv->", e.detail.iv)
          this.getSessionKey(res.code, e.detail.encryptedData, e.detail.iv);
        }
      }
    })
    },
  // 2.訪問登錄憑證校驗接口獲取session_key(后續改成后台實現)
  getSessionKey: function(js_code, encryptedData, iv) {
    // 3. 解密獲取手機號
    wx.request({
      url: 'http://localhost:8082/wechat/appletLogin',
      data: {
          'encryptedData': encodeURIComponent(encryptedData),//需要進行編碼
          'iv': iv,
          'jscode': js_code
      },
      method: 'POST', 
      header: {
          'content-type': 'application/json'
      }, // 設置請求的 header
      success: function(data2) {
          var data = data2.data
          console.log(data)
          if(data.resultCode == 200) { 
            if(data.data.user.phone==undefined){
              // 獲取手機號失敗 跳轉到 常規 用戶登錄頁面(通過webview)
              wx.navigateTo({
                url: '用戶登錄頁'
              })
              return
            }
            // 存儲數據到緩存
            wx.setStorage({
              key:"phoneNumber",
              data:data.data.user.phone
            })
            // 4.跳轉頁面
          }
      },
      fail: function(err) {
          console.log(err);
          wx.showToast({
            icon: "none",
            title: '獲取手機號失敗,請重試!',
          })
      }
    })
  },
  

  /**
   * 生命周期函數--監聽頁面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函數--監聽頁面顯示
   */
  onShow: function () {

  },

  /**
   * 生命周期函數--監聽頁面隱藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函數--監聽頁面卸載
   */
  onUnload: function () {

  },

  /**
   * 頁面相關事件處理函數--監聽用戶下拉動作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 頁面上拉觸底事件的處理函數
   */
  onReachBottom: function () {

  },

  /**
   * 用戶點擊右上角分享
   */
  onShareAppMessage: function () {

  }
})

login.json

{
  "usingComponents": {}
}

login.wxml

<!--pages/login.wxml-->
<view class="modal-mask" catchtouchmove="preventTouchMove" wx:if="{{isShow}}"></view>
  <view class="modal-dialog" wx:if="{{isShow}}">
  <view class="modal-content">
    <view><image src='../images/show.png' class='show'></image></view>
    <!-- <view>綁定手機號</view>
    <view>請先綁定手機號在進行此操作</view>   -->
    <button class="show" type="primary" lang="zh_CN"
    open-type='getPhoneNumber' bindgetphonenumber="getPhoneNumber" >
      微信用戶一鍵登錄
    </button>
  </view>
</view>

login.wxss

/* pages/login/login.wxml.wxss */
.show{
  display: block;
  border-radius: 8rpx;
  margin: 20rpx 20rpx 20rpx 20rpx;
  font-size: 35rpx;
}

.container{
  position: fixed;  /*關鍵屬性,設置為fixed*/
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

后端代碼

WxLoginController.java

@RestController
@RequestMapping("/wechat")
public class WxLoginController {

    private Logger logger = LoggerFactory.getLogger(WxLoginController.class);
    
    @Autowired
    UserDao userDao;
    
    @Value("${wechat.appid}")
    private String appid;
    @Value("${wechat.appsecret}")
    private String appsecret;
    
    private Map<String, Object> userMap = new HashMap<>();
    
    @GetMapping("/appletLogin")
    public ResultBean appletLogin(String encryptedData, String iv, String jscode) {
        System.out.println(encryptedData);
        ResultBean resultBean = new ResultBean();
        // jscode
        if (StringUtils.isEmpty(jscode)) {
            resultBean.setResultCode(-10001);
            resultBean.setResultMessage("鑒權失敗!");
            return resultBean;
        }
        // sign
        if (StringUtils.isEmpty(encryptedData)) {
            resultBean.setResultCode(-10001);
            resultBean.setResultMessage("鑒權失敗!");
            return resultBean;
        }
        if(StringUtils.isBlank(iv)) {
            resultBean.setResultCode(-10002);
            resultBean.setResultMessage("鑒權失敗!");
            return resultBean;
        }

        // 創建Httpclient對象
        CloseableHttpClient httpclient = HttpClients.createDefault();
        String resultString = "";
        CloseableHttpResponse response = null;
        String url =WxContants.url.replace(WxContants.APPID, appid).
                replace(WxContants.SECRET, appsecret).replace(WxContants.JSCODE, jscode);

        try {
            // 創建uri
            URIBuilder builder = new URIBuilder(url);
            URI uri = builder.build();

            // 創建http GET請求
            HttpGet httpGet = new HttpGet(uri);

            // 執行請求
            response = httpclient.execute(httpGet);
            // 判斷返回狀態是否為200
            if (response.getStatusLine().getStatusCode() == 200) {
                resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 解析json
        JSONObject jsonObject = (JSONObject) JSONObject.parse(resultString);
        String session_key = jsonObject.getString("session_key");
        String openid = jsonObject.getString("openid");

        System.out.println("session_key:"+session_key + ",openid:"+openid);
        if (StringUtils.isEmpty(session_key)) {
            resultBean.setResultCode(-10003);
            resultBean.setResultMessage("獲取sessionkey失敗!");
            return resultBean;
        }

        // 解碼
        try {
            encryptedData = URLDecoder.decode(encryptedData,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            logger.error("encryptedData,decode失敗!", e);
            resultBean.setResultCode(-10003);
            resultBean.setResultMessage("encryptedData,decode失敗!");
            return resultBean;
        }
        String decryptData = WechatDecryptDataUtil.decryptData(encryptedData, session_key, iv);
        JSONObject userInfo = JSONObject.parseObject(decryptData);
        // 存入redis+數據庫,判斷是否存在redis中,存在直接從redis返回
        // 本地使用Map模擬
        if (!userMap.containsKey(openid)) {
            // 判斷用戶是否存在系統中
            String phoneNumber = userInfo.getString("phoneNumber");
            User user = userDao.findByPhone(phoneNumber);
            if (user == null) {
                resultBean.setResultCode(200);
                resultBean.setResultMessage("此用戶不存在,請先注冊!");
                return resultBean;
            }
            user.setOpenId(openid);
            userDao.save(user);
            userMap.put(openid, user);
        }
        User user = (User) userMap.get(openid);
        resultBean.setResultCode(200);
        resultBean.setResultMessage("成功!");
        resultBean.setData(user);
        return resultBean;
    }
}    

UserDao.java

public interface UserDao extends JpaRepository<User, Long> {

    User findByPhone(String phone);

    User findByOpenId(String openId);
}

User.java

@Entity
@Table(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String openId;
    private String phone;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOpenId() {
        return openId;
    }

    public void setOpenId(String openId) {
        this.openId = openId;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

WechatDecryptDataUtil.java

public class WechatDecryptDataUtil {
	private static Logger logger = LogManager.getLogger(WechatDecryptDataUtil.class);

    public static void main(String[] args) {
        String result = decryptData(
                "BLY05rL1qLzGdqn+m4s4KfnM9CbMCm2sTZmgIcUJUpoOVeZkKKYh06ATVm2BgX8HUsH1a93811fhwr70MTh2Pk2qw9rPBzvos3husUJdVfaZgBk3Afp6wNFS0/kcoyt+7q2eMrIHMe6wkc4J0hgbbkxdUwZag/pyKXwe4pUzSQfta7dfBHy3DLu+REvwHiDNfvI3KJbZ0l8/9vRjfSmT4Q==",
                "C9Z59YjFkCDT8+9NQ3OxcA==",
                "oOWTpbZ80evG/GQqRdUu3w=="
        );
        System.out.println("result = " + result);
    }

    public synchronized static String decryptData(String encryptDataB64, String sessionKeyB64, String ivB64) {
    	String res = null;
    	try {
    		res = new String(
                    decryptOfDiyIV(
                            Base64.decode(encryptDataB64),
                            Base64.decode(sessionKeyB64),
                            Base64.decode(ivB64)
                    )
            );
		} catch (Exception e) {
			logger.error("encryptDataB64:"+encryptDataB64+"\n"+"sessionKeyB64:"+sessionKeyB64+"\n"+"ivB64:"+ivB64);
		}

        return res;
    }

    private static final String KEY_ALGORITHM = "AES";
    private static final String ALGORITHM_STR = "AES/CBC/PKCS7Padding";
    private static Key key;
    private static Cipher cipher;

    private static void init(byte[] keyBytes) {
        // 如果密鑰不足16位,那么就補足.  這個if 中的內容很重要
        int base = 16;
        if (keyBytes.length % base != 0) {
            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
            keyBytes = temp;
        }
        // 初始化
        Security.addProvider(new BouncyCastleProvider());
        // 轉化成JAVA的密鑰格式
        key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
        try {
            // 初始化cipher
            cipher = Cipher.getInstance(ALGORITHM_STR, "BC");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解密方法
     *
     * @param encryptedData 要解密的字符串
     * @param keyBytes      解密密鑰
     * @param ivs           自定義對稱解密算法初始向量 iv
     * @return 解密后的字節數組
     */
    private static byte[] decryptOfDiyIV(byte[] encryptedData, byte[] keyBytes, byte[] ivs) {
        byte[] encryptedText = null;
        init(keyBytes);
        try {
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivs));
            encryptedText = cipher.doFinal(encryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encryptedText;
    }

}

WxContants.java

public interface WxContants {

    String APPID = "APPID";
    String SECRET = "SECRET";
    String JSCODE = "JSCODE";
    String TRADE_TYPE = "JSAPI";
    String url = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
}


免責聲明!

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



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