SpringBoot 微信小程序登录功能

avatar 2021年03月08日21:57:13 0 327 views

最近在帮朋友做小程序项目,把之前的web的商城新增支持小程序,预计本周内做完。

今天把首页、商品列表、详情和登录做完了,记录一下登录的实现。

先上效果图啦!

不多说,直接上代码

一、小程序前端代码

/**
 * 调用微信登录,获取sessionKey
 */
function loginByWeixin() {
    let code = null;
    return new Promise(function (resolve, reject) {
        return login().then((res) => {
            code = res.code;
            return getUserInfo();
        }).then((userInfo) => {
            //登录远程服务器
            wx.request({
                url: "http://localhost:8080/api/wx/login",
                data: {
                    code: code,
                    userInfo: userInfo
                },
                method: 'POST',
                header: {
                    'Content-Type': 'application/json',
                    'X-Nideshop-Token': wx.getStorageSync('token')
                },
                success: function (res) {
                    console.log(res);

                    if (res.statusCode == 200) {
                        //存储用户信息
                        wx.setStorageSync('userInfo', res.data.data.userInfo);
                        wx.setStorageSync('token', res.data.data.token);
                        // 跳转登录页面
                        wx.switchTab({
                            url: '/pages/ucenter/index/index',
                        });
                        resolve(res);
                    } else {
                        reject(res.message);
                    }

                },
                fail: function (err) {
                    reject(err)
                }
            })
        }).catch((err) => {
            reject(err);
        })
    });
}

/**
 * 调用微信登录生成code
 */
function login() {
    return new Promise(function (resolve, reject) {
        wx.login({
            success: function (res) {
                if (res.code) {
                    //登录远程服务器
                    resolve(res);
                } else {
                    reject(res);
                }
            },
            fail: function (err) {
                reject(err);
            }
        });
    });
}

/**
 * 获取用户信息
 */
function getUserInfo() {
    return new Promise(function (resolve, reject) {
        wx.getUserInfo({
            withCredentials: true,
            success: function (res) {
                resolve(res);
            },
            fail: function (err) {
                reject(err);
            }
        })
    });
}

 

二、Java后端代码

1、控制器代码

    /**
     * 微信用户登录详情
     */
    @PostMapping("/api/wx/login")
    @ResponseBody
    public Result user_login(@RequestBody Map<String, Object> param) {
        String code = (String) param.get("code");
        Map<String, Object> userInfoMap = (Map<String, Object>) param.get("userInfo");
        Map<String, Object> rawData = (Map<String, Object>) userInfoMap.get("userInfo");
        String signature = (String) userInfoMap.get("signature");
        String encrypteData = (String) userInfoMap.get("encryptedData");
        String iv = (String) userInfoMap.get("iv");

        // 用户非敏感信息:rawData
        // 签名:signature
        // 1.接收小程序发送的code
        // 2.开发者服务器 登录凭证校验接口 appi + appsecret + code
        JSONObject SessionKeyOpenId = WxUtil.getSessionKeyOrOpenId(code);
        // 3.接收微信接口服务 获取返回的参数
        String openid = SessionKeyOpenId.getString("openid");
        String sessionKey = SessionKeyOpenId.getString("session_key");

        // 4.校验签名 小程序发送的签名signature与服务器端生成的签名signature2 = sha1(rawData + sessionKey)
        String signature2 = DigestUtils.sha1Hex(JSON.toJSONString(rawData) + sessionKey);
        if (!signature.equals(signature2)) {
            return ResultGenerator.genFailResult("签名校验失败");
        }
        // 5.根据返回的User实体类,判断用户是否是新用户,是的话,将用户信息存到数据库;不是的话,更新最新登录时间
        User user = this.userService.findByOpenId(openid);
        if (user == null) {
            // 用户信息入库
            String nickName = (String) rawData.get("nickName");
            String avatarUrl = (String) rawData.get("avatarUrl");

            user = new User();
            user.setOpenId(openid);
            user.setSkey(sessionKey);
            user.setCreateTime(new Date());
            user.setAvatar(avatarUrl);
            user.setNickName(nickName);
            String passwordMD5 = MD5Util.MD5Encode("123456", "UTF-8");
            user.setPasswordMd5(passwordMD5);
            user.setLoginName("wx用户" + System.currentTimeMillis());

            this.userService.insertUser(user);
        } else {
            // 重新设置会话skey
            user.setSkey(sessionKey);
            userService.updateUserInfo(user, null);
        }

        //6. 把新的skey返回给小程序
        Map<String, Object> result = new HashMap<>();
        result.put("token", sessionKey);
        result.put("userInfo", rawData);
        return ResultGenerator.genSuccessResult("登录成功", result);
    }

 

2、其他代码

pom.xml 部分代码

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.72</version>
</dependency>

<!--base64加密解密-->
<!--shiro依赖和缓存-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.0</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-api</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk16</artifactId>
    <version>1.46</version>
</dependency>

<!-- http请求工具包依赖 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.2</version>
</dependency>

 

HttpClientUtil.java

package com.example.mall.util;


import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
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.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
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.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author 言曌
 * @date 2021/3/8 5:52 下午
 */
public class HttpClientUtil {

    public static String doGet(String url, Map<String, String> param) {

        // 创建Httpclient对象
        CloseableHttpClient httpclient = HttpClients.createDefault();

        String resultString = "";
        CloseableHttpResponse response = null;
        try {
            // 创建uri
            URIBuilder builder = new URIBuilder(url);
            if (param != null) {
                for (String key : param.keySet()) {
                    builder.addParameter(key, param.get(key));
                }
            }
            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();
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return resultString;
    }

    public static String doGet(String url) {
        return doGet(url, null);
    }

    public static String doPost(String url, Map<String, String> param) {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
            // 创建参数列表
            if (param != null) {
                List<NameValuePair> paramList = new ArrayList<>();
                for (String key : param.keySet()) {
                    paramList.add(new BasicNameValuePair(key, param.get(key)));
                }
                // 模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
                httpPost.setEntity(entity);
            }
            // 执行http请求
            response = httpClient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }

    public static String doPost(String url) {
        return doPost(url, null);
    }

    public static String doPostJson(String url, String json) {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
            // 创建请求内容
            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);
            // 执行http请求
            response = httpClient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }
}

 

WxUtil

package com.example.mall.util;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.codec.Base64;
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;
import java.util.HashMap;
import java.util.Map;


/**
 * @author 言曌
 * @date 2021/3/8 5:49 下午
 */
public class WxUtil {

    /**
     * 获得openID和sessionkey
     *
     * @param code
     * @return
     */
    public static JSONObject getSessionKeyOrOpenId(String code) {
        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
        Map<String, String> requestUrlParam = new HashMap<>();
        // https://mp.weixin.qq.com/wxopen/devprofile?action=get_profile&token=164113089&lang=zh_CN
        //小程序appId
        requestUrlParam.put("appid", "wx818b2b43c52117f9");
        //小程序secret
        requestUrlParam.put("secret", "52877e406406b61b829a789fb2b68b3a");
        //小程序端返回的code
        requestUrlParam.put("js_code", code);
        //默认参数
        requestUrlParam.put("grant_type", "authorization_code");
        //发送post请求读取调用微信接口获取openid用户唯一标识
        JSONObject jsonObject = JSON.parseObject(HttpClientUtil.doPost(requestUrl, requestUrlParam));
        return jsonObject;
    }

    /**
     * 获取用户信息
     *
     * @param encryptedData
     * @param sessionKey
     * @param iv
     * @return
     */
    public static JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足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;
            }
            // 初始化
            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, "UTF-8");
                return JSON.parseObject(result);
            }
        } catch (Exception e) {
        }
        return null;
    }
}

 

其他代码就没必要贴了

 

参考:https://www.jianshu.com/p/4e4db943bfb3

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
可以匿名评论或者登录后台评论,评论回复后会有邮件通知

  

已通过评论:0   待审核评论数:0