2014-03-06 21:46:03 hemeng 阅读数 5626
  • 19年录制SpringBoot2.x整合微信支付在线教育网站项目...

    SpringBoot2.x开发在线教育微信支付项目实战,2019年新录制,课程分为11章63节课,从需求评审到设计数据库,编码,单元测试,Nginx集群部署;整合各种正式开发的工作技巧; IDEA工具热部署,Ngrock本地域名映射,数据库逆向工程生成实体类,动态Sql,微信OAuth2一键登录,网页微信扫码支付,JWT微服务登录鉴权,阿里云集群部署,公网域名解析配置,前端页面接口动静分离

    1404 人正在学习 去看看 张颜源

我们首先来认识一下OAuth协议吧,这个东西很早就听说过,总觉得离我很远(我的项目用不到这些),但是最近不得不学习一下了。我在网上找了一些解释,认为解释的最好的是这样说的(出处:http://hi.baidu.com/powerthinks/item/f1cb9b3c7a88251c9dc65efa

     如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间。是否有好办法可以避免这个问题呢?有的,听说有一些豪车的车主就不担心这个问题。豪车一般配备两种钥匙:主钥匙和泊车钥匙。当你到酒店后,只需要将泊车钥匙交给服务生,停车的事情就由服务生去处理。与主钥匙相比,这种泊车钥匙的使用功能是受限制的:它只能启动发动机并让车行驶一段有限的距离,可以锁车,但无法打开后备箱,无法使用车内其他设备。这里就体现了一种简单的“开放授权”思想:通过一把泊车钥匙,车主便能将汽车的部分使用功能(如启动发动机、行驶一段有限的距离)授权给服务生。

    授权是一个古老的概念,它是一个多用户系统必须支持的功能特性。比如,Alice和Bob都是Google的用户,那么Alice应该可以将自己的照片授权给Bob访问。但请注意到,这种授权是一种封闭授权,它只支持系统内部用户之间的相互授权,而不能支持与其他外部系统或用户之间的授权。比如说,Alice想使用“网易印像服务”将她的部分照片冲印出来,她怎么能做到呢?

肯定有人会说,Alice可以将自己的Google用户名和密码告诉网易印像服务,事情不就解决了吗?是的,但只有毫不关注安全和隐私的同学才会出此“绝招”。那么我们就来想一想,这一“绝招”存在哪些问题?

(1) 网易印像服务可能会缓存Alice的用户名和密码,而且可能没有加密保护。它一旦遭到攻击,Alice就会躺着中枪。

(2) 网易印像服务可以访问Alice在Google上的所有资源,Alice无法对他们进行最小的权限控制,比如只允许访问某一张照片,1小时内访问有效。

(3) Alice无法撤消她的单个授权,除非Alice更新密码。

在以Web服务为核心的云计算时代,像用户Alice的这种授权需求变得日益迫切与兴盛,“开放授权(Open Authorization)”也正因此而生,意在帮助Alice将她的资源授权给第三方应用,支持细粒度的权限控制,并且不会泄漏Alice的密码或其它认证凭据。

  

上面的例子写的通俗易懂,各位一看就明白了,后面作者还附了一个这样图,以及注释:

 

从引言部分的描述我们可以看出,OAuth的参与实体至少有如下三个:

· RO (resourceowner): 资源所有者,对资源具有授权能力的人。如上文中的用户Alice。

· RS (resourceserver): 资源服务器,它存储资源,并处理对资源的访问请求。如Google资源服务器,它所保管的资源就是用户Alice的照片。

· Client: 第三方应用,它获得RO的授权后便可以去访问RO的资源。如网易印像服务。

此外,为了支持开放授权功能以及更好地描述开放授权协议,OAuth引入了第四个参与实体:

· AS(authorization server): 授权服务器,它认证RO的身份,为RO提供授权审批流程,并最终颁发授权令牌(Access Token)。读者请注意,为了便于协议的描述,这里只是在逻辑上把AS与RS区分开来;在物理上,AS与RS的功能可以由同一个服务器来提供服务。

 

如图1所示,协议的基本流程如下:

(1) Client请求RO的授权,请求中一般包含:要访问的资源路径,操作类型,Client的身份等信息。

(2) RO批准授权,并将“授权证据”发送给Client。至于RO如何批准,这个是协议之外的事情。典型的做法是,AS提供授权审批界面,让RO显式批准。这个可以参考下一节实例化分析中的描述。

(3) Client向AS请求“访问令牌(AccessToken)”。此时,Client需向AS提供RO的“授权证据”,以及Client自己身份的凭证。

(4) AS验证通过后,向Client返回“访问令牌”。访问令牌也有多种类型,若为bearer类型,那么谁持有访问令牌,谁就能访问资源。

(5) Client携带“访问令牌”访问RS上的资源。在令牌的有效期内,Client可以多次携带令牌去访问资源。

(6) RS验证令牌的有效性,比如是否伪造、是否越权、是否过期,验证通过后,才能提供服务。

 

虽然上面的例子举的很好,图画的也很清楚,可是对于我这种愚钝的人,还是理解了半天,请允许我在此狗尾续貂一次吧:

其实上面的图就如同这样一个故事

1、我的一个同事小张(Client),向我或者公司(RO)借钱,他会给我说:“借给我2000元,可以吗?”(也就是说向我请求一个有限的资源,就是Authorization Request);

2、这时我肯定不会把我的银行卡给他,并告诉他密码,我要用到OAuth。首先我会说“好的”,并且给他写一个条,上面写着“允许小张借款2000元”的请求(Authorization Grant);

3、小张拿着我写的条(AuthorizationGrant),给财务总监(AS);

4、财务总监看到我的条后会给小张一个提款单(Access Token),上面写着请出纳给小张支出2000元的借款。

5、小张拿到了提款单(AccessToken)到出纳那里,按照公司的规定,出纳只有看到了财务总监给的提款单(Access Token),才能过支出钱,这是出纳核对后,小张就顺利的拿到了2000元钱(Protected Resource)。

 

根据上面的故事我们来说已说OAuth的特点:

1、小张不可能知道,我或者公司总共有多少钱;

2、我也保护了账户的密码,维护的公司的制度;

3、就像现实中一样,财务总监和出纳一般都在一起办公,而AS和RS一般也会在同一个服务器上;

4、按照同样的公司制度(也就是OAuth),其他人不管谁来支出钱都要这样,我只需要保证制度执行的严密,其他任何人都不会知道我的帐务信息了。

这也就是OAuth全称--- OpenAuthorization(开放式授权)的意义了。

 

下面我来看看为微信中是如何实现这个过程的,我引用了方倍工作室教程里面的图(http://www.cnblogs.com/txw1958/p/weixin71-oauth20.html):

 

在这个途中User就是我例子中的小张,APP就是我,Auth_svr就是财务总监和出纳。

如果我使用微信中的官法提法因该这么说:

1、小张向我提出访问,访问过程涉及以下参数:


   也就是要知道我的APPID:其实就是我的名字;redirect_uri:到财务那里怎么走;response_type:知道我想我要批条(请填写code);scope批条的类别(后面会介绍到);state:到财务那里带些什么(参数)。

 

2、我同意了他的要求

  也就是微信帮助提到的“如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。”也就是我给了小张一个批条(code),告诉小张去财务的路,以及到那里需要带些什么。

 

3、小张拿着我的批条到财务总监那里得到提款单

小张拿着我的批条(code),按照“redirect_uri/?code=CODE&state=STATE”这个路径去财务那里“通过code换取网页授权access_token”


上图可以知道我还可以获得OPENID!这个是非常重要的,前一章提到了它的用处,微信在这里还特别强调了“获取用户基本信息接口是在用户和公众号产生消息交互时,才能根据用户OpenID获取用户基本信息,而网页授权的方式获取用户基本信息,则无需消息交互,只是用户进入到公众号的网页,就可弹出请求用户授权的界面,用户授权后,就可获得其基本信息(此过程甚至不需要用户已经关注公众号。)”

 

4、小张拿到了提款单又到出纳那里提款

得到了access_token后,拉取用户信息,也就是“通过access_token和openid拉取用户信息了。”


最终得到是如下的信息:

 

本来就此已经说完了,不过不少研发人员可能还是会有疑问:“费这么半天劲,不就得到了个用户的基本信息嘛,我通过其他方式也可以得到!”。

其实不然,这还是非常有用的:

第一,这是唯一一个重不需要关注你的微信好就可获得用户信息的办法;

第二,我们来设想一下下面场景:如果我们开发一个系统想过没想过不要用户名和密码了?用户登录,并授权以后,进入我们的系统,我系统里面存放着这个微信号(OPENID)对应的权限的功能。这样我再也不用担心用户忘记密码或者密码泄露的事情了,因为这些事情微信已经帮助给做了。说到这里可能你还会问:“那岂不是每个人必须有微信号”,我先不说这里我们做的就是微信平台的应用,也不说腾讯有多大的野心,这种OAuth协议已经在微博,google,baidu,邮箱等广泛应用,我做他们响应的接口,你就说一个上用你系统的人,没有微信还不能有微博,google,baidu这些帐号,没有这些帐号你还能没有个邮箱?这些我觉得因该是以后的趋势。

何况,随着微信的完善,以后或许我们拿到的不仅是用户的基本信息,还有用户朋友圈的帖子,图片,用户的喜好等等,这些都为我们新开发的系统提供了有效的可分析的数据!

2019-01-30 20:16:19 chailihua0826 阅读数 107
  • 19年录制SpringBoot2.x整合微信支付在线教育网站项目...

    SpringBoot2.x开发在线教育微信支付项目实战,2019年新录制,课程分为11章63节课,从需求评审到设计数据库,编码,单元测试,Nginx集群部署;整合各种正式开发的工作技巧; IDEA工具热部署,Ngrock本地域名映射,数据库逆向工程生成实体类,动态Sql,微信OAuth2一键登录,网页微信扫码支付,JWT微服务登录鉴权,阿里云集群部署,公网域名解析配置,前端页面接口动静分离

    1404 人正在学习 去看看 张颜源
2017-01-19 16:48:53 doc_wei 阅读数 863
  • 19年录制SpringBoot2.x整合微信支付在线教育网站项目...

    SpringBoot2.x开发在线教育微信支付项目实战,2019年新录制,课程分为11章63节课,从需求评审到设计数据库,编码,单元测试,Nginx集群部署;整合各种正式开发的工作技巧; IDEA工具热部署,Ngrock本地域名映射,数据库逆向工程生成实体类,动态Sql,微信OAuth2一键登录,网页微信扫码支付,JWT微服务登录鉴权,阿里云集群部署,公网域名解析配置,前端页面接口动静分离

    1404 人正在学习 去看看 张颜源

根据官方文档,在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头,最新的要在域名空间的根目录放一个叫MP_verify_dTx4Hrh6cZDHUEgH.txt才能验证通过。(可能是最近加进去的,之前测试的没发现)


两种scope授权方式:

1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息


具体文档详见https://mp.weixin.qq.com/wiki/4/9ac2e7b1f1d22e9e57260f6553822520.html


1.用户同意授权,获取code

我这边是snsapi_userinfo发起的网页授权

我是直接在菜单测试的,或者你直接推送给测试用户都行,反正能打开这个链接就行了

redirect_uri必须用urlEncode处理下

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. ViewButton btn12 = new ViewButton();    
  2.         btn12.setName("授权测试");      
  3.         btn12.setType("view");  
  4.         String redirect_uri = "http://域名/项目名/oauth";  
  5.         String redirect_uris = null;  
  6.         try {  
  7.             redirect_uris = URLEncoder.encode(redirect_uri,"utf-8");  
  8.             logger.info(redirect_uris);    
  9.         } catch (UnsupportedEncodingException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.         String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=appid&redirect_uri="+redirect_uris+"&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect";  
  13.         logger.info(url);         
  14.         btn12.setUrl(url);  

用户打开之后,如果同意的话,可以用request.getParameter("code")直接获取到code。code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. @Controller  
  2. @RequestMapping("/oauth")  
  3. public class OAuthController {  
  4.       
  5.     private static final Logger logger = Logger.getLogger(OAuthController.class);  
  6.       
  7.     @Autowired  
  8.     private OAuthService oAuthService;  
  9.       
  10.     @RequestMapping(method = { RequestMethod.GET })  
  11.     public String processGet(HttpServletRequest request, HttpServletResponse response, String url) throws Exception{      
  12.         //request.setCharacterEncoding("gb2312");  
  13.         //response.setCharacterEncoding("gb2312");    
  14.         //用户同意授权后可以获得code  
  15.         String code = request.getParameter("code");  
  16.         logger.info("用户同意授权后的code: "+code);   
  17.         //用户同意授权  
  18.         if(!"authdeny".equals(code)){  
  19.             //获取网页授权access_token  
  20.             Oauth2Token oauth2Token = oAuthService.getOauth2AccessToken(WechatConfig.APP_ID, WechatConfig.APP_SECRET, code);  
  21.             //获取用户信息  
  22.             SNSUserInfo suser = oAuthService.getSNSUserInfo(oauth2Token.getAccessToken(), oauth2Token.getOpenId());  
  23.             if(suser!=null){              
  24.                 //执行保存绑定                  
  25.                 String openid = suser.getOpenid();  
  26.                 logger.info("绑定的openid="+openid);                 
  27.                 request.setAttribute("suser", suser);  
  28.             }  
  29.         }  
  30.         //  
  31.         return "test/test"//转发到指定的url  
  32.     }  
  33. }  

2.通过code换取网页授权access_token

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public class Oauth2Token {  
  2.     //网页授权接口调用凭证  
  3.     private  String accessToken;  
  4.     //凭证有效时长  
  5.     private int expiresIn;  
  6.     //用于刷新凭证  
  7.     private String refreshToken;  
  8.     //用户标识  
  9.     private String openId;  
  10.     //用户授权作用域  
  11.     private String scope;  
  12.       
  13.     public String getAccessToken() {  
  14.         return accessToken;  
  15.     }  
  16.     public void setAccessToken(String accessToken) {  
  17.         this.accessToken = accessToken;  
  18.     }  
  19.     public int getExpiresIn() {  
  20.         return expiresIn;  
  21.     }  
  22.     public void setExpiresIn(int expiresIn) {  
  23.         this.expiresIn = expiresIn;  
  24.     }  
  25.     public String getRefreshToken() {  
  26.         return refreshToken;  
  27.     }  
  28.     public void setRefreshToken(String refreshToken) {  
  29.         this.refreshToken = refreshToken;  
  30.     }  
  31.     public String getOpenId() {  
  32.         return openId;  
  33.     }  
  34.     public void setOpenId(String openId) {  
  35.         this.openId = openId;  
  36.     }  
  37.     public String getScope() {  
  38.         return scope;  
  39.     }  
  40.     public void setScope(String scope) {  
  41.         this.scope = scope;  
  42.     }  
  43. }  
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * 获取网页授权凭证 
  3.  * @param appId 公众账号的唯一标识 
  4.  * @param appSecret 公众账号的密钥 
  5.  * @param code 
  6.  * @return 
  7.  */  
  8. public Oauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {  
  9.     Oauth2Token ot = null;  
  10.     String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";  
  11.     requestUrl = requestUrl.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);       
  12.     logger.info("requestUrl"+requestUrl);     
  13.     //获取网页授权凭证  
  14.     JSONObject jsonObject = HttpRequestUtil.httpRequestJSONObject(requestUrl, HttpRequestUtil.GET_METHOD, null);  
  15.     if(null != jsonObject){  
  16.         try {  
  17.             ot = new Oauth2Token();  
  18.             ot.setAccessToken(jsonObject.getString("access_token"));  
  19.             ot.setExpiresIn(jsonObject.getInt("expires_in"));  
  20.             ot.setRefreshToken(jsonObject.getString("refresh_token"));  
  21.             ot.setOpenId(jsonObject.getString("openid"));  
  22.             ot.setScope(jsonObject.getString("scope"));  
  23.         } catch (Exception e) {  
  24.             int errorCode = jsonObject.getInt("errcode");  
  25.             String errorMsg = jsonObject.getString("errmsg");  
  26.             logger.info("获取网页授权凭证失败,错误码:"+errorCode+",错误提示:"+errorMsg);  
  27.         }  
  28.     }  
  29.     return ot;  
  30. }  
3.刷新access_token(如果需要)

由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。

获取第二步的refresh_token后,请求以下链接获取access_token:  
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN ,需要的话自己替换下URL就好了

4.拉取用户信息

如果是scope是snsapi_base,拉取用户的openid; 如果scope是 snsapi_userinfo,可以拉取到全部信息

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public class SNSUserInfo {  
  2.     //用户标识  
  3.     private String openid;  
  4.     //用户昵称  
  5.     private String nickname;  
  6.     //性别(1是男性,2是女性,0是未知)  
  7.     private String sex;  
  8.     //国家  
  9.     private String country;  
  10.     //省份  
  11.     private String province;  
  12.     //城市  
  13.     private String city;  
  14.     //用户头像链接  
  15.     private String headimgurl;  
  16.     //用户特权信息  
  17.     private List<String> privilege;  
  18.     //只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段   
  19.     private String unionid;  
  20.       
  21.     public String getOpenid() {  
  22.         return openid;  
  23.     }  
  24.     public void setOpenid(String openid) {  
  25.         this.openid = openid;  
  26.     }  
  27.     public String getNickname() {  
  28.         return nickname;  
  29.     }  
  30.     public void setNickname(String nickname) {  
  31.         this.nickname = nickname;  
  32.     }  
  33.     public String getSex() {  
  34.         return sex;  
  35.     }  
  36.     public void setSex(String sex) {  
  37.         this.sex = sex;  
  38.     }  
  39.     public String getCountry() {  
  40.         return country;  
  41.     }  
  42.     public void setCountry(String country) {  
  43.         this.country = country;  
  44.     }  
  45.     public String getProvince() {  
  46.         return province;  
  47.     }  
  48.     public void setProvince(String province) {  
  49.         this.province = province;  
  50.     }  
  51.     public String getCity() {  
  52.         return city;  
  53.     }  
  54.     public void setCity(String city) {  
  55.         this.city = city;  
  56.     }  
  57.     public String getHeadimgurl() {  
  58.         return headimgurl;  
  59.     }  
  60.     public void setHeadimgurl(String headimgurl) {  
  61.         this.headimgurl = headimgurl;  
  62.     }  
  63.     public List<String> getPrivilege() {  
  64.         return privilege;  
  65.     }  
  66.     public void setPrivilege(List<String> privilege) {  
  67.         this.privilege = privilege;  
  68.     }  
  69.     public String getUnionid() {  
  70.         return unionid;  
  71.     }  
  72.     public void setUnionid(String unionid) {  
  73.         this.unionid = unionid;  
  74.     }  
  75. }  

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * 通过网页授权获取用户信息 
  3.  * @param oauth2Token 
  4.  * @return 
  5.  */  
  6. public SNSUserInfo getSNSUserInfo(String accessToken, String openId) {  
  7.     SNSUserInfo suser = null;  
  8.     String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";  
  9.     requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);       
  10.     //通过网页授权获取用户信息  
  11.     JSONObject jsonObjet = HttpRequestUtil.httpRequestJSONObject(requestUrl, HttpRequestUtil.GET_METHOD, null);  
  12.     if(null != jsonObjet){  
  13.         try {  
  14.             suser = new Gson().fromJson(jsonObjet.toString(), SNSUserInfo.class);  
  15.         } catch (JsonSyntaxException e) {  
  16.             logger.info("transfer exception");  
  17.             throw e;                  
  18.         }   
  19.     }  
  20.     return suser;  
  21. }  

获取到SNSUserInfo之后,可以选择与数据库的用户绑定。

如果想解绑网站账号的话,可以学中信的公众号一样加个安全设置再解绑,相应的把openid从关联的用户表删除,可以加个绑定和解绑的时间日志。


问:如果PC端网站想绑定微信等第三方,把第一步的那个url作为场景ID生成一个临时的带参二维码,扫描之后输入网站的用户名密码就可以与公众号绑定了。试问是这样做的吗?

2015-03-16 11:51:15 lenovouser 阅读数 1958
  • 19年录制SpringBoot2.x整合微信支付在线教育网站项目...

    SpringBoot2.x开发在线教育微信支付项目实战,2019年新录制,课程分为11章63节课,从需求评审到设计数据库,编码,单元测试,Nginx集群部署;整合各种正式开发的工作技巧; IDEA工具热部署,Ngrock本地域名映射,数据库逆向工程生成实体类,动态Sql,微信OAuth2一键登录,网页微信扫码支付,JWT微服务登录鉴权,阿里云集群部署,公网域名解析配置,前端页面接口动静分离

    1404 人正在学习 去看看 张颜源

微信公众平台开发—利用OAuth2.0获取微信用户基本信息

  (2014-06-21 14:53:20)
标签: 

微信

 

用户信息

 

access

 

token

 

oauth2.0

分类: .net(c#)

在借鉴前两篇获取微信用户基本信息的基础下,本人也总结整理了一些个人笔记:如何通过OAuth2.0获取微信用户信息

 

1、首先在某微信平台下配置OAuth2.0授权回调页面:

2、通过appid构造url获取微信回传code值(appid可在微信平台下找到)

1)、微信不弹出授权页面url:

A、code回传到页面wxProcess2.aspx,不带参数

Response.Redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=http://localhost:8888/wxProcess2.aspx&response_type=code&scope=snsapi_base&state=1#wechat_redirect");  

B、code回传到页面wxProcess2.aspx,带参数reurl,即wxProcess2.aspx获得code的同时,也能获取reurl的值,具体如下:

Response.Redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=http://localhost:8888/wxProcess2.aspx?reurl=" + reurl + "&response_type=code&scope=snsapi_base&state=1#wechat_redirect");

2)、微信弹出授权页面url:需要用户授权,才能获取code及后面需要获取的用户信息

Response.Redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=http://localhost:8888/wxProcess2.aspx?reurl=" + reurl + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect");

 

说明:微信是否弹出授权页面url的区别只在一个参数scope,不弹出微信授权页面:scope=snsapi_base,弹出微信授权页面:scope=snsapi_userinfo。

微信授权页面如下:

 

3、通过appid、secret、code构造url,获取微信用户的openid和access token。appid、secret可在微信平台下找到,code已在上面方法中获取并回传。具体访问url:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + appsecret + "&code=" + Code + "&grant_type=authorization_code

 

4、通过openid、access token获取用户信息,具体访问url:

https://api.weixin.qq.com/sns/userinfo?access_token=" + REFRESH_TOKEN + "&openid=" + OPENID

 

说明:主要通过访问微信的3个url地址并回传数据,获取微信用户基本信息

 

====================================================================

具体代码:

1、获取微信code处理页面:wxProcess.aspx

protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            string reurl = "";
            //传递参数,获取用户信息后,可跳转到自己定义的页面,想怎么处理就怎么处理
            if (Request.QueryString["reurl"] != null && Request.QueryString["reurl"] != "")
            {
                reurl = Request.QueryString["reurl"].ToString();
            }
            else
            {
                reurl = "http://www.csdn.net";
            }

            string code = "";

            //弹出授权页面(如在不弹出授权页面基础下未获得openid,则弹出授权页面,提示用户授权)
            if (Request.QueryString["auth"] != null && Request.QueryString["auth"] != "" && Request.QueryString["auth"] == "1")
            {
                Response.Redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=http://localhost:8888/wxProcess2.aspx?reurl=" + reurl + "&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect");
            }
            else
            {
                //不弹出授权页面
                Response.Redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=http://localhost:8888/wxProcess2.aspx?reurl=" + reurl + "&response_type=code&scope=snsapi_base&state=1#wechat_redirect");            }
        }
    }

 

2、获取微信code值回传到自己的页面wxProcess2.aspx:

public string reurl = "";
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            //获取从wxProcess.aspx传递过来的跳转地址reurl
            if (Request.QueryString["reurl"] != null && Request.QueryString["reurl"] != "")
            {
                reurl = Request.QueryString["reurl"].ToString();
            }

            string code = "";
            if (Request.QueryString["code"] != null && Request.QueryString["code"] != "")
            {
                //获取微信回传的code
                code = Request.QueryString["code"].ToString();  
                OAuth_Token Model = Get_token(code);  //获取token
                OAuthUser OAuthUser_Model = Get_UserInfo(Model.access_token, Model.openid );
                if(OAuthUser_Model.openid!=null && OAuthUser_Model.openid !="")  //已获取得openid及其他信息
                {
                    //在页面上输出用户信息
                    Response.Write("用户OPENID:" + OAuthUser_Model.openid + "
用户昵称:" + OAuthUser_Model.nickname + "
性别:" + OAuthUser_Model.sex + "
所在省:" + OAuthUser_Model.province + "
所在市:" + OAuthUser_Model.city + "
所在国家:" + OAuthUser_Model.country + "
头像地址:" + OAuthUser_Model.headimgurl + "
用户特权信息:" + OAuthUser_Model.privilege);
                    //或跳转到自己的页面,想怎么处理就怎么处理
                    Response.Redirect(reurl);
                }
                else  //未获得openid,回到wxProcess.aspx,访问弹出微信授权页面
                {
                    Response.Redirect("wxProcess.aspx?auth=1");
                }
            }
        }
    }

 

   #region 属性
    public string appid = "wx6669e231bffa123f";  //公众微信平台下可以找到
    public string appsecret = "9d693f7a81236c123464281115p78445";  //公众微信平台下可以找到
    #endregion

 

 //根据appid,secret,code获取微信openid、access token信息
    protected OAuth_Token Get_token(string Code)
    {
        //获取微信回传的openid、access token
        string Str = GetJson("https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + appsecret + "&code=" + Code + "&grant_type=authorization_code");
        //微信回传的数据为Json格式,将Json格式转化成对象
        OAuth_Token Oauth_Token_Model = JsonHelper.ParseFromJson(Str);
        return Oauth_Token_Model;
    }

    //刷新Token(好像这个刷新Token没有实际作用)
    protected OAuth_Token refresh_token(string REFRESH_TOKEN)
    {
        string Str = GetJson("https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" + appid + "&grant_type=refresh_token&refresh_token=" + REFRESH_TOKEN);
        OAuth_Token Oauth_Token_Model = JsonHelper.ParseFromJson(Str);
        return Oauth_Token_Model;
    }

    //根据openid,access token获得用户信息
    protected OAuthUser Get_UserInfo(string REFRESH_TOKEN, string OPENID)
    {
        string Str = GetJson("https://api.weixin.qq.com/sns/userinfo?access_token=" + REFRESH_TOKEN + "&openid=" + OPENID);
        OAuthUser OAuthUser_Model = JsonHelper.ParseFromJson(Str);
        return OAuthUser_Model;
    }

    //访问微信url并返回微信信息
    protected string GetJson(string url)
    {
        WebClient wc = new WebClient();
        wc.Credentials = CredentialCache.DefaultCredentials;
        wc.Encoding = Encoding.UTF8;
        string returnText = wc.DownloadString(url);

        if (returnText.Contains("errcode"))
        {
            //可能发生错误
        }
        return returnText;
    }


    ///
    /// token类
    ///
    public class OAuth_Token
    {
        public OAuth_Token()
        {

            //
            //TODO: 在此处添加构造函数逻辑
            //
        }
        //access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
        //expires_in access_token接口调用凭证超时时间,单位(秒)
        //refresh_token 用户刷新access_token
        //openid 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
        //scope 用户授权的作用域,使用逗号(,)分隔
        public string _access_token;
        public string _expires_in;
        public string _refresh_token;
        public string _openid;
        public string _scope;
        public string access_token
        {
            set { _access_token = value; }
            get { return _access_token; }
        }
        public string expires_in
        {
            set { _expires_in = value; }
            get { return _expires_in; }
        }

        public string refresh_token
        {
            set { _refresh_token = value; }
            get { return _refresh_token; }
        }
        public string openid
        {
            set { _openid = value; }
            get { return _openid; }
        }
        public string scope
        {
            set { _scope = value; }
            get { return _scope; }
        }

    }

    ///
    /// 用户信息类
    ///
    public class OAuthUser
    {
        public OAuthUser()
        { }
        #region 数据库字段
        private string _openID;
        private string _searchText;
        private string _nickname;
        private string _sex;
        private string _province;
        private string _city;
        private string _country;
        private string _headimgUrl;
        private string _privilege;
        #endregion

        #region 字段属性
        ///
        /// 用户的唯一标识
        ///
        public string openid
        {
            set { _openID = value; }
            get { return _openID; }
        }
        ///
        ///
        ///
        public string SearchText
        {
            set { _searchText = value; }
            get { return _searchText; }
        }
        ///
        /// 用户昵称
        ///
        public string nickname
        {
            set { _nickname = value; }
            get { return _nickname; }
        }
        ///
        /// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
        ///
        public string sex
        {
            set { _sex = value; }
            get { return _sex; }
        }
        ///
        /// 用户个人资料填写的省份
        ///
        public string province
        {
            set { _province = value; }
            get { return _province; }
        }
        ///
        /// 普通用户个人资料填写的城市
        ///
        public string city
        {
            set { _city = value; }
            get { return _city; }
        }
        ///
        /// 国家,如中国为CN
        ///
        public string country
        {
            set { _country = value; }
            get { return _country; }
        }
        ///
        /// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
        ///
        public string headimgurl
        {
            set { _headimgUrl = value; }
            get { return _headimgUrl; }
        }
        ///
        /// 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)其实这个格式称不上JSON,只是个单纯数组
        ///
        public string privilege
        {
            set { _privilege = value; }
            get { return _privilege; }
        }
        #endregion
    }

    ///
    /// 将Json格式数据转化成对象
    ///
    public class JsonHelper
    {
        ///  
        /// 生成Json格式 
        ///  
        ///  
        ///  
        ///  
        public static string GetJson(T obj)
        {
            DataContractJsonSerializer json = new DataContractJsonSerializer(obj.GetType());
            using (MemoryStream stream = new MemoryStream())
            {
                json.WriteObject(stream, obj);
                string szJson = Encoding.UTF8.GetString(stream.ToArray()); return szJson;
            }
        }
        ///  
        /// 获取Json的Model 
        ///  
        ///  
        ///  
        ///  
        public static T ParseFromJson(string szJson)
        {
            T obj = Activator.CreateInstance();
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(szJson)))
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
                return (T)serializer.ReadObject(ms);
            }
        }
    }

 

 输出微信用户信息:

微信公众平台开发—利用OAuth2.0获取微信用户基本信息

 

CSDN博客地址:利用OAuth2.0获取微信用户基本信息(CSDN版)

CSDN源码下载地址:利用OAuth2.0获取微信用户基本信息源码下载

2014-12-24 17:58:00 weixin_30326745 阅读数 5
  • 19年录制SpringBoot2.x整合微信支付在线教育网站项目...

    SpringBoot2.x开发在线教育微信支付项目实战,2019年新录制,课程分为11章63节课,从需求评审到设计数据库,编码,单元测试,Nginx集群部署;整合各种正式开发的工作技巧; IDEA工具热部署,Ngrock本地域名映射,数据库逆向工程生成实体类,动态Sql,微信OAuth2一键登录,网页微信扫码支付,JWT微服务登录鉴权,阿里云集群部署,公网域名解析配置,前端页面接口动静分离

    1404 人正在学习 去看看 张颜源
原文:Senparc.Weixin.MP SDK 微信公众平台开发教程(十二):OAuth2.0说明

  紧接上一篇《Senparc.Weixin.MP SDK 微信公众平台开发教程(十一):高级接口说明》,这里专讲OAuth2.0。

理解OAuth2.0

  首先我们通过一张图片来了解一下OAuth2.0的运作模式:

  从上图我们可以看到,整个过程进行了2次“握手”,最终利用授权的AccessToken进行一系列的请求,相关的过程说明如下:

  • A:由客户端向服务器发出验证请求,请求中一般会携带这些参数
    • ID标识,例如appId
    • 验证后跳转到的URL(redirectUrl)
    • 状态参数(可选)
    • 授权作用域scope(可选)
    • 响应类型(可选)
  • B:服务器返回一个grant授权标识(微信默认情况下称之为code),类似于一个一次性的临时字符串密钥。如果在A中提供了redirectUrl,这里服务器会做一次跳转,带上grant和状态参数,访问redirectUrl。
  • C:客户端的redirectUrl对应页面,凭借grant再次发起请求,这次请求通常会携带一些敏感信息:
    • ID标识
    • 密码
    • grant字符串(code)
    • grant类型(可选,微信中默认为code)
  • D:服务器验证ID标识、密码、grant都正确之后,返回AccessToken(注意,这里的AccessToken和之前通用接口、高级接口介绍的AccessToken没有关系,不能交叉使用)
  • E:客户端凭借AccessToken请求一系列的API,在此过程中不再会携带appId,Secret,grant等敏感的信息。
  • F:服务器返回请求结果。

微信的OAuth2.0使用

  了解了OAuth2.0的基本原理之后,我们来看一下OAuth2.0在微信中是如何运用的。

  假设一个场景:用户进入了一个微信公众账号,随后通过消息中的链接,在微信内嵌浏览器中打开了一个游戏网页,这个游戏需要用户登录并且记录用户的游戏得分。

  这种情况下我们有两种处理方式:

  1. 让用户在网页中进行注册、登录(并且每次打开这个网页都可能要重新进行登录,因为微信内置浏览器的cookie保存时间非常短),这个当然是个很坑爹的设计。
  2. 利用OAuth2.0。在用户进入这个页面的时候,先判断用户是否登录,如果没有,自动跳转到OAuth2.0授权页面,这个页面又自动进行了上述ABCD一系列验证,再通过EF得到用户的OpenId甚至更加详细的信息(包括头像),自动完成登录(或必要的注册)过程,随后用户以登录状态直接进入游戏。

  可以看出,使用OAuth2.0大幅度提高了用户体验,并且可以自动绑定识别用户微信OpenId。

  需要注意的是,上面提到的“OAuth2.0授权页面”还有两种形式:

  1. 当请求A中的Scope为snsapi_base时,整个授权过程自动完成,用户的客户端不会有任何中间页面显示,但是授权的结果仅能获取用户的OpenId(不管用户是否已关注,当然如果用户是关注用户,再次利用高级接口中的用户信息接口,利用OpenId获取用户资料也是可以的,只不过绕了几个弯)
  2. 当请求A中的Scope为snsapi_userinfo时,需要提供一个授权页面(类似很多网站利用微博账号、QQ号登陆的那种授权),仅当用户同意之后,立即获取到用户的详细信息,这里的用户必须是已经关注的用户。

  也就是说,snsapi_base的方法可以“神不知鬼不觉”地获取用户OpenId,全自动完成登录注册过程,但是信息量有限;snsapi_userinfo需要用户在指定界面上授权之后,自动完成整个过程,这个授权有一个时间段,超过时间后需要重新询问用户。

Senparc.Weixin.MP OAuth2.0接口

  源文件文件夹:Senparc.Weixin.MP/AdvancedAPIs/OAuth

  相比其他接口OAuth2.0略微复杂,相关内容将在下一篇专门进行介绍:Senparc.Weixin.MP SDK 微信公众平台开发教程(十二):OAuth2.0说明

  源代码中相关方法如下:

namespace Senparc.Weixin.MP.AdvancedAPIs
{
    //官方文档:http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E4.B8.80.E6.AD.A5.EF.BC.9A.E7.94.A8.E6.88.B7.E5.90.8C.E6.84.8F.E6.8E.88.E6.9D.83.EF.BC.8C.E8.8E.B7.E5.8F.96code

    /// <summary>
    /// 应用授权作用域
    /// </summary>
    public enum OAuthScope
    {
        /// <summary>
        /// 不弹出授权页面,直接跳转,只能获取用户openid
        /// </summary>
        snsapi_base,
        /// <summary>
        /// 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息
        /// </summary>
        snsapi_userinfo
    }

    public static class OAuth
    {
        /// <summary>
        /// 获取验证地址
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="redirectUrl"></param>
        /// <param name="state"></param>
        /// <param name="scope"></param>
        /// <param name="responseType"></param>
        /// <returns></returns>
        public static string GetAuthorizeUrl(string appId, string redirectUrl, string state, OAuthScope scope, string responseType = "code")
        {
            var url =
                string.Format("https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type={2}&scope={3}&state={4}#wechat_redirect",
                                appId, redirectUrl.UrlEncode(), responseType, scope, state);

            /* 这一步发送之后,客户会得到授权页面,无论同意或拒绝,都会返回redirectUrl页面。
             * 如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。这里的code用于换取access_token(和通用接口的access_token不通用)
             * 若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE
             */
            return url;
        }

        /// <summary>
        /// 获取AccessToken
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="secret"></param>
        /// <param name="code">code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。</param>
        /// <param name="grantType"></param>
        /// <returns></returns>
        public static OAuthAccessTokenResult GetAccessToken(string appId, string secret, string code, string grantType = "authorization_code")
        {
            var url =
                string.Format("https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type={3}",
                                appId, secret, code, grantType);

            return CommonJsonSend.Send<OAuthAccessTokenResult>(null, url, null, CommonJsonSendType.GET);
        }

        /// <summary>
        /// 刷新access_token(如果需要)
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="refreshToken">填写通过access_token获取到的refresh_token参数</param>
        /// <param name="grantType"></param>
        /// <returns></returns>
        public static OAuthAccessTokenResult RefreshToken(string appId, string refreshToken, string grantType = "refresh_token")
        {
            var url =
                string.Format("https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={0}&grant_type={1}&refresh_token={2}",
                                appId, grantType, refreshToken);

            return CommonJsonSend.Send<OAuthAccessTokenResult>(null, url, null, CommonJsonSendType.GET);
        }

        public static OAuthUserInfo GetUserInfo(string accessToken,string openId)
        {
            var url = string.Format("https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}",accessToken,openId);
            return CommonJsonSend.Send<OAuthUserInfo>(null, url, null, CommonJsonSendType.GET);
        }
    }
}

  具体的示例方法见Senparc.Weixin.MP.Sample/Controllers/OAuth2Controller.cs,以及对应视图的代码。

注意点

  1. 必须是通过认证的服务号才可以使用OAuth接口。
  2. 接口中用到的AccessToken和高级接口(包括通用接口)中用到的AccessToken互不相关,即使他们都是通过相同的AppId和Secret得到的。
  3. 目前官方的授权页面不是100%稳定,有时候需要多点几次才能顺利通过,如果发现此类情况,需要做一些判断反复请求,至少在表面上可以不让用户看到错误页面。
  4. 出于安全,在使用OAuth2.0之前,需要进入到微信后台的【我的服务】对回调页面的域名进行设置:

 

 

  系列教程索引:http://www.cnblogs.com/szw/archive/2013/05/14/weixin-course-index.html

 

posted on 2014-12-24 17:58 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/4182963.html

没有更多推荐了,返回首页