我的微信扫描二维码实现登录のJava

微信登录开发参考网址

我的微信登录开发流程

配置一些基本数据
/**
* 配置好一些需要用到的数据
*/
public class WechatConfig {
    //第三方用户唯一凭证,即appid
    public static final String APPID = "wxee746fb0e8f48f44";
    //第三方用户唯一凭证密钥,即appsecret
    public static final String SECRET = "bed208cba5e32cf8e92bbc2c96bd16da";
    //获取access_token
    public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";
    // 临时二维码
    public static final String QR_SCENE = "QR_SCENE";
    // 临时的字符串参数值
    public static final String QR_STR_SCENE = "QR_STR_SCENE";
    // 永久二维码
    public static final String QR_LIMIT_SCENE = "QR_LIMIT_SCENE";
    // 永久二维码(字符串)
    public static final String QR_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE";
    // 通过accessToken 以及json 创建二维码  获取ticket
    public static final String CREATE_TICKET_PATH = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=ACCESS_TOKEN";
    // 通过ticket换取二维码
    public static final String SHOWQR_CODE_PATH = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET";
    //此接口用于获取用户个人信息 UnionID机制
    public static final String GET_UNIONID_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID";

}

生成二维码
  • 获取access_token,access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。
  • 根据access_token 以及生成二维码需要的参数,生成带参数的二维码的ticket
  • 获取二维码ticket后,通过ticket换取二维码图片url展示到前端
    /**
     * 生成带参数的二维码,扫描关注微信公众号,自动登录网站
     * @param modelMap
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/wechat/login")
    @ResponseBody
    public Result wechatMpLogin(ModelMap modelMap) throws Exception {
//        String accessToken = wechatService.getAccessToken();
        String scene_str = "perFei." + new Date().getTime();
        String ticket = wechatService.createTempStrTicket("600", scene_str);
        if(ticket != null){
            String qrcodeUrl = wechatService.showqrcode(ticket);
            modelMap.put("qrcodeUrl", qrcodeUrl); //二维码的图片路径
        }
        modelMap.put("scene_str", scene_str);
        return Result.ok(null, modelMap);
    }


    /**
     * 生成带参数的二维码的ticket
     * @param expire_seconds
     * @param scene_str
     * @return
     * @throws Exception
     */
    @Override
    public String createTempStrTicket(String expire_seconds,String scene_str) throws Exception {
        String access_token = this.getAccessToken();
        String url = WechatConfig.CREATE_TICKET_PATH;
        url = url.replaceAll("ACCESS_TOKEN",access_token);

        Map<String,String> strMap = new HashMap<String,String>();
        strMap.put("scene_str",scene_str);

        Map<String,Map<String,String>> mapMap = new HashMap<String,Map<String,String>>();
        mapMap.put("scene", strMap);

        Map<String,Object> paramsMap = new HashMap<String,Object>();
        paramsMap.put("expire_seconds", expire_seconds);
        paramsMap.put("action_name", WechatConfig.QR_STR_SCENE);
        paramsMap.put("action_info", mapMap);
        String data = new Gson().toJson(paramsMap);
        // 发送请求
        Map<String, Object> map = HttpClient.httpClientPost(url, data);
        String ticket = (String) map.get("ticket");

        return ticket==null?null:ticket;
    }


    /**
     * 获取access_tocken GET方法
     * @return
     * @throws Exception
     */
    @Override
    public String getAccessToken() throws Exception {
        String url = WechatConfig.ACCESS_TOKEN_URL;
        url = url.replaceAll("APPID", WechatConfig.APPID);
        url = url.replaceAll("SECRET", WechatConfig.SECRET);
        // 发送请求
        HttpClient client = new HttpClient(url);
        // 发送get请求
        client.get();
        // 获取到请求的结果  json格式的字符串,把json格式的字符串转换成对象或者Map集合
        String token_content = client.getContent();
        Map<String, Object> map = JSON.parseObject(token_content, Map.class);
        return map.get("access_token").toString();
    }

    /**
     * 获取二维码ticket后,通过ticket换取二维码图片展示
     * @param ticket
     * @return
     */
    @Override
    public String showqrcode(String ticket) {
        String qrcodeUrl = WechatConfig.SHOWQR_CODE_PATH;
        try {
            String encode = URLEncoder.encode(ticket, "utf-8");
            qrcodeUrl = qrcodeUrl.replaceAll("TICKET",encode);
        }catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }
        return qrcodeUrl;
    }

验证检测是否扫描了二维码
  • 主要是通过生成的二维码的参数进行判断用户是否有过扫描
    /**
     * 检测登录
     * @param scene_str
     * @return
     */
    @RequestMapping("/wechat/checkLogin")
    @ResponseBody
    public  Map<String, Object> wechatMpCheckLogin(String scene_str) {
        // 根据scene_str查询数据库,获取对应记录
        WechatUser wechat = wechatService.getWechatUser(scene_str);
        Map<String, Object> returnMap = new HashMap<String, Object>();
        if (wechat != null ) {
            returnMap.put("openid", wechat.getOpenid());
            returnMap.put("result", "true");//登录成功
        } else {
            returnMap.put("result", "false");//登录失败
        }
        return returnMap;
    }

    @Override
    public WechatUser getWechatUser(String scene_str) {
        WechatUser wechatUser = wechatMapper.selectBySceneStr(scene_str);
        if(wechatUser != null){
            return wechatUser;
        }
        return wechatUser;
    }
微信验证签名信息和接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息
    /**
     * 自定义token, 用作生成签名,从而验证安全性
     */
    private static final String TOKEN = "perFei";

    /**
     * 回调函数 post请求 接受、处理、响应微信信息
     * @param request
     * @param response
     * @throws Exception
     */
    @PostMapping(value = "/wechat/callback")
    public void callbackPost(HttpServletRequest request, HttpServletResponse response) throws Exception{
        // TODO 接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息
        // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        System.out.println("请求进入");
        String result = "";
        try {
            //获得解析微信发来的请求
            Map<String,String> map = MessageUtil.parseXml(request);
            if (map != null && map.get("FromUserName").toString() != null){
                // 通过openid获取用户信息
                Map<String, Object> wechatUserMap = wechatService.getUserInfoByOpenid(map.get("FromUserName"));
                // 将数据写入到数据库中
                String event = map.get("EventKey");
                wechatService.insertWechatUser(wechatUserMap,event);
            }
            System.out.println("开始构造消息");
            //根据消息类型 构造返回消息
            result = MessageUtil.buildXml(map);
            System.out.println(result);
            if(result.equals("")){
                result = "未正确响应";
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("发生异常:"+ e.getMessage());
        }
        response.getWriter().println(result);
    }

    /**
     * 回调函数 get请求 验证签名信息
     * @param request
     * @throws Exception
     */
    @GetMapping(value = "/wechat/callback")
    public void callbackGet(HttpServletRequest request, HttpServletResponse response) throws Exception{
        // TODO 验证接口配置信息
        System.out.println("-----开始校验签名-----");
        // 接收微信服务器发送请求时传递过来的参数

        //微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp"); //时间戳
        String nonce = request.getParameter("nonce"); //随机数
        String echostr = request.getParameter("echostr");//随机字符串

        //将token、timestamp、nonce三个参数进行字典序排序
        //并拼接为一个字符串
        String sortStr = sort(TOKEN,timestamp,nonce);
        //字符串进行shal加密
        String mySignature = shal(sortStr);
        // 校验微信服务器传递过来的签名 和  加密后的字符串是否一致, 若一致则签名通过
        if(!"".equals(signature) && !"".equals(mySignature) && signature.equals(mySignature)){
            System.out.println("-----签名校验通过-----");
            response.getWriter().write(echostr);
            response.getWriter().flush();
        }else {
            System.out.println("-----校验签名失败-----");
        }
    }

    /**
     * 参数排序
     * @param token
     * @param timestamp
     * @param nonce
     * @return
     */
    public String sort(String token, String timestamp, String nonce) {
        String[] strArray = {token, timestamp, nonce};
        Arrays.sort(strArray);
        StringBuilder sb = new StringBuilder();
        for (String str : strArray) {
            sb.append(str);
        }
        return sb.toString();
    }

    /**
     * 字符串进行shal加密
     * @param str
     * @return
     */
    public String shal(String str){
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.update(str.getBytes());
            byte messageDigest[] = digest.digest();

            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 "";
    }

工具类
package com.zcf.utils;

import com.google.gson.Gson;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
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 javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 可以发送Http请求的工具类(可以发送Https请求)
 */
public class HttpClient {

	// 请求的地址
	private String url;
	// 请求的参数
	private Map<String, String> param;
	private int statusCode;
	// 请求后,获取到的响应内容
	private String content;
	private String xmlParam;
	private boolean isHttps;

	public boolean isHttps() {
		return isHttps;
	}

	public void setHttps(boolean isHttps) {
		this.isHttps = isHttps;
	}

	public String getXmlParam() {
		return xmlParam;
	}

	public void setXmlParam(String xmlParam) {
		this.xmlParam = xmlParam;
	}

	public HttpClient(String url, Map<String, String> param) {
		this.url = url;
		this.param = param;
	}

	public HttpClient(String url) {
		this.url = url;
	}

	public void setParameter(Map<String, String> map) {
		param = map;
	}

	public void addParameter(String key, String value) {
		if (param == null)
			param = new HashMap<String, String>();
		param.put(key, value);
	}

	/**
	 * 发送post请求
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public void post() throws ClientProtocolException, IOException {
		HttpPost http = new HttpPost(url);
		setEntity(http);
		execute(http);
	}

	/**
	 * 发送put请求
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public void put() throws ClientProtocolException, IOException {
		HttpPut http = new HttpPut(url);
		setEntity(http);
		execute(http);
	}

	/**
	 * 发送get请求
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public void get() throws ClientProtocolException, IOException {
		if (param != null) {
			StringBuilder url = new StringBuilder(this.url);
			boolean isFirst = true;
			for (String key : param.keySet()) {
				if (isFirst)
					url.append("?");
				else
					url.append("&");
				url.append(key).append("=").append(param.get(key));
			}
			this.url = url.toString();
		}
		HttpGet http = new HttpGet(url);
		execute(http);
	}

	/**
	 * set http post,put param
	 */
	private void setEntity(HttpEntityEnclosingRequestBase http) {
		if (param != null) {
			List<NameValuePair> nvps = new LinkedList<NameValuePair>();
			for (String key : param.keySet())
				nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
			http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
		}
		if (xmlParam != null) {
			http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
		}
	}

	/**
	 * 真正发送请求的方法
	 * @param http
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	private void execute(HttpUriRequest http) throws ClientProtocolException,
			IOException {
		CloseableHttpClient httpClient = null;
		try {
			if (isHttps) {
				SSLContext sslContext = new SSLContextBuilder()
						.loadTrustMaterial(null, new TrustStrategy() {
							// 信任所有
							public boolean isTrusted(X509Certificate[] chain,
									String authType)
									throws CertificateException {
								return true;
							}
						}).build();
				SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
						sslContext);
				httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
						.build();
			} else {
				httpClient = HttpClients.createDefault();
			}
			CloseableHttpResponse response = httpClient.execute(http);
			try {
				if (response != null) {
					if (response.getStatusLine() != null)
						statusCode = response.getStatusLine().getStatusCode();
					HttpEntity entity = response.getEntity();
					// 响应内容
					content = EntityUtils.toString(entity, Consts.UTF_8);
				}
			} finally {
				response.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			httpClient.close();
		}
	}

	public int getStatusCode() {
		return statusCode;
	}

	public String getContent() throws ParseException, IOException {
		return content;
	}

	/**
	 * httpClient-Post请求
	 * @param url 请求地址
	 * @param params post参数
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> httpClientPost(String url, String params) throws Exception {
		org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient();
		client.getParams().setContentCharset("UTF-8");
		PostMethod httpPost = new PostMethod(url);
		try {
			RequestEntity requestEntity = new ByteArrayRequestEntity(params.getBytes("utf-8"));
			httpPost.setRequestEntity(requestEntity);
			client.executeMethod(httpPost);
			String response = httpPost.getResponseBodyAsString();
			Map<String, Object> map = new Gson().fromJson(response, Map.class);
			return map;
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			httpPost.releaseConnection();
		}
	}

	/**
	 * httpClient-Get请求
	 * @param url 请求地址
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> httpClientGet(String url) throws Exception {
		org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient();
		client.getParams().setContentCharset("UTF-8");
		GetMethod httpGet = new GetMethod(url);
		try {
			client.executeMethod(httpGet);
			String response = httpGet.getResponseBodyAsString();
			Map<String, Object> map = new Gson().fromJson(response, Map.class);
			return map;
		} catch (Exception e) {
			throw e;
		} finally {
			httpGet.releaseConnection();
		}
	}
}

package com.zcf.utils;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zhangchuanfei
 * @create 2020-04-12-23:46
 */
public class MessageUtil {

    /**
     * 解析微信发来的请求(XML)
     * @param request
     * @return map
     * @throws Exception
     */
    public static  Map<String,String> parseXml(HttpServletRequest request) throws Exception {
        // 将解析结果存储在HashMap中
        Map<String,String> map = new HashMap<String,String>();
        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();
        //System.out.println("获取输入流");
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();

        // 遍历所有子节点
        for (Element e : elementList) {
            System.out.println(e.getName() + "|" + e.getText());
            map.put(e.getName(), e.getText());
        }

        // 释放资源
        inputStream.close();
        inputStream = null;
        return map;
    }

    /**
     * 根据消息类型 构造返回消息
     */
    public static String buildXml(Map<String,String> map) {
        String result;
        String msgType = map.get("MsgType").toString();
        //System.out.println("MsgType:" + msgType);
        if(msgType.toUpperCase().equals("TEXT")){
            result = buildTextMessage(map, "亲,有什么可以帮到您的?");
        }else{
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
            result = String
                    .format(
                            "<xml>" +
                                    "<ToUserName><![CDATA[%s]]></ToUserName>" +
                                    "<FromUserName><![CDATA[%s]]></FromUserName>" +
                                    "<CreateTime>%s</CreateTime>" +
                                    "<MsgType><![CDATA[text]]></MsgType>" +
                                    "<Content><![CDATA[%s]]></Content>" +
                                    "</xml>",
                            fromUserName, toUserName, getUtcTime(),
                            "登录成功");
//        "请回复如下关键词:\n文本\n图片\n语音\n视频\n音乐\n图文");
        }
        return result;
    }

    /**
     * 构造文本消息
     *
     * @param map
     * @param content
     * @return
     */
    private static String buildTextMessage(Map<String,String> map, String content) {
        //发送方帐号
        String fromUserName = map.get("FromUserName");
        // 开发者微信号
        String toUserName = map.get("ToUserName");
        /**
         * 文本消息XML数据格式
         */
        return String.format(
                "<xml>" +
                        "<ToUserName><![CDATA[%s]]></ToUserName>" +
                        "<FromUserName><![CDATA[%s]]></FromUserName>" +
                        "<CreateTime>%s</CreateTime>" +
                        "<MsgType><![CDATA[text]]></MsgType>" +
                        "<Content><![CDATA[%s]]></Content>" + "</xml>",
                fromUserName, toUserName, getUtcTime(), content);
    }

    private static String getUtcTime() {
        Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是当前系统时间
        DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 设置显示格式
        String nowTime = df.format(dt);
        long dd = (long) 0;
        try {
            dd = df.parse(nowTime).getTime();
        } catch (Exception e) {

        }
        return String.valueOf(dd);
    }


}

package com.zcf.utils;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

/**
 * @author zhangchuanfei
 * @create 2020-04-19-10:25
 */
@Data
@AllArgsConstructor
public class Result implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 状态码
     */
    private Integer code;

    /**
     * 信息
     */
    private String info;

    /**
     * 数据
     */
    private Object data;

    public static Result ok(String info, Object data) {
        return new Result(ResultEnum.OK.getCode(), info, data);
    }

    public static Result ok() {
        return ok(null,null);
    }

    public static Result error(Integer code, String info) {
        return error(code, info,null);
    }

    public static Result error(Integer code, String info, Object data) {
        return new Result(code, info, data);
    }

    public static Result error(ResultEnum resultEnum) {
        return error(resultEnum,null);
    }

    public static Result error(ResultEnum resultEnum, Object data) {
        return error(resultEnum.getCode(), resultEnum.getInfo(),data);
    }

}

开发中需要有自己的服务器资源,我使用的是NATAPP内网穿透,使用的教程很简单,百度一下就可以了。

到这里基本上就完成了,不用多说了,登录成功之后根据个人的需要返回页面。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值