微信扫码登录实现方式有多中,本次介绍的是利用带场景值的二维码去实现扫码登录
流程大致是
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 "";
}