|
@@ -8,20 +8,23 @@ import beilv.common.utils.rsa.RsaUtil;
|
|
|
import beilv.system.domain.SysMember;
|
|
|
import beilv.system.mapper.SysMemberMapper;
|
|
|
import cn.binarywang.wx.miniapp.api.WxMaService;
|
|
|
-import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
|
|
-import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import me.chanjar.weixin.common.error.WxErrorException;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
-
|
|
|
-import static beilv.common.enums.ErrorCodeConstants.AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR;
|
|
|
-import static beilv.common.enums.ErrorCodeConstants.MINI_AUTH_LOGIN_BAD;
|
|
|
-import static beilv.common.servlet.ServletUtils.getClientIP;
|
|
|
-import static beilv.common.utils.ExceptionUtil.exception;
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import javax.crypto.spec.IvParameterSpec;
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
+import java.io.*;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.net.HttpURLConnection;
|
|
|
+import java.net.URL;
|
|
|
+import java.net.URLConnection;
|
|
|
+import java.util.*;
|
|
|
|
|
|
|
|
|
/**
|
|
@@ -39,48 +42,242 @@ public class AppAuthServiceImpl implements IAppAuthService {
|
|
|
private SysMemberMapper sysMemberMapper;
|
|
|
@Value("${rsa.publicKey}")
|
|
|
private String publicKey;
|
|
|
-
|
|
|
+ @Value("${wx.pay.appId}")
|
|
|
+ private String appId;
|
|
|
+ @Value("${wx.pay.appSecret}")
|
|
|
+ private String appSecret;
|
|
|
|
|
|
@Override
|
|
|
public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) throws Exception {
|
|
|
- // 获得对应的手机号信息
|
|
|
- WxMaPhoneNumberInfo phoneNumberInfo;
|
|
|
- try {
|
|
|
- phoneNumberInfo = wxMaService.getUserService().getNewPhoneNoInfo(reqVO.getPhoneCode());
|
|
|
- } catch (Exception exception) {
|
|
|
- log.error(exception.getMessage());
|
|
|
- throw exception(AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
|
|
|
- }
|
|
|
- WxMaJscode2SessionResult session;
|
|
|
- try {
|
|
|
- session = wxMaService.getUserService().getSessionInfo(reqVO.getLoginCode());
|
|
|
- } catch (WxErrorException e) {
|
|
|
- log.error(e.getMessage());
|
|
|
- throw exception(MINI_AUTH_LOGIN_BAD);
|
|
|
- }
|
|
|
+ JSONObject responseMap = getSessionKeyOropenid(reqVO.getLoginCode());
|
|
|
+ String openId = (String) responseMap.get("openid");
|
|
|
+ String sessionKey = (String) responseMap.get("session_key");
|
|
|
+ //获取用户信息
|
|
|
+ SysMember sysMember = getUserInfo(sessionKey, reqVO.getEncryptedData(), reqVO.getIv(), reqVO.getTelephoneCode());
|
|
|
// 用户已经存在
|
|
|
- SysMember memberUserDO = sysMemberMapper.selectByMobile(phoneNumberInfo.getPhoneNumber());
|
|
|
+ SysMember memberUserDO = sysMemberMapper.selectByMobile(sysMember.getMobile());
|
|
|
if (memberUserDO == null) {
|
|
|
// 获得获得注册用户
|
|
|
- memberUserDO = sysMemberMapper.createUserIfAbsent(phoneNumberInfo.getPhoneNumber(), getClientIP(),
|
|
|
- LoginTypeEnum.WXAPP.getValue());
|
|
|
- memberUserDO.setRealName("用户_" + memberUserDO.getId());
|
|
|
-
|
|
|
+ SysMember userIfAbsent = sysMemberMapper.createUserIfAbsent(sysMember);
|
|
|
}
|
|
|
- // 使用 sessionKey 和 openid 进行用户登录
|
|
|
- String openId = session.getOpenid();
|
|
|
- String sessionKey = session.getSessionKey();
|
|
|
-
|
|
|
- memberUserDO.setOpenId(session.getOpenid());
|
|
|
- sysMemberMapper.updateSysMember(memberUserDO);
|
|
|
-
|
|
|
//加密
|
|
|
String accessToken = RsaUtil.encrypt(memberUserDO.getId().toString(), publicKey);
|
|
|
-
|
|
|
AppAuthLoginRespVO userInfo = new AppAuthLoginRespVO();
|
|
|
userInfo.setAccessToken(accessToken);
|
|
|
userInfo.setOpenId(openId);
|
|
|
+ userInfo.setSessionKey(sessionKey);
|
|
|
return userInfo;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取用户信息
|
|
|
+ *
|
|
|
+ * @param sessionKey sessionKey
|
|
|
+ * @param encryptedData encryptedData
|
|
|
+ * @param iv iv
|
|
|
+ * @param telephoneCode telephoneCode
|
|
|
+ * @return 手机号
|
|
|
+ */
|
|
|
+ public SysMember getUserInfo(String sessionKey, String encryptedData, String iv, String telephoneCode) {
|
|
|
+ SysMember sysMember = new SysMember();
|
|
|
+ Date date = new Date();
|
|
|
+ try {
|
|
|
+ // 1. 解密用户信息
|
|
|
+ byte[] result = decrypt(encryptedData, sessionKey, iv);
|
|
|
+ JSONObject json = JSONObject.parseObject(new String(result));
|
|
|
+ // 2. 处理解密后的手机号信息
|
|
|
+ String userPhone = getUserPhone(telephoneCode);
|
|
|
+ sysMember.setMobile(userPhone);
|
|
|
+ sysMember.setOpenId(json.get("openId").toString());
|
|
|
+ sysMember.setUsername(json.get("nickName").toString() + "_" + userPhone);
|
|
|
+ sysMember.setIntegral(BigDecimal.ZERO);
|
|
|
+ sysMember.setCreateTime(date);
|
|
|
+ return sysMember;
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ return null; // 处理异常
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户手机号
|
|
|
+ *
|
|
|
+ * @param telephoneCode telephoneCode
|
|
|
+ * @return 手机号
|
|
|
+ */
|
|
|
+ public String getUserPhone(String telephoneCode) {
|
|
|
+ String accessToken = getAccessToken();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 构建 URL
|
|
|
+ String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
|
|
|
+ URL obj = new URL(url);
|
|
|
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
|
|
+ // 设置请求方法为 POST
|
|
|
+ con.setRequestMethod("POST");
|
|
|
+ con.setRequestProperty("Content-Type", "application/json");
|
|
|
+ // 发送 POST 请求
|
|
|
+ con.setDoOutput(true);
|
|
|
+ String jsonInputString = "{\"code\":\"" + telephoneCode + "\"}";
|
|
|
+ try (DataOutputStream wr = new DataOutputStream(con.getOutputStream())) {
|
|
|
+ wr.writeBytes(jsonInputString);
|
|
|
+ wr.flush();
|
|
|
+ }
|
|
|
+ // 获取响应
|
|
|
+ int responseCode = con.getResponseCode();
|
|
|
+ BufferedReader in;
|
|
|
+ if (responseCode == HttpURLConnection.HTTP_OK) {
|
|
|
+ in = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
|
|
+ } else {
|
|
|
+ in = new BufferedReader(new InputStreamReader(con.getErrorStream()));
|
|
|
+ }
|
|
|
+ String inputLine;
|
|
|
+ StringBuilder response = new StringBuilder();
|
|
|
+ while ((inputLine = in.readLine()) != null) {
|
|
|
+ response.append(inputLine);
|
|
|
+ }
|
|
|
+ in.close();
|
|
|
+ JSONObject json = JSONObject.parseObject(response.toString());
|
|
|
+ if (json.get("errcode").equals(0)) {
|
|
|
+ JSONObject phoneObject = JSONObject.parseObject(json.get("phone_info").toString());
|
|
|
+ return phoneObject.get("phoneNumber").toString();
|
|
|
+ } else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ return null; // 处理异常
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取微信小程序 session_key 和 openid
|
|
|
+ * 用户端
|
|
|
+ *
|
|
|
+ * @param code 调用微信登陆返回的Code
|
|
|
+ * @return
|
|
|
+ * @author zhuping
|
|
|
+ */
|
|
|
+ public JSONObject getSessionKeyOropenid(String code) {
|
|
|
+ //微信端登录code值
|
|
|
+ String wxCode = code;
|
|
|
+ String requestUrl = "https://api.weixin.qq.com/sns/jscode2session"; //请求地址
|
|
|
+ Map<String, String> requestUrlParam = new HashMap<String, String>();
|
|
|
+ requestUrlParam.put("appid", appId); //开发者设置中的appId
|
|
|
+ requestUrlParam.put("secret", appSecret); //开发者设置中的appSecret
|
|
|
+ requestUrlParam.put("js_code", wxCode); //小程序调用wx.login返回的code
|
|
|
+ requestUrlParam.put("grant_type", "authorization_code"); //默认参数
|
|
|
+ //发送post请求读取调用微信 https://api.weixin.qq.com/sns/jscode2session 接口获取openid用户唯一标识
|
|
|
+ JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl, requestUrlParam, null));
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取微信小程序access_token
|
|
|
+ * 用户端
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ * @author zhuping
|
|
|
+ */
|
|
|
+ public String getAccessToken() {
|
|
|
+ //微信端登录code值
|
|
|
+ String requestUrl = "https://api.weixin.qq.com/cgi-bin/token"; //请求地址
|
|
|
+ Map<String, String> requestUrlParam = new HashMap<String, String>();
|
|
|
+ requestUrlParam.put("appid", appId); //开发者设置中的appId
|
|
|
+ requestUrlParam.put("secret", appSecret); //开发者设置中的appSecret
|
|
|
+ requestUrlParam.put("grant_type", "client_credential"); //小程序调用wx.login返回的code
|
|
|
+ //发送post请求读取调用微信 https://api.weixin.qq.com/sns/jscode2session 接口获取openid用户唯一标识
|
|
|
+ JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl, requestUrlParam, null));
|
|
|
+ System.out.println("----------:" + jsonObject.getString("access_token"));
|
|
|
+ return jsonObject.getString("access_token");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 向指定 URL 发送POST方法的请求
|
|
|
+ *
|
|
|
+ * @param url 发送请求的 URL
|
|
|
+ * @param
|
|
|
+ * @return 所代表远程资源的响应结果
|
|
|
+ */
|
|
|
+ public static String sendPost(String url, Map<String, ?> paramMap, String headerToken) {
|
|
|
+ PrintWriter out = null;
|
|
|
+ BufferedReader in = null;
|
|
|
+ String result = "";
|
|
|
+ String param = "";
|
|
|
+ Iterator<String> it = paramMap.keySet().iterator();
|
|
|
+ while (it.hasNext()) {
|
|
|
+ String key = it.next();
|
|
|
+ param += key + "=" + paramMap.get(key) + "&";
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ URL realUrl = new URL(url);
|
|
|
+ // 打开和URL之间的连接
|
|
|
+ URLConnection conn = realUrl.openConnection();
|
|
|
+ // 设置通用的请求属性
|
|
|
+ conn.setRequestProperty("accept", "*/*");
|
|
|
+ conn.setRequestProperty("connection", "Keep-Alive");
|
|
|
+ conn.setRequestProperty("Accept-Charset", "utf-8");
|
|
|
+ conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
|
|
+ if (headerToken != null) {
|
|
|
+ conn.setRequestProperty("access_token", headerToken);
|
|
|
+ }
|
|
|
+ // 发送POST请求必须设置如下两行
|
|
|
+ conn.setDoOutput(true);
|
|
|
+ conn.setDoInput(true);
|
|
|
+ // 获取URLConnection对象对应的输出流
|
|
|
+ out = new PrintWriter(conn.getOutputStream());
|
|
|
+ // 发送请求参数
|
|
|
+ out.print(param);
|
|
|
+ // flush输出流的缓冲
|
|
|
+ out.flush();
|
|
|
+ // 定义BufferedReader输入流来读取URL的响应
|
|
|
+ in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
|
|
|
+ String line;
|
|
|
+ while ((line = in.readLine()) != null) {
|
|
|
+ result += line;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ //log.error(e.getMessage(), e);
|
|
|
+ }
|
|
|
+ //使用finally块来关闭输出流、输入流
|
|
|
+ finally {
|
|
|
+ try {
|
|
|
+ if (out != null) {
|
|
|
+ out.close();
|
|
|
+ }
|
|
|
+ if (in != null) {
|
|
|
+ in.close();
|
|
|
+ }
|
|
|
+ } catch (IOException ex) {
|
|
|
+ ex.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解密用户信息
|
|
|
+ */
|
|
|
+ private byte[] decrypt(String encryptedData, String sessionKey, String iv) throws Exception {
|
|
|
+ // Base64解码
|
|
|
+ byte[] dataByte = Base64.getDecoder().decode(encryptedData);
|
|
|
+ byte[] keyByte = Base64.getDecoder().decode(sessionKey);
|
|
|
+ byte[] ivByte = Base64.getDecoder().decode(iv);
|
|
|
+
|
|
|
+ // 如果密钥不足16位,就补足为16位
|
|
|
+ byte[] key = new byte[16];
|
|
|
+ System.arraycopy(keyByte, 0, key, 0, Math.min(keyByte.length, key.length));
|
|
|
+
|
|
|
+ // 解密
|
|
|
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
|
+ SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
|
|
|
+ IvParameterSpec ivParameterSpec = new IvParameterSpec(ivByte);
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
|
|
|
+ return cipher.doFinal(dataByte);
|
|
|
+ }
|
|
|
+
|
|
|
}
|