浏览代码

微信登录

Administrator 2 年之前
父节点
当前提交
45d5fe7d45

+ 18 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@@ -2,6 +2,8 @@ package com.ruoyi.web.controller.system;
 
 import java.util.List;
 import java.util.Set;
+
+import com.ruoyi.framework.web.domain.AppletLoginForm;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -51,6 +53,22 @@ public class SysLoginController
         return ajax;
     }
 
+    /**
+     * 微信登录方法
+     *
+     * @param  form 登录信息
+     * @return 结果
+     */
+    @PostMapping("/weChatLogin")
+    public AjaxResult weChatLogin(@RequestBody AppletLoginForm form)
+    {
+        AjaxResult ajax = AjaxResult.success();
+        // 生成令牌
+        String token = loginService.weChatLogin(form);
+        ajax.put(Constants.TOKEN, token);
+        return ajax;
+    }
+
     /**
      * 获取用户信息
      * 

+ 7 - 0
ruoyi-common/pom.xml

@@ -125,6 +125,13 @@
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
         </dependency>
+        <!--http-->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.2</version>
+        </dependency>
+
 
     </dependencies>
 

+ 278 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/Base64.java

@@ -0,0 +1,278 @@
+package com.ruoyi.common.utils;
+
+/*
+ * Copyright (C) 2010 The MobileSecurePay Project
+ * All right reserved.
+ * author: shiqun.shi@alipay.com
+ */
+public final class Base64 {
+
+    static private final int BASELENGTH = 128;
+    static private final int LOOKUPLENGTH = 64;
+    static private final int TWENTYFOURBITGROUP = 24;
+    static private final int EIGHTBIT = 8;
+    static private final int SIXTEENBIT = 16;
+    static private final int FOURBYTE = 4;
+    static private final int SIGN = -128;
+    static private final char PAD = '=';
+    static private final boolean fDebug = false;
+    static final private byte[] base64Alphabet = new byte[BASELENGTH];
+    static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
+
+    static {
+        for (int i = 0; i < BASELENGTH; ++i) {
+            base64Alphabet[i] = -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--) {
+            base64Alphabet[i] = (byte) (i - 'A');
+        }
+        for (int i = 'z'; i >= 'a'; i--) {
+            base64Alphabet[i] = (byte) (i - 'a' + 26);
+        }
+
+        for (int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i - '0' + 52);
+        }
+
+        base64Alphabet['+'] = 62;
+        base64Alphabet['/'] = 63;
+
+        for (int i = 0; i <= 25; i++) {
+            lookUpBase64Alphabet[i] = (char) ('A' + i);
+        }
+
+        for (int i = 26, j = 0; i <= 51; i++, j++) {
+            lookUpBase64Alphabet[i] = (char) ('a' + j);
+        }
+
+        for (int i = 52, j = 0; i <= 61; i++, j++) {
+            lookUpBase64Alphabet[i] = (char) ('0' + j);
+        }
+        lookUpBase64Alphabet[62] = (char) '+';
+        lookUpBase64Alphabet[63] = (char) '/';
+
+    }
+
+    private static boolean isWhiteSpace(char octect) {
+        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
+    }
+
+    private static boolean isPad(char octect) {
+        return (octect == PAD);
+    }
+
+    private static boolean isData(char octect) {
+        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static String encode(byte[] binaryData) {
+
+        if (binaryData == null) {
+            return null;
+        }
+
+        int lengthDataBits = binaryData.length * EIGHTBIT;
+        if (lengthDataBits == 0) {
+            return "";
+        }
+
+        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+        int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
+        char encodedData[] = null;
+
+        encodedData = new char[numberQuartet * 4];
+
+        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        if (fDebug) {
+            System.out.println("number of triplets = " + numberTriplets);
+        }
+
+        for (int i = 0; i < numberTriplets; i++) {
+            b1 = binaryData[dataIndex++];
+            b2 = binaryData[dataIndex++];
+            b3 = binaryData[dataIndex++];
+
+            if (fDebug) {
+                System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3);
+            }
+
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+            byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
+
+            if (fDebug) {
+                System.out.println("val2 = " + val2);
+                System.out.println("k4   = " + (k << 4));
+                System.out.println("vak  = " + (val2 | (k << 4)));
+            }
+
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
+        }
+
+        // form integral number of 6-bit groups
+        if (fewerThan24bits == EIGHTBIT) {
+            b1 = binaryData[dataIndex];
+            k = (byte) (b1 & 0x03);
+            if (fDebug) {
+                System.out.println("b1=" + b1);
+                System.out.println("b1<<2 = " + (b1 >> 2));
+            }
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
+            encodedData[encodedIndex++] = PAD;
+            encodedData[encodedIndex++] = PAD;
+        } else if (fewerThan24bits == SIXTEENBIT) {
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
+            encodedData[encodedIndex++] = PAD;
+        }
+
+        return new String(encodedData);
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param encoded string containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode(String encoded) {
+
+        if (encoded == null) {
+            return null;
+        }
+
+        char[] base64Data = encoded.toCharArray();
+        // remove white spaces
+        int len = removeWhiteSpace(base64Data);
+
+        if (len % FOURBYTE != 0) {
+            return null;//should be divisible by four
+        }
+
+        int numberQuadruple = (len / FOURBYTE);
+
+        if (numberQuadruple == 0) {
+            return new byte[0];
+        }
+
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
+        char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
+
+        int i = 0;
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        decodedData = new byte[(numberQuadruple) * 3];
+
+        for (; i < numberQuadruple - 1; i++) {
+
+            if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
+                    || !isData((d3 = base64Data[dataIndex++]))
+                    || !isData((d4 = base64Data[dataIndex++]))) {
+                return null;
+            }//if found "no data" just return null
+
+            b1 = base64Alphabet[d1];
+            b2 = base64Alphabet[d2];
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+        }
+
+        if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
+            return null;//if found "no data" just return null
+        }
+
+        b1 = base64Alphabet[d1];
+        b2 = base64Alphabet[d2];
+
+        d3 = base64Data[dataIndex++];
+        d4 = base64Data[dataIndex++];
+        if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters
+            if (isPad(d3) && isPad(d4)) {
+                if ((b2 & 0xf) != 0)//last 4 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 1];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                return tmp;
+            } else if (!isPad(d3) && isPad(d4)) {
+                b3 = base64Alphabet[d3];
+                if ((b3 & 0x3) != 0)//last 2 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 2];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+                tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                return tmp;
+            } else {
+                return null;
+            }
+        } else { //No PAD e.g 3cQl
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+
+        }
+
+        return decodedData;
+    }
+
+    /**
+     * remove WhiteSpace form MIME containing encoded Base64 data.
+     *
+     * @param data the byte array of base64 data (with WS)
+     * @return the new length
+     */
+    private static int removeWhiteSpace(char[] data) {
+        if (data == null) {
+            return 0;
+        }
+
+        // count characters that's not whitespace
+        int newSize = 0;
+        int len = data.length;
+        for (int i = 0; i < len; i++) {
+            if (!isWhiteSpace(data[i])) {
+                data[newSize++] = data[i];
+            }
+        }
+        return newSize;
+    }
+}

+ 117 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpClientUtils.java

@@ -0,0 +1,117 @@
+package com.ruoyi.common.utils.http;
+
+
+
+import com.alibaba.fastjson2.JSONObject;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
+import java.io.IOException;
+import java.util.Map;
+
+public class HttpClientUtils {
+
+    final static int TIMEOUT = 1000;
+    final static int TIMEOUT_MSEC = 5 * 1000;
+
+    public static String doPost(String url, JSONObject json) throws IOException {
+        // 创建Httpclient对象
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        CloseableHttpResponse response = null;
+        String resultString = "";
+        try {
+            // 创建Http Post请求
+            HttpPost httpPost = new HttpPost(url);
+/*            // 创建参数列表
+            if (paramMap != null) {
+                List<NameValuePair> paramList = new ArrayList<>();
+                for (Map.Entry<String, String> param : paramMap.entrySet()) {
+                    paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
+                }
+                // 模拟表单
+                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
+                httpPost.setEntity(entity);
+            }*/
+
+            StringEntity s = new StringEntity(json.toString(), "utf-8");
+            s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
+                    "application/json"));
+            httpPost.setEntity(s);
+
+            httpPost.setConfig(builderRequestConfig());
+
+            // 执行http请求
+            response = httpClient.execute(httpPost);
+
+            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
+        } catch (Exception e) {
+            throw e;
+        } finally {
+            try {
+                response.close();
+            } catch (IOException e) {
+                throw e;
+            }
+        }
+
+        return resultString;
+    }
+
+    public static String doGet(String url, Map<String, String> paramMap) throws IOException {
+        url += "?appid=" + paramMap.get("appid") + "&secret=" + paramMap.get("secret") + "&js_code=" + paramMap.get("js_code") + "&grant_type=" + paramMap.get("grant_type");
+        // 创建Httpclient对象
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        CloseableHttpResponse response = null;
+        String resultString = "";
+        try {
+            // 创建Http Post请求
+            HttpGet httpGet = new HttpGet(url);
+            // 执行http请求
+            response = httpClient.execute(httpGet);
+            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
+        } catch (Exception e) {
+            throw e;
+        } finally {
+            try {
+                response.close();
+            } catch (IOException e) {
+                throw e;
+            }
+        }
+        return resultString;
+    }
+
+    public static String doGet1(String url) throws IOException {
+        // 创建Httpclient对象
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        CloseableHttpResponse response = null;
+        String resultString = "";
+        try {
+            // 创建Http Post请求
+            HttpGet httpGet = new HttpGet(url);
+            // 执行http请求
+            response = httpClient.execute(httpGet);
+            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
+        } catch (Exception e) {
+            throw e;
+        } finally {
+            assert response != null;
+            response.close();
+        }
+        return resultString;
+    }
+
+    private static RequestConfig builderRequestConfig() {
+        return RequestConfig.custom()
+                .setConnectTimeout(TIMEOUT_MSEC)
+                .setConnectionRequestTimeout(TIMEOUT_MSEC)
+                .setSocketTimeout(TIMEOUT_MSEC).build();
+    }
+}

+ 5 - 0
ruoyi-framework/pom.xml

@@ -17,6 +17,11 @@
 
     <dependencies>
 
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.59</version>
+        </dependency>
         <!-- SpringBoot Web容器 -->
          <dependency>
             <groupId>org.springframework.boot</groupId>

+ 57 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/utils/AppletDecryptDataUtil.java

@@ -0,0 +1,57 @@
+package com.ruoyi.framework.utils;
+
+
+import com.alibaba.fastjson2.JSONObject;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.AlgorithmParameters;
+import java.security.Security;
+import java.util.Arrays;
+
+/**
+ * 解密工具
+ */
+public class AppletDecryptDataUtil {
+
+    public static JSONObject decryptData(byte[] keyByte, byte[] ivByte, byte[] dataByte) throws Exception {
+        // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
+        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;
+        }
+
+        byte[] resultByte;
+        // 初始化
+        Security.addProvider(new BouncyCastleProvider());
+        try {
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
+            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
+            parameters.init(new IvParameterSpec(ivByte));
+            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
+            resultByte = cipher.doFinal(dataByte);
+            if (null == resultByte || resultByte.length <= 0) {
+                return null;
+            }
+        }catch (Exception e){
+            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);// 初始化
+            resultByte = cipher.doFinal(dataByte);
+            if (null == resultByte || resultByte.length <= 0) {
+                return null;
+            }
+        }
+
+        String result = new String(resultByte, "UTF-8");
+        return JSONObject.parseObject(result);
+    }
+}

+ 109 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/utils/WxCodeSessionUtil.java

@@ -0,0 +1,109 @@
+package com.ruoyi.framework.utils;
+
+
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.utils.Base64;
+import com.ruoyi.common.utils.http.HttpClientUtils;
+import com.ruoyi.framework.web.domain.AppletLoginForm;
+import com.ruoyi.framework.web.domain.AppletSessionDTO;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ *
+ */
+public class WxCodeSessionUtil {
+
+
+    /**
+     * 根据code获取小程序openid和unionid
+     */
+    private static final String JSCODE_SESSION_API = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
+
+    /**
+     * 小程序appId
+     */
+
+    private static final String APP_ID = "1";
+
+    /**
+     * 小程序密钥
+     */
+    private static final String APP_SECRET = "2";
+
+
+    /**
+     * 根据code获取小程序openid和unionid
+     *
+     * @param form
+     * @return
+     */
+    public AppletSessionDTO jscode2Session(AppletLoginForm form) {
+        // 获取openId和sessionKey
+        JSONObject result;
+        try {
+            String requestUrl = JSCODE_SESSION_API.replace("APPID", APP_ID)
+                    .replace("SECRET", APP_SECRET)
+                    .replace("JSCODE", form.getCode().trim());
+
+            String jsonStr = HttpClientUtils.doGet1(requestUrl);
+            result = JSONObject.parseObject(jsonStr);
+            if (StringUtils.isEmpty(result.toString())) {
+                throw new RuntimeException("错误");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("错误");
+        }
+
+        int errcode = result.getIntValue("errcode");
+        if (errcode != 0) {
+            String errmsg = result.getString("errmsg");
+            throw new RuntimeException("获取小程序授权错误信息, " + errmsg);
+        }
+        // 获取openId,unionId,sessionKey
+        AppletSessionDTO appletSession = new AppletSessionDTO();
+        appletSession.setOpenId(result.getString("openid"));
+        // unionId有可能是空
+        appletSession.setUnionId(result.getString("unionid"));
+        appletSession.setSessionKey(result.getString("session_key"));
+
+        System.out.println();
+        String phoneNumber = getPhoneNumber(form, appletSession);
+        appletSession.setPhoneNumber(phoneNumber);
+        return appletSession;
+    }
+
+    /**
+     * 手机号解密
+     */
+    private String getPhoneNumber(AppletLoginForm form, AppletSessionDTO appletSession) {
+
+        // 解密文件
+        String encryptedData = form.getEncryptedData();
+        // 解密向量
+        String iv = form.getIv();
+        // 加密秘钥
+        byte[] dataByte = Base64.decode(encryptedData);
+        // session_key
+        byte[] keyByte = Base64.decode(appletSession.getSessionKey());
+        // 偏移量
+        byte[] ivByte = Base64.decode(iv);
+        JSONObject result;
+        try {
+            result = AppletDecryptDataUtil.decryptData(keyByte, ivByte, dataByte);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+
+        assert result != null;
+        String purePhoneNumber = result.getString("purePhoneNumber");
+        if (null == purePhoneNumber || purePhoneNumber.isEmpty()) {
+            throw new RuntimeException("获取手机号失败");
+        }
+        return purePhoneNumber;
+    }
+
+
+}

+ 55 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/AppletLoginForm.java

@@ -0,0 +1,55 @@
+package com.ruoyi.framework.web.domain;
+
+import com.ruoyi.common.core.domain.entity.SysUser;
+
+/**
+ * @author shiqian
+ * @date 2021年09月08日 17:15
+ **/
+public class AppletLoginForm {
+
+
+    // 微信code
+    private String code;
+
+    // 微信用户基本信息
+    private SysUser user;
+
+    // 加密数据
+    private String encryptedData;
+
+    // 向量
+    private String iv;
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public SysUser getUser() {
+        return user;
+    }
+
+    public void setUser(SysUser user) {
+        this.user = user;
+    }
+
+    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;
+    }
+}

+ 52 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/AppletSessionDTO.java

@@ -0,0 +1,52 @@
+package com.ruoyi.framework.web.domain;
+
+/**
+ * 微信通用接口凭证
+ */
+
+public class AppletSessionDTO {
+
+    // 授权openid
+    private String openId;
+
+    // 微信会话session
+    private String sessionKey;
+
+    // 微信用户唯一unionid
+    private String unionId;
+
+    // 绑定手机号
+    private String phoneNumber;
+
+    public String getOpenId() {
+        return openId;
+    }
+
+    public void setOpenId(String openId) {
+        this.openId = openId;
+    }
+
+    public String getSessionKey() {
+        return sessionKey;
+    }
+
+    public void setSessionKey(String sessionKey) {
+        this.sessionKey = sessionKey;
+    }
+
+    public String getUnionId() {
+        return unionId;
+    }
+
+    public void setUnionId(String unionId) {
+        this.unionId = unionId;
+    }
+
+    public String getPhoneNumber() {
+        return phoneNumber;
+    }
+
+    public void setPhoneNumber(String phoneNumber) {
+        this.phoneNumber = phoneNumber;
+    }
+}

+ 60 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java

@@ -1,11 +1,21 @@
 package com.ruoyi.framework.web.service;
 
 import javax.annotation.Resource;
+import javax.validation.constraints.Size;
+
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.enums.UserStatus;
+import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.framework.utils.WxCodeSessionUtil;
+import com.ruoyi.framework.web.domain.AppletLoginForm;
+import com.ruoyi.framework.web.domain.AppletSessionDTO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.stereotype.Component;
 import com.ruoyi.common.constant.CacheConstants;
 import com.ruoyi.common.constant.Constants;
@@ -29,6 +39,8 @@ import com.ruoyi.framework.security.context.AuthenticationContextHolder;
 import com.ruoyi.system.service.ISysConfigService;
 import com.ruoyi.system.service.ISysUserService;
 
+import static com.sun.xml.internal.ws.spi.db.BindingContextFactory.LOGGER;
+
 /**
  * 登录校验方法
  * 
@@ -52,6 +64,15 @@ public class SysLoginService
     @Autowired
     private ISysConfigService configService;
 
+    @Resource
+    private WxCodeSessionUtil codeUtil;
+
+    @Autowired
+    private SysPasswordService passwordService;
+
+    @Autowired
+    private SysPermissionService permissionService;
+
     /**
      * 登录验证
      * 
@@ -100,6 +121,45 @@ public class SysLoginService
         return tokenService.createToken(loginUser);
     }
 
+    /**
+     * 微信登录验证
+     */
+    public String weChatLogin(AppletLoginForm form)
+    {
+        //用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID
+        // (若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key
+        AppletSessionDTO dto = codeUtil.jscode2Session(form);
+        String phoneNumber = dto.getPhoneNumber();
+        if (StringUtils.isBlank(phoneNumber)){
+            throw new BaseException("对不起,未获取到手机号");
+        }
+
+        SysUser user = userService.getByPhone(dto.getPhoneNumber());
+        if (user == null){
+            throw new BaseException("对不起,未获取到相关信息");
+        }
+        String phonenumber = user.getPhonenumber();
+
+        if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
+        {
+
+            throw new ServiceException("对不起,您的账号:" + phonenumber + " 已被删除");
+        }
+         if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
+        {
+
+            throw new ServiceException("对不起,您的账号:" + phonenumber + " 已停用");
+        }
+        passwordService.validate(user);
+        LoginUser loginUser = createLoginUser(user);
+        recordLoginInfo(loginUser.getUserId());
+        return tokenService.createToken(loginUser);
+    }
+
+    public LoginUser createLoginUser(SysUser user)
+    {
+        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
+    }
     /**
      * 校验验证码
      * 

+ 8 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java

@@ -43,6 +43,14 @@ public interface SysUserMapper
      */
     public SysUser selectUserByUserName(String userName);
 
+    /**
+     * 通过手机号查询用户
+     *
+     * @param phone 手机号
+     * @return 用户对象信息
+     */
+    public SysUser getByPhone(String phone);
+
     /**
      * 通过用户ID查询用户
      * 

+ 7 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java

@@ -41,6 +41,13 @@ public interface ISysUserService
      * @return 用户对象信息
      */
     public SysUser selectUserByUserName(String userName);
+    /**
+     * 通过手机号查询用户
+     *
+     * @param phone 用户名
+     * @return 用户对象信息
+     */
+    public SysUser getByPhone(String phone);
 
     /**
      * 通过用户ID查询用户

+ 11 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

@@ -111,6 +111,17 @@ public class SysUserServiceImpl implements ISysUserService
     {
         return userMapper.selectUserByUserName(userName);
     }
+    /**
+     * 通过手机号查询用户
+     *
+     * @param phone 手机号
+     * @return 用户对象信息
+     */
+    @Override
+    public SysUser getByPhone(String phone) {
+        return userMapper.getByPhone(phone);
+
+    }
 
     /**
      * 通过用户ID查询用户

+ 7 - 2
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -141,8 +141,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	<select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
 		select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1
 	</select>
-	
-	<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
+
+	<select id="getByPhone" parameterType="String" resultMap="SysUserResult">
+		<include refid="selectUserVo"/>
+		where u.phonenumber = #{phone} and u.del_flag = '0'
+	</select>
+
+    <insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
  		insert into sys_user(
  			<if test="userId != null and userId != 0">user_id,</if>
  			<if test="deptId != null and deptId != 0">dept_id,</if>