微信扫码登录

微信扫码登录实现方式有多中,本次介绍的是利用带场景值的二维码去实现扫码登录

流程大致是

1授权绑定

【生成二维码并保存】     ->【  接收微信推送事件】->【轮询是否扫码】 ->【授权绑定】 

2扫码登录

【生成二维码并保存】     ->【  接收微信推送事件】->【轮询是否扫码】 ->【登录成功】

代码大致如下:

工具类

import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;

public class WeixinUtil {

    // 获取code的请求地址
    public static String Get_Code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STAT#wechat_redirect";

    // 获取Web_access_token https的请求地址
    public static String Web_access_tokenhttps = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

    // 获取Web_refresh_token的请求地址
    public static String Web_refresh_tokenhttps = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s";

    //获取公众号access_token的请求地址
    public static String web_access_token = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

    //获取公众号ticket的请求地址
    public static String web_ticket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";



    // 3.拉取用户信息的请求地址
    public static String User_Message = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s";


    // 4.订阅号拉去用户信息的请求地址,涉及订阅号是否关注的字段属性
    public static String User_Message_Subscribe = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN";
    // 5.创建二维码
    public static String cqrcode = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s";


    // 5.换取维码
    public static String qrcode = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s";

    // 替换字符串
    public static String getUserMessage(String access_token, String openid) {
        return String.format(User_Message, access_token, openid);
    }

    public static String getUserMessageSubscribe(String access_token, String openid) {
        return String.format(User_Message_Subscribe, access_token, openid);
    }
    // 替换字符串
    public static String createQRcode(String access_token) {
        return String.format(cqrcode, access_token);
    }


    // 替换字符串
    public static String getQRcode(String ticket) {
        return String.format(qrcode, ticket);
    }

    /**获取code的请求地址
     * @param APPID
     * @param REDIRECT_URI
     * @param SCOPE
     * @return code的请求地址
     */
    public static String getCode(String APPID, String REDIRECT_URI,String SCOPE) {
        // 替换字符串
        return String.format(Get_Code,APPID,REDIRECT_URI,SCOPE);
    }

    /**获取Web_access_tokenhttps的请求地址
     * @param APPID
     * @param SECRET
     * @param CODE
     * @return access_token的请求地址
     */
    public static String getWebAccess(String APPID, String SECRET,String CODE) {
        // 替换字符串
        return String.format(Web_access_tokenhttps, APPID, SECRET,CODE);
    }

    /**获取refresh_token的请求地址
     * @param APPID
     * @param REFRESH_TOKEN
     * @returnrefresh_token的请求地址
     */
    public static String getWebRefresh(String APPID, String REFRESH_TOKEN) {
        // 替换字符串
        return String.format(Web_refresh_tokenhttps, APPID, REFRESH_TOKEN);
    }

    /**获取WebAccessToken的请求地址
     * @param APPID
     * @param SECRET
     * @returnrefresh_token的请求地址
     */
    public static String getWebAccessToken(String APPID,String SECRET) {
        // 替换字符串
        return String.format(web_access_token, APPID,SECRET);
    }

    /**获取web_ticket的请求地址
     * @param AccessToken
     * @return web_ticket的请求地址
     */
    public static String getWebTicket(String AccessToken) {
        // 替换字符串
        return String.format(web_ticket,AccessToken);
    }

    /**
     * 获取客户端ip
     * @param request
     * @return IP
     */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if(index != -1){
                return ip.substring(0,index);
            }else{
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            return ip;
        }
        return request.getRemoteAddr();
    }
    /**
     * 动态遍历获取所有收到的参数,此步非常关键,因为收银宝以后可能会加字段,动态获取可以兼容由于收银宝加字段而引起的签名异常
     * @param request
     * @return
     */
    private TreeMap<String, String> getParams(HttpServletRequest request){
        TreeMap<String, String> map = new TreeMap<String, String>();
        Map reqMap = request.getParameterMap();
        for(Object key:reqMap.keySet()){
            String value = ((String[])reqMap.get(key))[0];
            System.out.println(key+";"+value);
            map.put(key.toString(),value);
        }
        return map;
    }
    /**
     * 以https方式发送请求并将请求响应内容以String方式返回
     *
     * @param path   请求路径
     * @param method 请求方法
     * @param body   请求数据体
     * @return 请求响应内容转换成字符串信息
     */
    public static String httpsRequestToString(String path, String method, String body) {
        if (path == null || method == null) {
            return null;
        }

        String response = null;
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;
        HttpsURLConnection conn = null;
        try {
            // 创建SSLConrext对象,并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new JEEWeiXinX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new SecureRandom());

            // 从上述对象中的到SSLSocketFactory
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            System.out.println(path);

            URL url = new URL(path);
            conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);

            //设置请求方式(git|post)
            conn.setRequestMethod(method);

            //有数据提交时
            if (null != body) {
                OutputStream outputStream = conn.getOutputStream();
                outputStream.write(body.getBytes("UTF-8"));
                outputStream.close();
            }

            // 将返回的输入流转换成字符串
            inputStream = conn.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
            bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }

            response = buffer.toString();
        } catch (Exception e) {

        } finally {
            if (conn != null) {
                conn.disconnect();
            }
            try {
                bufferedReader.close();
                inputStreamReader.close();
                inputStream.close();
            } catch (IOException execption) {

            }
        }
        return response;
    }
    /**

     * 方法名:httpRequest</br>

     * 详述:发送http请求</br>

     * @param requestUrl

     * @param requestMethod

     * @param outputStr

     * @return 说明返回值含义
     * */
    public static JSONObject httpRequest(String requestUrl,String requestMethod, String outputStr) {
        JSONObject jsonObject = null;
        StringBuffer buffer = new StringBuffer();
        try {
            TrustManager[] tm = { new JEEWeiXinX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new SecureRandom());
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            URL url = new URL(requestUrl);
            HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
            httpUrlConn.setSSLSocketFactory(ssf);
            httpUrlConn.setDoOutput(true);
            httpUrlConn.setDoInput(true);
            httpUrlConn.setUseCaches(false);
            httpUrlConn.setRequestMethod(requestMethod);
            if ("GET".equalsIgnoreCase(requestMethod)){
                httpUrlConn.connect();
            }
            if (null != outputStr) {
                OutputStream outputStream = httpUrlConn.getOutputStream();
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            httpUrlConn.disconnect();
            jsonObject = JSONObject.fromObject(buffer.toString());
        } catch (ConnectException ce) {
            ce.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

}

 

一.生成二维码

/**
 * 生成临时/永久二维码
 * @param sceneId 代理标识 此处传值手机号
 * @param isLimitScene 是否是永久二维码 true 永久二维码 false 临时二维码
 * @param time 临时二维码时长 默认604800 单位秒 2592000 =30天
 * @return R
 */
@Override
public R createQrcode(String sceneId, String accessToken,@RequestParam(defaultValue = "true")Boolean isLimitScene, @RequestParam(defaultValue = "604800") int time){
    String url =WeixinUtil.createQRcode(accessToken);
    JSONObject param = new JSONObject();
    JSONObject scene = new JSONObject();
    JSONObject actionInfo = new JSONObject();
    try {

        if(isLimitScene){
            param.put("action_name","QR_LIMIT_STR_SCENE");
        }else{
            param.put("action_name","QR_STR_SCENE");
            param.put("expire_seconds",time);
        }
        scene.put("scene_id",sceneId);
        actionInfo.put("scene",scene);
        param.put("action_info",actionInfo);
        logger.info("url:"+url+"send data:"+param);

        String jsonstr = HttpRequestUtils.sendPost(url,param);
        logger.info("jsonstr:"+jsonstr);
        JSONObject job = JSONObject.parseObject(jsonstr);
        job.put("sceneId",sceneId);
        return R.ok().put("data",job);
    }catch (Exception e){
        logger.warn("createQrcode error :url:"+url+"send data:"+param+"error:"+e.getMessage());
        return R.error(e.getMessage());
    }

}

 

二.微信推送事件处理

/**处理服务器推送消息
 * @return 获取结果
 */
@RequestMapping("/handelerEvent")
public void handelerEvent() {
   try {
      logger.error("++++++++++++++++++++++++微信事件推送通知+++++++++++++++++++++++++++++++++"+request.getParameterMap().toString());
      if(request.getMethod().equals("GET")){
         logger.error("++++++++++++++++++++++++校验URL真实性+++++++++++++++++++++++++++++++++"+request.getParameterMap().toString());
         access(request,response);
      }else{
         WexHandeler(request,response);
      }
   } catch (Exception e) {
      e.printStackTrace();
   }
}
/**
 * 解析处理推送事件
 * @param request
 * @param response
 * @return
 * @throws Exception
 */
public String WexHandeler(HttpServletRequest request, HttpServletResponse response) throws Exception {

        try {
            String notifyData = getNotifyStr(request);
            if (StringUtils.isBlank(notifyData)) {
                return null;
            }
            Map<String, String> eventMap = WXPayUtil.xmlToMap(notifyData);

            // 转换成map
            if (eventMap == null) {
                logger.error("微信事件推送:返回数据错误");
                return null;
            }
            if (!eventMap.containsKey("MsgType") || StringUtils.isBlank(eventMap.get("MsgType"))) {
                logger.error("微信事件推送:返回数据MsgType错误");
                return null;
            }
            String content = "未知事件推送";
            String Event = (String) eventMap.get("Event");//事件类型 关注事件还是取消关注等
            if (!StringUtils.isBlank(Event)) { //如果是事件
                try {
                    if ("subscribe".equals(Event)) {  //带参数二维码的关注事件
                        content = "关注并扫码成功";
                        //此处处理扫码扫码事件
                    } else if ("SCAN".equals(Event)) {//扫描带参数二维码事件 用户已关注时的事件推送
                        content = "扫码推送";
                       //此处处理扫码扫码事件
                    } else if("unsubscribe".equals(Event)){
                        content = "取消关注";
                       //此处处理扫码扫码事件
                    }

                    String result = "<xml>"
                            + "<ToUserName><![CDATA[" + eventMap.get("FromUserName") + "]]></ToUserName>"
                            + "<FromUserName><![CDATA[" + eventMap.get("ToUserName") + "]]></FromUserName>"
                            + "<CreateTime>" + System.currentTimeMillis() + "</CreateTime>"
                            + "<MsgType><![CDATA[text]]></MsgType>"
                            + "<Content><![CDATA["+content+"]]></Content></xml>";
                    response.setContentType("text/html;charset=UTF-8");
                    response.getWriter().println(result);
                } catch (Exception e) {
                    return e.getMessage();
                }
            }



        } catch (Exception e) {
            return request.getParameter("echostr");
        }
        return request.getParameter("echostr");
    } 
public String getNotifyStr(HttpServletRequest request) {
    String notifyData = "";
    try {
        InputStream is = request.getInputStream();
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            notifyData=sb.toString();
            System.err.println(notifyData);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            is.close();
        }
    }catch (Exception e){
        logger.error("获取回调数据异常:" + e.getMessage());
    }

    return notifyData;
}
/**
 * XML格式字符串转换为Map
 *
 * @param strXML XML字符串
 * @return XML数据转换后的Map
 * @throws Exception
 */
public static Map<String, String> xmlToMap(String strXML) throws Exception {
    try {
        Map<String, String> data = new HashMap<String, String>();
        DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
        InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
        org.w3c.dom.Document doc = documentBuilder.parse(stream);
        doc.getDocumentElement().normalize();
        NodeList nodeList = doc.getDocumentElement().getChildNodes();
        for (int idx = 0; idx < nodeList.getLength(); ++idx) {
            Node node = nodeList.item(idx);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                data.put(element.getNodeName(), element.getTextContent());
            }
        }
        try {
            stream.close();
        } catch (Exception ex) {
            // do nothing
        }
        return data;
    } catch (Exception ex) {
        WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
        throw ex;
    }

}
/**
 * 签名校验  验证URL真实性
 * @param request request
 * @param response response
 */
public static  void access(HttpServletRequest request, HttpServletResponse response) {
    try {
        System.out.println("开始签名校验");
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");
        String token = "Token";//你设置的token值
        ArrayList<String> array = new ArrayList<String>();
        array.add(signature);
        array.add(timestamp);
        array.add(nonce);

        //排序
        String sortString = sort(token, timestamp, nonce);
        //加密
        String mytoken = SHA1(sortString);
        //校验签名
        if (mytoken != null && !mytoken.equals("") && mytoken.equals(signature)) {
            System.out.println("签名校验通过。");
            response.getWriter().println(echostr);
        } else {
            System.out.println("签名校验失败。");
        }
    }catch (Exception e){
        e.printStackTrace();

    }
}


/**
 * 排序
 */
public static String sort(String token, String timestamp, String nonce) {
    String[] strArray = { token, timestamp, nonce };
    Arrays.sort(strArray);

    StringBuilder sbuilder = new StringBuilder();
    for (String str : strArray) {
        sbuilder.append(str);
    }

    return sbuilder.toString();
}
public static String SHA1(String decript) {
    try {
        MessageDigest digest = MessageDigest
                .getInstance("SHA-1");
        digest.update(decript.getBytes());
        byte messageDigest[] = digest.digest();
        // Create Hex String
        StringBuffer hexString = new StringBuffer();
        // 字节数组转换为 十六进制 数
        for (int i = 0; i < messageDigest.length; i++) {
            String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
            if (shaHex.length() < 2) {
                hexString.append(0);
            }
            hexString.append(shaHex);
        }
        return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值