微信开发之获取OAuth2.0网页授权认证和获取用户信息进行关联

标签: 微信 开发 oauth2 | 发表时间:2015-10-16 14:36 | 作者:
出处:http://www.iteye.com


        最近有做了关于微信公众号和自己网站用户进行用户关联授权登录的一个功能,主要是用户关注该公众号,点击会员中心,则会弹出需要关联授权的网页授权:OAuth2.0网页授权,然后用户同意获取用户信息,进行用户和网站的关联,然后用户则可以使用微信进行登录。

        本次做的是一个在Java的Action层处理各个返回参数获取数据。

       一、 使用到的工具:

            1、ngrok,将你自己的本机映射到公网,这样保证可以随时测试开发;

                   1、下载ngrok,网址: http://www.tunnel.mobi/

                   2、将文件放到Tomcat目录下,在cmd中运行ngrok -config ngrok.cfg -subdomain xinzhi 8080

                   3、ngrok工具为在慕课网@LAOBI 看到的

            2、微信公众号测试账号,随时测试,首先保证在测试账号下没有问题后在进行公众号的移植。

       二、使用到在Java中发送一个Http请求,然后返回JSON参数,获得JSON参数,然后进行处理。

           首先,获取将公众号测试号放到properties文件中,以便我们进行调用或者更换,如:url请用https

   

AppID = wxf00**c3dd2ebfa0
AppSecret = 3cb220755f****506dc35391aa5c03ec
url = https://xinzhi.tunnel.mobi

 

           这里url为我们映射到外网的地址,一会需要用到。然后需要两个工具类,该工具类作用是在Java的Action中发送http请求后获取到去返回值

            这里使用的是@ 柳峰,关于服务器请求的代码 http://blog.csdn.net/lyq8479/article/details/9841371,启用自己有相应的改动,以适应本项目的需求:

           WeixinUtil.java 和 MyX509TrustManager.java

 

package com.zhtx.common.util;


import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 公众平台通用接口工具类
 * 
 * @author xinz
 * @date 2015-10-14
 */
public class WeixinUtil {
	private static Logger log = LoggerFactory.getLogger(WeixinUtil.class);

	/**
	 * 发起https请求并获取结果
	 * 
	 * @param requestUrl 请求地址
	 * @param requestMethod 请求方式(GET、POST)
	 * @param outputStr 提交的数据
	 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
	 */
	public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
		StringBuffer buffer = new StringBuffer();
		try {
			// 创建SSLContext对象,并使用我们指定的信任管理器初始化
			TrustManager[] tm = { new MyX509TrustManager() };
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			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);
			// 设置请求方式(GET/POST)
			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();
			
		} catch (ConnectException ce) {
			log.error("Weixin server connection timed out.");
		} catch (Exception e) {
			log.error("https request error:{}", e);
		}
		return buffer.toString();
	}
}

 

 对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口,代码如下:

 

package com.zhtx.common.util;


import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

/**
 * 证书信任管理器(用于https请求)
 * 
 * @author xinz
 * @date 2015-10-14
 */
public class MyX509TrustManager implements X509TrustManager {

	public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
	}

	public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
	}

	public X509Certificate[] getAcceptedIssuers() {
		return null;
	}
}

 微信返回参数的一个POJO类:

 

  private String  openid;  //用户的唯一标识 
  private String  nickname;//用户昵称 
  private Integer sex;// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 
  private String  province;//用户个人资料填写的省份 
  private String  city;//普通用户个人资料填写的城市 
  private String  country;// 国家,如中国为CN 
  private String  headimgurl;  // 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。 
  private String  privilege;// 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom) 
  private String  unionid;// 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制) 
  private String access_token;

 

 授权凭证验证的类:

 

private String errcode;
private String errmsg;

 通过code换取网页授权access_token

 

	private String access_token;
	private String expires_in;
	private String refresh_token;
	private String openid;
	private String scope;
	private String unionid;

 关于微信头像的,获取的是一个http的url,则需要将图片下载到服务器存储,然后获得相对路径:

 

/**
	 * 使用url或者http存入文件
	 * @Title: fileUpload
	 * @param @param fileUrl  文件url,可以是http
	 * @param @param path     文件存储路径
	 * @return void
	 * @throws xinz
	 */
	public static void fileUpload (String fileUrl,String path){
		 //读取文件
		  String s1 = fileUrl;   
		  java.io.InputStream is = null; //定义一个输入流。
		  BufferedInputStream bis = null;//定义一个带缓冲的输入流 。 
		//写到本地 
		  BufferedOutputStream bos = null; //定义一个带缓冲的输出流。
		  try{ 
			java.net.URL url = new java.net.URL(s1);//创建一个URL对象。
		  	is = url.openStream();//打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream。
		  	bis = new java.io.BufferedInputStream(is);     
		    File file = new File(path);   
			if(!file.exists()){ //测试此抽象路径名表示的文件或目录是否存在。  
				file.createNewFile();   //创建此抽象路径名表示的文件或目录。
			}   
		  bos = new BufferedOutputStream(new FileOutputStream(file));;     
		  byte[] b = new byte[1024]; //创建字节数组。
		  while(bis.read(b)!=-1){//输入流中的数据如果还有下一行(!=-1)将继续循环
			  bos.write(b);//将字节数组写入输出流。    
		  } 
		  }catch(Exception   e){     
			  System.out.println(e.toString());       
		  }finally{     
			  try{     
				  bos.flush();//刷新此缓冲的输出流。 
				  bis.close(); //关闭此输入流 。 
			  }catch(Exception   e){     
				  System.out.println(e.toString());       
			  }     
		  }  
	}

 

现在是基础工作都做完了,现在开发代码的开发,在微信开发文档中  http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html 有提到每一个步骤,然后我们按照这个步骤开发:

 

第一步:用户同意授权,获取code

  这里的url就是前面所准备在properties中的url了。

 

/**
	 * 微信用户授权
	 * @Title: wechatOauth
	 * @param @param request
	 * @param @param response
	 * @param @param model
	 * @param @return
	 * @return String
	 * @throws xinz
	 */
	@RequestMapping("wechatOauth")
	public String wechatOauth(HttpServletRequest request,HttpServletResponse response,Model model)  {
		/**
		 *  1 第一步:用户同意授权,获取code
		 */
		//首先拿到微信公众号的AppID、AppSecret等参数
		String AppID = ZhtxHelper.getApplicationResourcesProp("sendSms","AppID");
		String urlOpen = ZhtxHelper.getApplicationResourcesProp("sendSms","url");
		//如果用户授权成功则跳转到此url
		String loginUrl = ""+urlOpen+"/zhtx-wap/weixin/getAccessToken";
		//用户授权,获取code
		String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"
					+ "appid="+AppID+""
					+ "&redirect_uri="+loginUrl+""
					+ "&response_type=code"
					+ "&scope=snsapi_userinfo"
					+ "&state=123#wechat_redirect";
		//forward redirect
		return "redirect:"+url+""; 
	}

 

第二步:通过code换取网页授权access_token

 

/**
	 * 通过code换取网页授权access_token
	 * @Title: getAccessToken
	 * @param @param request
	 * @param @param response
	 * @param @param model
	 * @param @return
	 * @return String
	 * @throws xinz
	 */
	@RequestMapping("getAccessToken")
	public String getAccessToken(HttpServletRequest request,HttpServletResponse response,Model model) {
		//获取到返回的参数
		try {
			//首先拿到微信公众号的AppID、AppSecret等参数
			String AppID = ZhtxHelper.getApplicationResourcesProp("sendSms","AppID");
			String AppSecret = ZhtxHelper.getApplicationResourcesProp("sendSms","AppSecret");
			String code = request.getParameter("code");
			String url = null;
			if(code!=null){
				/**
				 *  2 第二步:通过code换取网页授权access_token
				 */
				//用户授权,获取code
				url = "https://api.weixin.qq.com/sns/oauth2/access_token?"
						+ "appid="+AppID+""
						+ "&secret="+AppSecret+""
						+ "&code="+code+""
						+ "&grant_type=authorization_code";
				String requestMethod = "GET";
				String outputStr = "";
				String httpRequest = WeixinUtil.httpRequest(url, requestMethod, outputStr);
				
				System.out.println("通过code换取网页授权access_token="+httpRequest);
				
				AccessTokenModel accTok = JSON.parseObject(httpRequest, AccessTokenModel.class);
				/**
				 *  4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
				 */	                                                                      
				//用户授权,获取code
				String urlUser = "https://api.weixin.qq.com/sns/userinfo?"
						+ "access_token="+accTok.getAccess_token()+""
						+ "&openid="+accTok.getOpenid()+""
						+ "&lang=zh_CN";
				
				String httpUser = WeixinUtil.httpRequest(urlUser, requestMethod, outputStr);
				System.out.println("拉取用户信息=="+httpUser);
				
				WechatUser wechatUser = JSON.parseObject(httpUser, WechatUser.class);
				wechatUser.setAccess_token(accTok.getAccess_token());
				/**
				 *  5 附:检验授权凭证(access_token)是否有效
				 */
				WechatMsg checkAccessToken = checkAccessToken(wechatUser.getAccess_token(), wechatUser.getOpenid());
				if(checkAccessToken.getErrcode().equals("0")){
					CurrentSession.setAttribute("wechatUser", wechatUser);
					WechatUser wechatU = new WechatUser();
					wechatU.setOpenid(wechatUser.getOpenid());
					List<WechatUser> findWechatUser = wechatUserService.findWechatUser(wechatU);
					if(findWechatUser.size()>0){
						UserRegister userRegister = userService.findUserByOpenid(wechatUser.getOpenid());
						CurrentSession.setAttribute("user", userRegister);
						return "redirect:/user/userCenter";
					}else{
						
						return "/jsp/wechat/wechatregister"; 
					}
					
				}else{
					//如果access_token失效,则再次进行调用,并存储access_token值,access_token有效期为2个小时
					this.wechatOauth(request, response, model); 
				}
			}
		} catch (Exception e) {
			System.out.println("===拉取用户出错===");
			e.printStackTrace();
		}
		//forward redirect
		return "/jsp/wechat/wechatregister"; 
	}

 

第四步:拉取用户,和自己网站用户绑定

/**
	 * 微信关联用户
	 * @Title: saveWechatUser
	 * @param @param mobilePhone
	 * @param @param password
	 * @param @param validataCode
	 * @param @return
	 * @return String
	 * @throws xinz
	 */
	@RequestMapping("saveWechatUser")
	public String saveWechatUser(HttpServletResponse response,String mobilePhone,String password,String validataCode){
		//使用手机号来判断该手机是否在注册
		UserRegister userRegister = userService.findUserByPhone(mobilePhone);
		WechatUser wechatUser = (WechatUser)CurrentSession.getAttribute("wechatUser");
		WechatUser wechatU = new WechatUser();
		wechatU.setOpenid(wechatUser.getOpenid());
		List<WechatUser> findWechatUser = wechatUserService.findWechatUser(wechatU);
		if(findWechatUser.size()>0 && userRegister.getOpenid()!=null){
			CurrentSession.setAttribute("user", userRegister);
			return "redirect:/user/userCenter";
		}else{
			//如果没有注册,开始注册
			if(userRegister==null){
				Result<UserRegister> saveUserInfoApp = userRegisterService.saveUserInfoApp(mobilePhone, password, validataCode,wechatUser);
				if(saveUserInfoApp.getState()==1){
					//进行微信和用户的关联
					wechatUserService.saveWechatUser(wechatUser);
					CurrentSession.setAttribute("user", userRegister);
					return "redirect:/user/userCenter";
				}
			}else if(userRegister.getOpenid()==null || userRegister.getOpenid().equals("")){
			//否则,查询出用户信息,放入session中,关联微信,跳转到用户中心	
				UserRegister userReg = new UserRegister();
				userReg.setId(userRegister.getId());
				//存入微信openid
				userReg.setOpenid(wechatUser.getOpenid());
				userService.upUser(userReg);
				UserInfo user = new UserInfo();
				//存入微信头像
				//图片类型
				String dateStr =DateUtil.format(DateUtil.getCurrentDate(), "yyyyMMdd")  + "/";
				//图片类型
				String imgType = "JPG";
				//微信头像名称
				String app2DBarNameAndType = UuidUtil.getUUID()+"."+imgType;
				//微信头像路径
				String path =   ZhtxHelper.getApplicationResourcesProp("application","app.img.projectpath")+ SysConstant.GOODS2DBARPATH + dateStr;
				File file1 = new File(path);
				file1.mkdirs();
				//图片全路径
				String imgUrl = SysConstant.GOODS2DBARPATH + dateStr+app2DBarNameAndType;
				FileUtil.fileUpload(wechatUser.getHeadimgurl(), path);
				user.setRegisterId(userRegister.getId());
				user.setImageUrl(imgUrl);
				userInfoService.updateUserInfo(user);
				//存入微信用户
				wechatUserService.saveWechatUser(wechatUser);
				
				UserRegister userW = userService.findUserByPhone(mobilePhone);
				CurrentSession.setAttribute("user", userW);
				return "redirect:/user/userCenter";
			}else{
				CurrentSession.setAttribute("user", userRegister);
				return "redirect:/user/userCenter";
			}
		}
		return "redirect:/user/userCenter";
	}

 

附:检验授权凭证(access_token)是否有效

 

/**
	 * 检验授权凭证(access_token)是否有效
	 * @Title: checkAccessToken
	 * @param @param access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同 
	 * @param @param openid 用户的唯一标识 
	 * @return WechatMsg   返回消息实体
	 * @throws xinz
	 */
	public static WechatMsg checkAccessToken(String access_token,String openid){
		 String requestMethod = "GET";
		 String outputStr = "";	
		 String url = "https://api.weixin.qq.com/sns/auth?"
		 		+ "access_token="+access_token+""
		 		+ "&openid="+openid+"";
		 String httpmsg = WeixinUtil.httpRequest(url, requestMethod, outputStr);
		 System.out.println("拉取用户信息返回消息=="+httpmsg);
			
		 WechatMsg msg = JSON.parseObject(httpmsg, WechatMsg.class);
	    
		 return msg;
	}

 然后在网页端,则是需要编写H5页面,进行自己网站和微信用户的关联,我这里是使用手机号,用户输入手机号,进行判断,如果注册过就直接关联,如果用户没有注册则进行注册后关联,完成后跳转到会员中心。

 

 

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [微信 开发 oauth2] 推荐:

SPRING BOOT OAUTH2 + KEYCLOAK - service to service call

- - BlogJava-首页技术区
employee-service调用department-service,如果要按OAUTH2.0流程,只需要提供client-id和client-secrect即可. 在KEYCLOAK中引入service-account,即配置该employee-service时,取消standard-flow,同时激活service-account.

【转】oauth2开放认证协议原理及案例分析

- - 研发管理 - ITeye博客
OAuth认证协议原理分析及使用方法. ,虽然 OAuth2还没有正式发布,但是国内外的OAuth2的采用情况几乎要完全替代掉OAuth1.1了. 像淘宝、腾讯、人人网、百度开放平台就已经采用Oauth2,新浪微博也发来邮件说是要很快上马OAuth2,彻底替换掉OAuth1.1. ,最新的版本是 2011年7月25号发布的,协议变化还是很快的,所以看到国内的一些已经实现的实例,再比照官方的 oauth2,会有些出入的.

Spring security oauth2最简单入门环境搭建--二、干货

- - ITeye博客
关于OAuth2的一些简介,见我的上篇blog: http://wwwcomy.iteye.com/blog/2229889 PS:貌似内容太水直接被鹳狸猿干沉. 友情提示 学习曲线:spring+spring mvc+spring security+Oauth2基本姿势,如果前面都没看过请及时关闭本网页.

使用OAUTH2+Zuul实现认证和授权 GitHub - wiselyman/uaa-zuul:

- -
在 Spring Cloud需要使用 OAUTH2来实现多个微服务的统一认证授权,通过向 OAUTH服务发送某个类型的 grant type进行集中认证和授权,从而获得 access_token,而这个token是受其他微服务信任的,我们在后续的访问可以通过 access_token来进行,从而实现了微服务的统一认证授权.

SpringBoot 整合 oauth2(三)实现 token 认证 - 简书

- -
关于session和token的使用,网上争议一直很大. session是空间换时间,而token是时间换空间. session占用空间,但是可以管理过期时间,token管理部了过期时间,但是不占用空间.. sessionId失效问题和token内包含. session基于cookie,app请求并没有cookie.

使用Spring Security Oauth2完成RESTful服务password认证的过程 - 王安琪

- - 博客园_首页
        摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息、ClientDetails、token存入数据库而非内存. 配置过程比较复杂,经过几天时间试验终于成功,下面我将具体的使用Spring Security Oauth2完成password认证的过程记录下来与大家分享.

微信开发入门

- - 行业应用 - ITeye博客
【做微信平台开发需要以下步骤,wx.zip示例可以参考,修改配置即可】. 1.申请一个公众号(订阅号或者服务号). 2.需要有自己的服务器(建议使用花生壳做内网映射). * 检查是否是微信发送的请求. * @param signature 签名. * @param timestamp 时间戳. * @param nonce 随机数.

微信公众平台开发(一)

- - BlogJava-首页技术区
  开始微信公众平台的开发,我们首先要了解微信平台可以帮助我们做哪些事情. 使用您的公众账号登陆http://mp.weixin.qq.com/,选择菜单--高级功能-开发模式--查看文档,即能看到微信公众平台目前所能开发的功能. 接受用户发送给您公众账号的消息. 需要特别说明的是,发送消息和回复消失是一个连贯的过程,只能在一个对话中完成.

如何成为微信开发者

- - 神刀网
要成为微信开发者,准备工作如下:. a.这个可以自己购买,如果之前已有网站,可直接使用其服务器,这样也不用再购买域名了. b.也可以使用免费的服务器,搜索一下,有很多. c.还可以使用 百度云开发平台或者 新浪云平台,是免费的. a.如果服务器自己购买,也需要购买域名,然后要设置域名DNS,将域名绑定到购买的服务器.

微信公众平台接口开发

- - CSDN博客互联网推荐文章
随着微信公众平台的开放,微信营销推广也越发受到重视. 现在企业越来越注重求职者是否拥有“微信公众平台接口开发”的经验. 现在参考资料介绍下微信公众平台接口开发模式:. 首先你得有个微信公众平台账号,注册地址:http://mp.weixin.qq.com/. 开发者提交信息后,微信服务器将发送GET请求到填写的URL上,GET请求携带四个参数:.