风尘訫 發表於 2020-12-31 17:20:00

java开发小程序登录功能

<p>整体流程如下:</p>
<p><strong>- 1.获取code</strong></p>
<p><strong>- 2.用步骤1获取到的临时code换取用户唯一标识OpenId和会话密钥 session_key</strong></p>
<p><strong>- 3.获取用户手机号,进行登录</strong></p>
<p>请求地址:GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&amp;secret=SECRET&amp;js_code=JSCODE&amp;grant_type=authorization_code</p>
<p>请求参数:</p>
<p>请求参数:</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>类型</th>
<th>默认值</th>
<th>必填</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>appid</td>
<td>string</td>
<td></td>
<td>是</td>
<td>小程序 appId</td>
</tr>
<tr>
<td>secret</td>
<td>string</td>
<td></td>
<td>是</td>
<td>小程序 appSecret</td>
</tr>
<tr>
<td>js_code</td>
<td>string</td>
<td></td>
<td>是</td>
<td>登录时获取的 code</td>
</tr>
<tr>
<td>grant_type</td>
<td>string</td>
<td></td>
<td>是</td>
<td>授权类型,此处只需填写 authorization_code</td>
</tr>
</tbody>
</table>
<p>返回值:</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>openid</td>
<td>string</td>
<td>用户唯一标识</td>
</tr>
<tr>
<td>session_key</td>
<td>string</td>
<td>会话密钥</td>
</tr>
<tr>
<td>unionid</td>
<td>string</td>
<td>用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。</td>
</tr>
<tr>
<td>errcode</td>
<td>number</td>
<td>错误码</td>
</tr>
<tr>
<td>errmsg</td>
<td>string</td>
<td>错误信息</td>
</tr>
</tbody>
</table>
<p><strong>开发前准备(必须)</strong></p>
<p>appid:wx4c1d53***<em>*</em>7a671</p>
<p>appsecret:fc9f99c08dcb2a0***<em>*</em>eb26376c65eae</p>
<p>小程序代码:</p>
<p><strong>login.js</strong></p>
<pre><code class="language-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 =&gt; {
          if(res.code){
            console.log("code-&gt;", res.code)
          }
      }
      })
    },

      // 0.获取手机号授权
getPhoneNumber: function(e) {
    // 用户拒绝授权
    if(e.detail.errMsg == "getPhoneNumber:fail user deny") {
      wx.showToast({
      icon: "none",
      title: '请允许获取手机号,否则功能不可用!',
      })
      return
    }
//console.log("e-&gt;detail", e.detail)
    /// 用户允许授权
    // console.log("iv-&gt;", e.detail.iv); //包括敏感数据在内的完整用户信息的加密数据,需要解密
    // console.log("encryptedData-&gt;", e.detail.encryptedData); //加密算法的初始向量,解密需要用到

    /// 获取手机号
    // 1.获取临时登录凭证code
    wx.login({
      success: res =&gt; {
      if(res.code){
          this.code = res.code;
          console.log("code-&gt;", res.code)
          console.log("encryptedData-&gt;", encodeURIComponent(e.detail.encryptedData))
          console.log("iv-&gt;", 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 () {

}
})
</code></pre>
<p><strong>login.json</strong></p>
<pre><code class="language-json">{
"usingComponents": {}
}
</code></pre>
<p><strong>login.wxml</strong></p>
<pre><code class="language-html">&lt;!--pages/login.wxml--&gt;
&lt;view class="modal-mask" catchtouchmove="preventTouchMove" wx:if="{{isShow}}"&gt;&lt;/view&gt;
&lt;view class="modal-dialog" wx:if="{{isShow}}"&gt;
&lt;view class="modal-content"&gt;
    &lt;view&gt;&lt;image src='../images/show.png' class='show'&gt;&lt;/image&gt;&lt;/view&gt;
    &lt;!-- &lt;view&gt;绑定手机号&lt;/view&gt;
    &lt;view&gt;请先绑定手机号在进行此操作&lt;/view&gt;   --&gt;
    &lt;button class="show" type="primary" lang="zh_CN"
    open-type='getPhoneNumber' bindgetphonenumber="getPhoneNumber" &gt;
      微信用户一键登录
    &lt;/button&gt;
&lt;/view&gt;
&lt;/view&gt;
</code></pre>
<p><strong>login.wxss</strong></p>
<pre><code class="language-css">/* 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%;
}
</code></pre>
<p><strong>后端代码</strong></p>
<p><strong>WxLoginController.java</strong></p>
<pre><code class="language-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&lt;String, Object&gt; userMap = new HashMap&lt;&gt;();
   
    @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;
    }
}   
</code></pre>
<p><strong>UserDao.java</strong></p>
<pre><code class="language-java">public interface UserDao extends JpaRepository&lt;User, Long&gt; {

    User findByPhone(String phone);

    User findByOpenId(String openId);
}
</code></pre>
<p><strong>User.java</strong></p>
<pre><code class="language-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;
    }
}

</code></pre>
<p><strong>WechatDecryptDataUtil.java</strong></p>
<pre><code class="language-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;
            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;
    }

}
</code></pre>
<p><strong>WxContants.java</strong></p>
<pre><code class="language-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&amp;secret=SECRET&amp;js_code=JSCODE&amp;grant_type=authorization_code";
}
</code></pre><br><br>
来源:https://www.cnblogs.com/jerry0612/p/14217999.html
頁: [1]
查看完整版本: java开发小程序登录功能