微信開放平臺之獲取用戶信息的受權方式擴展

在SENPARC底層,提供了微信用戶受權驗證的方式:數據庫

在下圖位置:json

 

namespace Senparc.Weixin.MP.Sample.Filters
{
    /// <summary>
    /// OAuth自動驗證,能夠加在Action或整個Controller上
    /// </summary>
    public class CustomOAuthAttribute : SenparcOAuthAttribute
    {
        public CustomOAuthAttribute(string appId, string oauthCallbackUrl)
            : base(appId, oauthCallbackUrl)
        {
            base._appId = base._appId ?? System.Configuration.ConfigurationManager.AppSettings["TenPayV3_AppId"];
        }

        public override bool IsLogined(HttpContextBase httpContext)
        {
            return httpContext != null && httpContext.Session["OpenId"] != null;

            //也可使用其餘方法如Session驗證用戶登陸
            //return httpContext != null && httpContext.User.Identity.IsAuthenticated;
        }
    }
}

在我近期的項目中進行了一些擴展:api

//-----------------------------------------------------------------------
// <copyright file="CustomOAuthAttribute" company="FenSiShengHuo, Ltd.">
//     Copyright (c) 2018 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System.Web;
using Senparc.Weixin.MP.MvcExtension;
using System.Web.Configuration;
using DotNet.MVC.Infrastructure.Utilities;
using System;
using DotNet.WeChat.CommonService;

namespace DotNet.WeChat.MVC.Filters
{
    /// <summary>
    /// CustomOAuthAttribute
    /// 
    /// OAuth自動驗證,能夠加在Action或整個Controller上
    /// 
    /// 修改紀錄
    /// 
    /// 2018-03-29 版本:1.0 JiShiYu 建立文件。     
    /// 
    /// <author>
    ///     <name>JiShiYu</name>
    ///     <date>2018-03-29</date>
    /// </author>
    /// </summary>
    public class CustomWechatMPOAuthAttribute : SenparcOAuthAttribute
    {
        /// <summary>
        ///  OAuth自動驗證,能夠加在Action或整個Controller上
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="componentAppId"></param>
        /// <param name="oauthCallbackUrl"></param>
        public CustomWechatMPOAuthAttribute(string appId = null, string componentAppId = null, string oauthCallbackUrl = null)
            : base(appId, oauthCallbackUrl)
        {
            base._appId = base._appId ?? WebConfigurationManager.AppSettings["WeixinAppId"];
            base._oauthCallbackUrl = base._oauthCallbackUrl ?? "/Admin/WeChatMP/OAuthCallback";
        }

        public override bool IsLogined(HttpContextBase httpContext)
        {
            if (httpContext.Request.Url.Host.IndexOf("fensishenghuo.com", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                //return httpContext != null && httpContext.Session[GlobalSetting.WechatOpenIdSessionKey] != null;

                if (httpContext != null)
                {
                    if (httpContext.Session[GlobalSetting.WechatOpenIdSessionKey] != null)
                    {
                        WechatUserInfo wechatUserInfo = httpContext.Session[GlobalSetting.WechatOpenIdSessionKey] as WechatUserInfo;
                        if (wechatUserInfo != null && wechatUserInfo.MPUserInfo != null)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
            //也可使用其餘方法如Session驗證用戶登陸
            //return httpContext != null && httpContext.User.Identity.IsAuthenticated;
        }
    }
}

目前要基於開放平臺進行開發,也須要獲取用戶的信息,開放平臺獲取用戶返回的信息跟公衆號的不同,分別以下:數組

 公衆號的用戶:  Senparc.Weixin.MP.AdvancedAPIs.User.UserInfoJson.cs,我這裏增長了  [Serializable]緩存

/*----------------------------------------------------------------
    Copyright (C) 2018 Senparc

    文件名:UserInfoJson.cs
    文件功能描述:獲取用戶信息返回結果


    建立標識:Senparc - 20150211

    修改標識:Senparc - 20150303
    修改描述:整理接口

    修改標識:jsionr - 20150321
    修改描述:添加remark屬性

    修改標識:Senparc - 20150321
    修改描述:添加unionid屬性

    修改標識:jsionr - 20150513
    修改描述:增長我的分組信息

    修改標識:Senparc - 20150526
    修改描述:修改備註

    修改標識:Senparc - 20150727
    修改描述:添加批量獲取用戶基本信息返回結果

    修改標識:Senparc - 20150727
    修改描述:添加批量獲取用戶基本信息返回結果

----------------------------------------------------------------*/

using System;
using System.Collections.Generic;
using Senparc.Weixin.Entities;

namespace Senparc.Weixin.MP.AdvancedAPIs.User
{
    /// <summary>
    /// 高級接口獲取的用戶信息
    /// </summary>
    [Serializable]
    public class UserInfoJson : WxJsonResult
    {
        /// <summary>
        /// 用戶是否訂閱該公衆號標識,值爲0時,表明此用戶沒有關注該公衆號,拉取不到其他信息。
        /// </summary>
        public int subscribe { get; set; }
        /// <summary>
        /// 用戶的標識,對當前公衆號惟一
        /// </summary>
        public string openid { get; set; }
        /// <summary>
        /// 用戶的暱稱
        /// </summary>
        public string nickname { get; set; }
        /// <summary>
        /// 用戶的性別,值爲1時是男性,值爲2時是女性,值爲0時是未知
        /// </summary>
        public int sex { get; set; }
        /// <summary>
        ///用戶的語言,簡體中文爲zh_CN
        /// </summary>
        public string language { get; set; }
        /// <summary>
        /// 用戶所在城市
        /// </summary>
        public string city { get; set; }
        /// <summary>
        /// 用戶所在省份
        /// </summary>
        public string province { get; set; }
        /// <summary>
        /// 用戶所在國家
        /// </summary>
        public string country { get; set; }
        /// <summary>
        /// 用戶頭像,最後一個數值表明正方形頭像大小(有0、4六、6四、9六、132數值可選,0表明640*640正方形頭像),用戶沒有頭像時該項爲空。若用戶更換頭像,原有頭像URL將失效。
        /// </summary>
        public string headimgurl { get; set; }
        /// <summary>
        /// 用戶關注時間,爲時間戳。若是用戶曾屢次關注,則取最後關注時間
        /// </summary>
        public long subscribe_time { get; set; }
        /// <summary>
        /// 只有在用戶將公衆號綁定到微信開放平臺賬號後,纔會出現該字段。
        /// </summary>
        public string unionid { get; set; }
        /// <summary>
        /// 公衆號運營者對粉絲的備註,公衆號運營者可在微信公衆平臺用戶管理界面對粉絲添加備註
        /// </summary>
        public string remark { get; set; }
        /// <summary>
        /// 用戶所在的分組ID(兼容舊的用戶分組接口)
        /// </summary>
        public int groupid { get; set; }
        /// <summary>
        /// 用戶標籤
        /// </summary>
        public int[] tagid_list { get; set; }
        /// <summary>
        /// 返回用戶關注的渠道來源,ADD_SCENE_SEARCH 公衆號搜索,ADD_SCENE_ACCOUNT_MIGRATION 公衆號遷移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 掃描二維碼,ADD_SCENEPROFILE LINK 圖文頁內名稱點擊,ADD_SCENE_PROFILE_ITEM 圖文頁右上角菜單,ADD_SCENE_PAID 支付後關注,ADD_SCENE_OTHERS 其餘
        /// </summary>
        public string subscribe_scene { get; set; }
        /// <summary>
        /// 二維碼掃碼場景(開發者自定義)
        /// </summary>
        public int qr_scene { get; set; }
        /// <summary>
        /// 二維碼掃碼場景描述(開發者自定義)
        /// </summary>
        public string qr_scene_str { get; set; }
    }

    /// <summary>
    /// 批量獲取用戶基本信息返回結果
    /// </summary>
    public class BatchGetUserInfoJsonResult : WxJsonResult
    {
        public List<UserInfoJson> user_info_list { get; set; }
    }
}
View Code

開放平臺的用戶:Senparc.Weixin.Open.OAuthAPIs.OAuthUserInfo.cs 增長了  [Serializable],比公衆號少了很多屬性字段安全

/*----------------------------------------------------------------
    Copyright (C) 2018 Senparc
    
    文件名:OAuthUserInfo.cs
    文件功能描述:經過OAuth的獲取到的用戶信息
    
    
    建立標識:Senparc - 20150712
    
----------------------------------------------------------------*/

using System;

namespace Senparc.Weixin.Open.OAuthAPIs
{
    /// <summary>
    /// 經過OAuth的獲取到的用戶信息(snsapi_userinfo=scope)
    /// </summary>
    [Serializable]
    public class OAuthUserInfo
    {
        public string openid { get; set; }
        public string nickname { get; set; }
        /// <summary>
        /// 用戶的性別,值爲1時是男性,值爲2時是女性,值爲0時是未知
        /// </summary>
        public int sex { get; set; }
        public string province { get; set; }
        public string city { get; set; }
        public string country { get; set; }
        /// <summary>
        /// 用戶頭像,最後一個數值表明正方形頭像大小(有0、4六、6四、9六、132數值可選,0表明640*640正方形頭像),用戶沒有頭像時該項爲空
        /// </summary>
        public string headimgurl { get; set; }
        /// <summary>
        /// 用戶特權信息,json 數組,如微信沃卡用戶爲(chinaunicom)
        /// 做者注:其實這個格式稱不上JSON,只是個單純數組。
        /// </summary>
        public string[] privilege { get; set; }
        /// <summary>
        /// 只有在用戶將公衆號綁定到微信開放平臺賬號後,纔會出現該字段。詳見:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&lang=zh_CN
        /// </summary>
        public string unionid { get; set; }
    }
}
View Code

我想在開放平臺也能獲取到公衆號同樣的用戶信息,進行了以下改造 CustomWechatOpenOAuthAttribute微信

//-----------------------------------------------------------------------
// <copyright file="CustomOAuthAttribute.cs" company="FenSiShengHuo, Ltd.">
//     Copyright (c) 2018 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;

namespace DotNet.WeChat.MVC.Filters
{
    using DotNet.MVC.Infrastructure.Utilities;
    using DotNet.Utilities;
    using DotNet.WeChat.CommonService;
    using Senparc.Weixin.MP.MvcExtension;

    /// <summary>
    /// CustomOAuthAttribute
    ///  參考  Senparc.Weixin.MP.MvcExtension.SenparcOAuthAttribute 進行了重寫
    /// 
    /// OAuth自動驗證,能夠加在Action或整個Controller上
    /// 
    /// 修改紀錄
    /// 
    /// 2018-03-29 版本:1.0 JiShiYu 建立文件。     
    /// 
    /// <author>
    ///     <name>JiShiYu</name>
    ///     <date>2018-03-29</date>
    /// </author>
    /// </summary>
    public class CustomWechatOpenOAuthAttribute : SenparcOAuthAttribute
    {
        /// <summary>
        ///  OAuth自動驗證,能夠加在Action或整個Controller上
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="oauthCallbackUrl"></param>
        public CustomWechatOpenOAuthAttribute(string appId = null, string oauthCallbackUrl = null)
            : base(appId, oauthCallbackUrl)
        {
            base._appId = base._appId ?? WebConfigurationManager.AppSettings["WeixinAppId"];
            base._oauthCallbackUrl = base._oauthCallbackUrl ?? "/Admin/WechatOpen/OAuthOpenCallback";
        }

        public override bool IsLogined(HttpContextBase httpContext)
        {
            if (httpContext.Request.Url.Host.IndexOf("fensishenghuo.com", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                // return httpContext != null && httpContext.Session[GlobalSetting.WechatOpenIdSessionKey] != null;
                if (httpContext != null)
                {
                    if (httpContext.Session[GlobalSetting.WechatOpenIdSessionKey] != null)
                    {
                        WechatUserInfo wechatUserInfo = httpContext.Session[GlobalSetting.WechatOpenIdSessionKey] as WechatUserInfo;
                        if (wechatUserInfo != null && wechatUserInfo.OpenUserInfo != null)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
            //也可使用其餘方法如Session驗證用戶登陸
            //return httpContext != null && httpContext.User.Identity.IsAuthenticated;
        }


#if NET35 || NET40 || NET45 || NET461|| NET462
        private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
            validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
        }
#endif


#if NET35 || NET40 || NET45 || NET461|| NET462
        public override void OnAuthorization(AuthorizationContext filterContext)
#else
        public override void OnAuthorization(AuthorizationFilterContext filterContext)
#endif
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (AuthorizeCore(filterContext.HttpContext))
            {
                // ** IMPORTANT **
                // Since we're performing authorization at the action level, the authorization code runs
                // after the output caching module. In the worst case this could allow an authorized user
                // to cause the page to be cached, then an unauthorized user would later be served the
                // cached page. We work around this by telling proxies not to cache the sensitive page,
                // then we hook our custom authorization code into the caching mechanism so that we have
                // the final say on whether a page should be served from the cache.

#if NET35 || NET40 || NET45 || NET461|| NET462
                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
#endif
            }
            else
            {
                if (IsLogined(filterContext.HttpContext))
                {

                }
                else
                {
                    var callbackUrl = Senparc.Weixin.HttpUtility.UrlUtility.GenerateOAuthCallbackUrl(filterContext.HttpContext, _oauthCallbackUrl);
                    var state = string.Format("{0}|{1}", "FromSenparc", DateTime.Now.Ticks);
                    //var url = Senparc.Weixin.MP.AdvancedAPIs.OAuthApi.GetAuthorizeUrl(_appId, callbackUrl, state, _oauthScope);

                    string componentAppId = WebConfigurationManager.AppSettings["Component_Appid"];

                    var url = Senparc.Weixin.Open.OAuthAPIs.OAuthApi.GetAuthorizeUrl(_appId, componentAppId, callbackUrl, state, new[] { Senparc.Weixin.Open.OAuthScope.snsapi_userinfo, Senparc.Weixin.Open.OAuthScope.snsapi_base });
                    NLogHelper.Info("url=" + url);

                    filterContext.Result = new RedirectResult(url);
                }
            }
        }

#if NET35 || NET40 || NET45 || NET461|| NET462
        // This method must be thread-safe since it is called by the caching module.
        protected override HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }

            bool isAuthorized = AuthorizeCore(httpContext);
            return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
        }
#endif
    }
}

其中WechatUserInfo是我擴展的一個類:cookie

//-----------------------------------------------------------------------
// <copyright file="WechatUserInfo" company="FenSiShengHuo, Ltd.">
//     Copyright (c) 2018 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using DotNet.Utilities;
using Senparc.Weixin.MP.AdvancedAPIs.User;
using Senparc.Weixin.Open.OAuthAPIs;
using System;

namespace DotNet.WeChat.CommonService
{
    /// <summary>
    /// WechatUserInfo
    /// 
    /// 微信用戶
    /// 
    /// 修改紀錄
    /// 
    /// 2018-07-11 版本:1.0 JiShiYu 建立文件。     
    /// 
    /// <author>
    ///     <name>JiShiYu</name>
    ///     <date>2018-07-11</date>
    /// </author>
    /// </summary>
    [Serializable]
    public class WechatUserInfo
    {
        /// <summary>
        /// 公衆號的用戶信息
        /// </summary>
        public UserInfoJson MPUserInfo { set; get; }

        /// <summary>
        /// 開放平臺的用戶信息
        /// </summary>
        public OAuthUserInfo OpenUserInfo { set; get; }

        /// <summary>
        /// 系統的用戶信息
        /// </summary>
        public BaseUserInfo UserInfoBase { set; get; }
    }
}
/Admin/WechatOpen/OAuthOpenCallback 開放平臺回調方法處理:
        /// <summary>
        ///  開放平臺 網頁受權回調  CustomWechatOpenOAuthAttribute 中使用
        ///  這個搞定,就能夠不依賴主公衆號的回調地址配置了
        /// </summary>
        /// <param name="code"></param>
        /// <param name="state"></param>
        /// <param name="appId"></param>
        /// <param name="returnUrl"></param>
        /// <returns></returns>
        public ActionResult OAuthOpenCallback(string code, string state, string appId, string returnUrl)
        {
            if (string.IsNullOrWhiteSpace(code))
            {
                return Content("您拒絕了受權!");
            }

            if (!state.Contains("|"))
            {
                //這裏的state實際上是會暴露給客戶端的,驗證能力很弱,這裏只是演示一下
                //實際上能夠存任何想傳遞的數據,好比用戶ID
                return Content("驗證失敗!請從正規途徑進入!1001");
            }

            Senparc.Weixin.Open.OAuthAPIs.OAuthAccessTokenResult result = null;

            //經過,用code換取access_token
            try
            {
                var componentAccessToken = ComponentContainer.GetComponentAccessToken(componentAppId);
                result = Senparc.Weixin.Open.OAuthAPIs.OAuthApi.GetAccessToken(appId, componentAppId, componentAccessToken, code);
            }
            catch (Exception ex)
            {
                return Content(ex.Message);
            }

            if (result.errcode != Senparc.Weixin.ReturnCode.請求成功)
            {
                return Content("錯誤:" + result.errmsg);
            }
            //下面2個數據也能夠本身封裝成一個類,儲存在數據庫中(建議結合緩存)
            //若是能夠確保安全,能夠將access_token存入用戶的cookie中,每個人的access_token是不同的
            Session["OAuthAccessTokenStartTime"] = DateTime.Now;
            Session["OAuthAccessToken"] = result;

            //由於第一步選擇的是OAuthScope.snsapi_userinfo,這裏能夠進一步獲取用戶詳細信息
            try
            {
                // 開放平臺的用戶信息
                Senparc.Weixin.Open.OAuthAPIs.OAuthUserInfo openUserInfo = Senparc.Weixin.Open.OAuthAPIs.OAuthApi.GetUserInfo(result.access_token, result.openid);
                // 公衆號的用戶信息
                //UserInfoJson mpUserInfo = UserApi.Info(result.access_token, result.openid);
                UserInfoJson mpUserInfo = UserApi.Info(appId, result.openid);

                // 實現將該用戶存儲起來
                // return View(userInfo);

                // 改成存儲UserInfoJson對象
                WechatUserInfo wechatUserInfo = new WechatUserInfo();
                wechatUserInfo.OpenUserInfo = openUserInfo;
                wechatUserInfo.MPUserInfo = mpUserInfo;

                NLogHelper.Info("OAuthOpenCallback,WechatUserInfo=" + wechatUserInfo.FastToJson() + ",returnUrl=" + returnUrl);
                // 改成存儲UserInfoJson對象
                Session[GlobalSetting.WechatOpenIdSessionKey] = wechatUserInfo;
            }
            catch (ErrorJsonResultException ex)
            {
                return Content(ex.Message);
            }

            return Redirect(returnUrl);
        }

 

你們對這個在開放平臺獲取用戶受權方式有什麼建議呢?app

相關文章
相關標籤/搜索