1. 程式人生 > >小程式登入(java後臺)

小程式登入(java後臺)

基本順序

        1.微信小程式的appId 
	2.微信小程式的secret
	3.ip在微信公眾號裡備案 
	4.新增業務域名會訪問專案中的MP_verify_fQ6FF0R8GZHgK5Kl.txt (程式實現這個檔案)
	5.wx.login()獲取微信給的臨時code 5分鐘有效期	
	6.code2ession() 介面,使用code 和appid 和secret 換取使用者的唯一標識openid和會話祕鑰session_key

1.微信小程式的appId(去往小程式)

設定=====>開發設定 ====>appId

2.微信小程式的secret

設定=====>開發設定=====>secret

3.ip在公眾號中備案

    基本設定====>IP白名單 可以設定多個ip 使用回車分隔

 

4.新增業務域名

點選公眾號頭像===>功能設定====>業務域名

設定自己的域名然後下載檔案

這裡使用程式碼替代 

域名設定  你的域名 xxx.com\mini  你的專案必須是啟動 ,微信會去驗證使用get方式

 

/**
 * @date: 2018/11/2 11:33
 * @author: YINLELE
 * @description:用於給微信驗證用的
 */
@Controller
@RequestMapping("/mini")
public class MiniWxController {

    Logger logger = LoggerFactory.getLogger(MiniWxController.class);

    /*用於微信驗證MVP檔案*/
    @RequestMapping("MP_verify_{path}.txt")
    public void index(@PathVariable String path, HttpServletResponse response){
        try {
            PrintWriter writer = response.getWriter();
            writer.print(path);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

 5.wx.login()獲取微信給的臨時code 5分鐘有效期

     1.使用wx.login()獲取微信使用者的code(點選即可)

         這裡面介紹了小程式登入以及加解密的原理和實現 

    2.這是獲取session_key用於解密使用者的加密資訊的(點選即可)

 

小程式程式碼 wxml

<view class="login-container">

  <view class="login" wx:if="{{ !logged }}">

    <view class="app-info">

      <image class="app-logo" src="{{logo}}" />
      <text class="app-name">{{title}}</text>
    </view>

   <view class='form-item'>
      <input class='username' value='{{phone}}' bindinput='getPhone' placeholder='請輸入手機號'>
      </input>
    </view>

    <button class="weui-btn" type="primary" open-type="getUserInfo" bindgetuserinfo="login">確認授權</button>
  </view>

  <view class="logged" wx:else>

    <image class="logged-icon" src="../../images/iconfont-weixin.png" />
    <view class="logged-text">近期你已經授權登陸過{{title}}</view>

    <view class="logged-text">自動登入中</view>
  </view>
</view>

小程式的js 我使用的是請求是封裝好的 我又獲取了使用者的手機號 所以多個屬性 一般解密使用者資訊在後臺 解析

const app = getApp();
var httpUtil = require("../../../utils/httpUtil.js");
var api = require("../../../config/api.js");
Page({
  data: {
    phone: "",
    fail: "網路繁忙"
  },
  onLoad: function() {

  },
  getPhone: function(e) {
    this.setData({
      phone: e.detail.value
    });
    console.log(this.data.phone);
  },
  login: function(e) {
    var that = this;
    wx.login({
      success: function(r) {
        var code = r.code; //登入憑證 
        console.log(code)
        if (code) {
          //2、呼叫獲取使用者資訊介面  
          wx.getUserInfo({
            success: function(res) {
              console.log(JSON.stringify("獲取的使用者資訊:===>" + that.data.phone));
              var data = {
                "code": r.code,//使用者code
                "encryptedData": res.encryptedData,//使用者資訊不過被加密 需要使用session_key解密
                "iv": res.iv,//參與解密使用者資訊的向量
                "phone": that.data.phone//手機號
              };
              //3.請求自己的伺服器,解密使用者資訊 獲取unionId等加密資訊  
              httpUtil.http_post(api.LoginCodeUrl, data).then((res) => {
                console.log("使用者資訊====>" + JSON.stringify(res));
                if (res.status == 80200) {
                  app.globalData.userInfo = res.data;
                  wx.setStorageSync('userInfo', res.data);
                  wx.switchTab({
                    url: '/pages/index/index',
                  })
                } else {
                  wx.showToast({
                    title: that.data.fail,
                    icon: "loading",
                    duration: 2000
                  })
                }
              });
            },
            fail: function() {
              console.log('獲取使用者資訊失敗')
            }
          })

        } else {
          console.log('獲取使用者登入態失敗!' + r.errMsg)
        }
      },
      fail: function() {
        console.log('登陸失敗')
      }
    })
  }
})

 小程式的wxss

.login-container {
  height: 100%;
  padding: 10px 30px;
  background: #fff;
}

.app-info {
  position:relative;
  padding:20px;
  text-align:center;
}

.app-info:after {
    content: " ";
    position: absolute;
    left: 0;
    bottom: 0;
    right: 0;
    height: 1px;
    border-bottom: 1px solid #E5E5E5;
    color: #E5E5E5;
    -webkit-transform-origin: 0 100%;
    transform-origin: 0 100%;
    -webkit-transform: scaleY(0.5);
    transform: scaleY(0.5);
}

.app-info .app-logo {
display: block;
width: 64px;
height: 64px;
margin: 10px auto;
border-radius: 4px;
}

.app-info .app-name {
font-weight: bold;
font-size: 18px;
color: #000;
}

.alert {
margin: 20px 0 30px;
}

.alert .alert-title {
  font-weight: 400;
  font-size: 16px;
  color: #000;
  margin-bottom: 10px;
}

.alert-desc {
  display: block;
  list-style: disc inside none;
}

.alert .alert-text {
  display: list-item;
  text-align: -webkit-match-parent;
  font-size: 14px;
  color: #999;
}

.logged {
  margin-top: 100px;
}

.logged .logged-icon {
  display: block;
  width: 64px;
  height: 64px;
  margin: 20px auto;
}

.logged .logged-text {  
  font-size: 14px;
  color: #000;
  text-align: center;
  margin: 10px 0;
}

.form-item{
    position: relative;
    background: #fff;
    height: 96rpx;
    border-bottom: 1px solid #d9d9d9;
}

.form-item .username, .form-item .password, .form-item .code{
    position: absolute;
    top: 26rpx;
    left: 0;
    display: block;
    width: 100%;
    height: 44rpx;
    background: #fff;
    color: #333;
    font-size: 30rpx;
}

6.後臺獲取小程式傳過來的值進行解密

import java.util.Map;

/**
 * @date: 2018/10/26 15:51
 * @author: YINLELE
 * @description: 用於小程式登入
 */
@RestController
@RequestMapping("/mini/login")
public class MiniLoginController extends BaseController {
    Logger logger =LoggerFactory.getLogger(MiniLoginController.class);

    @Autowired
    private UserService userService;

    @ResponseBody
    @PostMapping("/wxUserInfo")
    public ResponseBean wxLoginToken(@RequestBody WxEncryption wxEncryption) throws Exception {
        logger.info("wxUserInfo={}",wxEncryption);
        //準備url,獲取使用者的session_key和openId資訊
        String urlSM=WxSPConfig.CODE2_SESSION_URL.replace("APPID",WxSPConfig.APP_ID_SMALL_PROGRAM).replace("SECRET",WxSPConfig.SECRET_SMALL_PROGRAM).replace("JSCODE",wxEncryption.getCode());
        logger.info("urlSM={}",urlSM);
        //請求微信介面獲取資訊
        RestTemplate template = new RestTemplate();
        ResponseEntity<String> responseSM = template.getForEntity(urlSM, String.class);
        logger.info("responseSM={}",responseSM);
        //轉換成map集合
        String bodySM = responseSM.getBody();
        WxUserInfoVO wxUserInfoVO = FastJsonUtil.getObject(bodySM, WxUserInfoVO.class);
        //獲取解密後的資料
        String jsonData = AesCbcUtil.decrypt(wxEncryption.getEncryptedData(), wxUserInfoVO.getSession_key(),wxEncryption.getIv(), "UTF-8");
        logger.info("wxUserInfoVO={}",wxUserInfoVO);
        WxUserInfoVO userInfoVO = FastJsonUtil.getObject(jsonData, WxUserInfoVO.class);
        userInfoVO.setSession_key(wxUserInfoVO.getSession_key());
        userInfoVO.setWxUserInfo(jsonData);
        //根據openid查詢
        UserEntity userEntity=userService.findByOpenId(userInfoVO.getOpenId());
        if(null==userEntity){
            userInfoVO = userService.saveUserByWx(userInfoVO, wxEncryption);
        }else{
            userInfoVO = userService.updateUserByWx(userInfoVO, wxEncryption);
        }
        userInfoVO.setWxUserInfo(null);
        userInfoVO.setSession_key(null);
        return ResponseUtil.success(userInfoVO);
    }

}

使用的實體類

package com.aui.stock.controller.mini.vo;

/**
 * @date: 2018/11/6 23:46
 * @author: YINLELE
 * @description: 使用者儲存使用者資訊
 */
public class WxUserInfoVO {

    /*微信使用者在系統的的唯一ID*/
    private Long sn;

    /*使用者唯一的標識*/
    private String openId;

    /*微信使用者的暱稱*/
    private String nickName;

    /*微信使用者的性別*/
    private String  gender;

    /*微信使用者的城市*/
    private String city;

    /*微信使用者的省*/
    private String province;

    /*微信使用者的國家*/
    private String country;

    /*微信使用者的頭像*/
    private String avatarUrl;

    /*使用者手機號*/
    private String phone;

    /*使用者的語言*/
    private String language;

    /*使用者祕鑰*/
    private String session_key;

    /*使用者資訊json*/
    private String wxUserInfo;

    public String getWxUserInfo() {
        return wxUserInfo;
    }

    public void setWxUserInfo(String wxUserInfo) {
        this.wxUserInfo = wxUserInfo;
    }

    public String getOpenId() {
        return openId;
    }

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

    public Long getSn() {
        return sn;
    }

    public void setSn(Long sn) {
        this.sn = sn;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getAvatarUrl() {
        return avatarUrl;
    }

    public void setAvatarUrl(String avatarUrl) {
        this.avatarUrl = avatarUrl;
    }

    public String getPhone() {
        return phone;
    }

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

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getSession_key() {
        return session_key;
    }

    public void setSession_key(String session_key) {
        this.session_key = session_key;
    }
    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("WxUserInfoVO{");
        sb.append("sn=").append(sn);
        sb.append(", nickName='").append(nickName).append('\'');
        sb.append(", gender='").append(gender).append('\'');
        sb.append(", city='").append(city).append('\'');
        sb.append(", province='").append(province).append('\'');
        sb.append(", country='").append(country).append('\'');
        sb.append(", avatarUrl='").append(avatarUrl).append('\'');
        sb.append(", phone='").append(phone).append('\'');
        sb.append(", language='").append(language).append('\'');
        sb.append(", session_key='").append(session_key).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

 

package com.aui.stock.controller.mini.to;

/**
 * @date: 2018/11/6 22:27
 * @author: YINLELE
 * @description: 用於接受微信登入的引數
 */
public class WxEncryption {

    /*使用者登入的code 有效期5分鐘*/
    private String code;

    /*使用者的加密資訊*/
    private String encryptedData;

    /*加密演算法的初始向量*/
    private String iv;

    /*使用者手機號*/
    private String phone;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getEncryptedData() {
        return encryptedData;
    }

    public void setEncryptedData(String encryptedData) {
        this.encryptedData = encryptedData;
    }

    public String getIv() {
        return iv;
    }

    public void setIv(String iv) {
        this.iv = iv;
    }

    public String getPhone() {
        return phone;
    }

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

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("WxEncryption{");
        sb.append("code='").append(code).append('\'');
        sb.append(", encryptedData='").append(encryptedData).append('\'');
        sb.append(", iv='").append(iv).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

 解密的工具類

package com.aui.stock.util;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;

/**
 * @date: 2018/11/6 22:49
 * @author: YINLELE
 * @description:
 *   AES-128-CBC 加密方式
 *   注:
 *   AES-128-CBC可以自己定義“金鑰”和“偏移量“。
 *   AES-128是jdk自動生成的“金鑰”。
 */
public class AesCbcUtil {

    static {
        //BouncyCastle是一個開源的加解密解決方案,主頁在http://www.bouncycastle.org/
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * AES解密
     *
     * @param data           //密文,被加密的資料
     * @param key            //祕鑰
     * @param iv             //偏移量
     * @param encodingFormat //解密後的結果需要進行的編碼
     * @return
     * @throws Exception
     */
    public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {

        //被加密的資料
        byte[] dataByte = Base64.decodeBase64(data);
        //加密祕鑰
        byte[] keyByte = Base64.decodeBase64(key);
        //偏移量
        byte[] ivByte = Base64.decodeBase64(iv);


        try {
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, encodingFormat);
                return result;
            }
            return null;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidParameterSpecException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

}

解密所需要的maven座標

     <!--用於小程式解密-->
        <dependency>
            <groupId>org.codehaus.xfire</groupId>
            <artifactId>xfire-core</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.60</version>
        </dependency>
        <!--小程式解密-->