微信小程序登录态维持

小程序登录过程

小程序用户登录时,先判断storage中的sessionid是否存在,如果存在,则将sessionid传到后台,判断用户信息是否失效。
-----若失效,前台将sessionid缓存删除,重新调用登录接口。
-----若未失效,返回原页面,后台更新缓存失效时间。
如果不存在,前台调用wx.login获取code,调用getUserInof获取用户信息。
-----如果成功,用户将唯一标识和会话id写入storage缓存。
-----如果失败,用户清除缓存中的唯一标识和会话id
(code用来换取openid和session_key,encryptedDataheiv用来获得并解密用户信息)
如果后台获取用户信息成功,返回sessionid,登录成功。

用户授权登录

  bindGetUserInfo: function(e) {
      console.log("用户授权"+JSON.stringify(e))
          if (!e.detail.userInfo) {
      return;
    }
    if (app.globalData.isConnected) {
      wx.setStorageSync('userInfo', e.detail.userInfo)
      this.login();
    } else {
      wx.showToast({
        title: '当前无网络',
        icon: 'none',
      })
    }
  }

小程序获取code后,向第三方服务器发送请求

微信小程序调用wx.login接口

login: function() {
    let that = this;
    let sessionid = wx.getStorageSync("sessionid");
    console.log("sessionid" + sessionid);
    if (sessionid) {
      console.log("如果sessionid存在");
      wx.request({
        url: 'https://127.0.0.1/Api/wx/wxLogin',
        header: {
        //设置请求头部,sessionid是后台返回的唯一标识,session存放session会话id(例如:JSESSIONID=35EA60A066CE3349A87E524C6B865146; Path=/; Secure; HttpOnly)
          "sessionid": wx.getStorageSync("sessionid"),
          "Cookie": wx.getStorageSync("session")
        },
        method: "POST",
        success: function(res) {
          if (res.data === "登录失败") {
            console.log("后台缓存失效,小程序清空sessionid和userInfo");
            wx.removeStorageSync("sessionid");
            wx.removeStorageSync("session")
            //重新登录
            that.login();
          } else {
            console.log("后台缓存中的信息存在,直接返回");
          	//省略登录,跳转页面
          }
        },
      })
    } else {
      console.log("如果sessionid不存在");
      //调用wx.login接口获得code
      wx.login({
        success: res => {
          if (res.code) {
            //获取用户信息
            wx.getUserInfo({
              success: datas => {
                console.log("data", datas);
                //向第三方服务器发送code,并获得唯一标识和会话id
                wx.request({
                  url: 'https://127.0.0.1/Api/wx/getOpenId',
                  data: {
                    code: res.code,
                    //encryptedData和iv是用于用户信息解密
                    encryptedData: datas.encryptedData,
                    iv: datas.iv
                  },
                  header: {
                    'content-type': 'application/x-www-form-urlencoded'
                  },
                  method: 'post',
                  success: function(res) {
                    if (!res.data) {
					  //后台登录失败,删除小程序storage中的唯一标识和会话id
                      wx.removeStorageSync("sessionid", res.data)
                      wx.removeStorageSync("session", res.cookies[0])
                      return;
                    }
                    //后台登录成功,将唯一标识和会话id保存在微信小程序中
                    wx.setStorageSync("sessionid", res.data)
                    wx.setStorageSync("session", res.cookies[0]
                  }
                })
              }
            })
          }
        }
      })
    }
  }

第三方服务器使用code向微信服务器请求,换取session_key和openid

使用到的相关工具类附在最后

 @PostMapping("/getOpenId")
    public  String getSessionByCode(String code,String encryptedData,String iv) {
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        HttpSession session = request.getSession();
        //从配置文件中读取相关值
        appId = WxUtil.getValue("APP_ID");
        appSecret = WxUtil.getValue("APP_SECRET");
        grantType = WxUtil.getValue("GRANT_TYPE");
        requestUrl = WxUtil.getValue("requestUrl");
        String url = requestUrl
                + "?appid=" + appId
                + "&secret=" + appSecret
                + "&js_code=" + code
                + "&grant_type=" + grantType;
        // 发送请求
        String data = HttpUtil.get(url);
        ObjectMapper mapper = new ObjectMapper();
        Map<String, String> json = null;
        try {
            json = mapper.readValue(data, Map.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
	    
	    //持久化操作(省略)

        //从返回结果中取出openid和session_key
        String openid = json.get("openid");
        String session_key = json.get("session_key");

        //对encryptedData解密
        JSONObject user = WxSecurityHelper.getUserInfo(encryptedData,session_key,iv);

        //对openid加密,并以键值形式存放在第三方服务器的session中
        String value = session_key+','+openid;
        String key = WxSecurityHelper.authcode(openid,session_key);
        session.setAttribute(key,value);

        //以秒为单位,即在没有活动30分钟后,session将失效
        session.setMaxInactiveInterval(30*60);
        String content = (String) session.getAttribute(key);

		//将key作为唯一标识,并赋给sessionid
        String sessionid = key;

        //返回sessionid
        return sessionid;
    }

第三方服务器登录验证

 @PostMapping("/wxLogin")
    public String wxLogin() {
    //获取request
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String sessionid = request.getHeader("sessionid");
        //根据request和sessionid校验
        if (WxSecurityHelper.verify(request,sessionid)){
            return "登录成功";
        }

        return "登录失败";
    }

配置文件

wx.properties

APP_ID = "你的appid"
APP_SECRET = "你的appsecret"
GRANT_TYPE = authorization_code
requestUrl = https://api.weixin.qq.com/sns/jscode2session

工具类

WxUtil.java

读取配置文件中的常量(wx.properties)

package com.cisdi.info.simple.util;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class WxUtil {
    private static Properties props = new Properties();

    static
    {
        try
        {
            props.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("wx.properties"));
        }
        catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    public static String getValue(String key)
    {
        return props.getProperty(key);
    }

    public static void updateProperties(String key, String value)
    {
        props.setProperty(key, value);
    }

}

WxSecurityHelper.java

加密解密

package com.cisdi.info.simple.util;

import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.security.AlgorithmParameters;
import java.security.MessageDigest;
import java.security.Security;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Objects;

public class WxSecurityHelper {
    private Long expire = 1800L;   //session过期时间

    /**
     *
     * @return String 加密结果
     */
    public static String authcode(String str, String session_key) {
        String key = Objects.requireNonNull(md5(session_key)).substring(5, 8);
        str = Objects.requireNonNull(md5(str)).substring(8, 10);
        return md5(key + str);
    }

    /**
     * 小程序登录验证
     * @return
     */
    public static boolean verify(HttpServletRequest request, String sessionid) {
        if(request.getSession(false)==null){
            System.out.println("sessionid已过期");
            return false;
        }else{
            HttpSession session   =   request.getSession();
            // 获取session中所有的键值
            Enumeration<?> enumeration = session.getAttributeNames();
            String code;

            code = (String) session.getAttribute(sessionid);

            if (code != null){
                Integer splitIndex = code.indexOf(',');
                String session_key = code.substring(0,splitIndex);
                String openid = code.substring(splitIndex+1);
                String secode = authcode(openid,session_key);
                System.out.print("secode"+secode);
                if (secode.equals(sessionid)) {
                  //更新有效时间
                    session.setMaxInactiveInterval(1000*60);
                    return true;
                }
            }
            
//            request.removeAttribute(sessionid);
            return false;
        }
    }

    /**
     * MD5加密算法抽取
     *
     * @param string 待加密字符串
     * @return 加密结果
     */
    private static String md5(String string) {
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            byte[] strTemp = string.getBytes();
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(strTemp);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (byte byte0 : md) {
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }

//用户信息解密
    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 JSONObject.parseObject(result);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return null;

    }
}

HttpUtil.java

package com.cisdi.info.simple.util;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
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.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class HttpUtil {

    private static final String Charset = "utf-8";


    /**
     * 发送请求,如果失败,会返回null
     * @param url
     * @param map
     * @return
     */
    public static String post(String url, Map<String, String> map) {
        // 处理请求地址
        try {
            HttpClient client = HttpClientBuilder.create().build();
            URI uri = new URI(url);
            HttpPost post = new HttpPost(uri);

            // 添加参数
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            for (String str : map.keySet()) {
                params.add(new BasicNameValuePair(str, map.get(str)));
            }
            post.setEntity(new UrlEncodedFormEntity(params, Charset));
            // 执行请求
            HttpResponse response = client.execute(post);

            if (response.getStatusLine().getStatusCode() == 200) {
                // 处理请求结果
                StringBuffer buffer = new StringBuffer();
                InputStream in = null;
                try {
                    in = response.getEntity().getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in,Charset));
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        buffer.append(line);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // 关闭流
                    if (in != null)
                        try {
                            in.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                }

                return buffer.toString();
            } else {
                return null;
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return null;

    }

    /**
     * 发送请求,如果失败会返回null
     * @param url
     * @param str
     * @return
     */
    public static String post(String url, String str) {
        // 处理请求地址
        try {
            HttpClient client = HttpClientBuilder.create().build();
            URI uri = new URI(url);
            HttpPost post = new HttpPost(uri);
            post.setEntity(new StringEntity(str, Charset));
            // 执行请求
            HttpResponse response = client.execute(post);

            if (response.getStatusLine().getStatusCode() == 200) {
                // 处理请求结果
                StringBuffer buffer = new StringBuffer();
                InputStream in = null;
                try {
                    in = response.getEntity().getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in,"utf-8"));
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        buffer.append(line);
                    }

                } finally {
                    // 关闭流
                    if (in != null)
                        in.close();
                }

                return buffer.toString();
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    public static String get(String url) {
        try {
            System.out.println("URL:"+url);
            HttpClient client = HttpClientBuilder.create().build();
            URI uri = new URI(url);
            HttpGet get = new HttpGet(uri);
            HttpResponse response = client.execute(get);
            if (response.getStatusLine().getStatusCode() == 200) {
                StringBuffer buffer = new StringBuffer();
                InputStream in = null;
                try {
                    in = response.getEntity().getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in,Charset));
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        buffer.append(line);
                    }

                } finally {
                    if (in != null)
                        in.close();
                }

                return buffer.toString();
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }
}

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值