• NET仿微信Oauth2.0


    这个文章先说一说Oauth2.0的原理,再到应用场景,最后才是代码实现,这样才学会最终的思想,并在应用场景使用,所谓实践出真理。

    1,Oauth2.0的原理

    OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。在互联网,经常用到OAuth2.0无非有三种场景:

    1.1对外完全开放,系统与系统的对接,例如淘宝开放平台。

    1.2内部系统对内部系统,如:(api.xxxx.com是一个子系统,web.xxxx.com是另外一个业务线的子系统,交给不同的团队处理,有NET,有JAVA)

    1.3内部系统对内部客户端系统。如(API.xxxx.com是对外开放的接口系统,IOS,安卓,C/S客户端,相关对接).

     

    综合三种应用场景,我们来分解一下Oauth2.0怎样在实践中使用。

    在Oauth2.0的使用场景中,最常见到的就是对外完全开放,像淘宝开放平台,新浪,腾讯QQ,微信,大量使用Oauth2.0,相反,后两种由于都是内部使用,所以一般都存在内部,让我以为Oauth2.0授权模式只有第一种,其实不是,Oauth2.0分别有四种授权模式,分别为:

    • 授权码模式(authorization code)
    • 简化模式(implicit)
    • 密码模式(resource owner password credentials)
    • 客户端模式(client credentials)

    而微信公众号采用的是授权码模式(authorization code),即颁发用户Appid,AppSecret,URL地址合法性,来进行统一调度与验证。,

    
    
    
    
    

    1.1、授权码模式

    授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动,所以一般开放平台首先这一类授权码模式,新浪开放平台,公众号API,淘宝开放平台都采用这一类,一般搭配HTTPS来提高安全机制。

    微信公众号API就是采用这一种模式,我们来梳理一下授权码模式的流程:

    它的步骤如下:

    (A)用户访问客户端,后者将前者导向认证服务器。

    (B)用户选择是否给予客户端授权。

    (C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

    (D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

    (E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

    下面是上面这些步骤所需要的参数。

    A步骤中,客户端申请认证的URI,包含以下参数:

    • response_type:表示授权类型,必选项,此处的值固定为"code"
    • client_id:表示客户端的ID,必选项
    • redirect_uri:表示重定向URI,可选项
    • scope:表示申请的权限范围,可选项
    • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

    再来看一看微信公众平台OAuth2.0授权详细步骤如下:

    1. 用户关注微信公众账号。
    2. 微信公众账号提供用户请求授权页面URL。
    3. 用户点击授权页面URL,将向服务器发起请求
    4. 服务器询问用户是否同意授权给微信公众账号(scope为snsapi_base时无此步骤)
    5. 用户同意(scope为snsapi_base时无此步骤)
    6. 服务器将CODE通过回调传给微信公众账号
    7. 微信公众账号获得CODE
    8. 微信公众账号通过CODE向服务器请求Access Token
    9. 服务器返回Access Token和OpenID给微信公众账号
    10. 微信公众账号通过Access Token向服务器请求用户信息(scope为snsapi_base时无此步骤)
    11. 服务器将用户信息回送给微信公众账号(scope为snsapi_base时无此步骤)

     

    是不是一目了然,那么根椐这个需求,整理一下流程为:

    1,先去开放平台(api地址)申请一个CODE,(那么我们需要判断appid,请求过来的URL合法性,生成CODE,并附于某个时间有效,5分钟失效,仿微信,然后做持久存储)。

    2,再根据CODE,APPID,AppSecret(平台颁发的应用ID及密锁),生成有效期的Access Token。(判断APPID,AppSecret合法性,然后根椐Appid获取持久存储层的Access Token,并把CODE取消合法,然后返回Access Token过期时间)

    3,判断Access Token的合法化,完成。

    流程梳理完了,我们就开始实现代码的编写:

     /// <summary>
            /// 对接商家,开放平台的接口
            /// </summary>
            /// <param name="AppId">应用ID</param>
            /// <param name="RedirectUri">授权回调地址</param>
            /// <param name="response_type"></param>
            /// <param name="scope"></param>
            /// <param name="state"></param>
            /// <returns></returns>
            [HttpGet]
            public dynamic authorize(string AppId, string RedirectUri, string response_type, string scope, string state)
            {
                //获取APPID应用表的字段
                var model = _ISellerApplication.Get(AppId);
              //判断APPID合法性及RedirectUri地址的合法
                if (model.AppId.Trim().ToLower() == model.AppId.ToLower() && RedirectUri.ToLower().Contains(model.ApplicationUrl.ToLower()))
                {
                 
    
                   //请求的地址合法性,如果不合法,直接返回
                    if (!Request.RequestUri.AbsoluteUri.Contains(model.ApplicationUrl.ToLower()))
                    {
                                        return Redirect($"{RedirectUri}?state={state}");
                    }
                    var appcodemodel = new AppCodeModel()
                    {
                        AccessToken = string.Concat(Guid.NewGuid().ToString("N"), Guid.NewGuid().ToString("N")).ToLower().CutString(40),
                        AppCode = string.Concat(Guid.NewGuid().ToString("N"), Guid.NewGuid().ToString("N")).ToLower().CutString(40),
                        ApplicationUrl = RedirectUri,
                        CreateDate = DateTime.Now,
                       AppId=model.AppId
    
                    };
    
                    //如果插入成功,返回CODE给他们
                    if (_IAppCode.Insert(appcodemodel))
                    {
                        if (RedirectUri.Equals("?"))
                        { return Redirect($"{RedirectUri}&state={state}&code={appcodemodel.AppCode}");
                        }
                        else
                        { return Redirect($"{RedirectUri}?state={state}&code={appcodemodel.AppCode}");}
                    }
                }
                //插入不成功,直接返回state
                return Redirect($"{RedirectUri}?state={state}");
            }

    再实现第二步调用:

      /// <summary>
            ///通过网页授权获取access_token
            /// </summary>
            /// <param name="AppId"></param>
            /// <param name="AppSecret"></param>
            /// <param name="code"></param>
            /// <param name="grant_type"></param>
            /// <returns></returns>
            [HttpGet]
            public dynamic AccessToken(string AppId, string AppSecret, string code, string grant_type)
            {

    //从持久层获取APPID模型
    var model = _IAppSoft.Get(AppId); //判断Appid if (model == null || model.AppId.Trim() != AppId.Trim()) { return new ApiArgumentException(ApiArgumentExceptionEum.APPID出错.ToString(), (int)ApiArgumentExceptionEum.APPID出错); } //判断AppSecret if (model.AppSecret.Trim() != AppSecret.Trim()) { return new ApiArgumentException(ApiArgumentExceptionEum.AppSecret错误.ToString(), (int)ApiArgumentExceptionEum.AppSecret错误); } //获取CODE并判断,CODE是否在过期范围内,5表示5分钟内有效 var AppCodeModel = _IAppCode.Get(5)); if (AppCodeModel == null) { return new ApiArgumentException(ApiArgumentExceptionEum.不合法的oauth_code.ToString(), (int)ApiArgumentExceptionEum.不合法的oauth_code); } var apptokenmodel = new AppTokenModel() { AccessToken = AppCodeModel.AccessToken, AppId = AppId, ClientId = model.ClientId, CreateDate = DateTime.Now, ExpireDate = DateTime.Now.AddHours(2), Status = 0 };
    //把AccessToken插入数据库,或者存储缓存
    bool bl = _IAppToken.Insert(apptokenmodel);
    //返回 AccessToken 及过期时间
    return new { AccessToken = AppCodeModel.AccessToken, ExpireDate = 7200 }; }

    现在仿微信都完成了授权的核心代码,第二步就可开始写AOP,然后统一验证AccessToken,由于用到的是WEB API2.0,所以我们统一写一个AOP类,并且继承  AuthorizationFilterAttribute, IActionFilter类,即可以写一个简单的AOP

    代码如下:

    namespace Saas.AOP
    {
    
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    
        
    
        public class AppOauthAuthentication : AuthorizationFilterAttribute, IActionFilter
        {
          
            protected AppTokenModel apptoken;
    
            public AppOauthAuthentication() { }
    
            public override void OnAuthorization(HttpActionContext actionContext)
            {
                var query = actionContext.Request.GetParamsFromUrl();
    
                var tokenString = query.Get("accessToken");
    
    
                if (string.IsNullOrWhiteSpace(tokenString) )
                {
                    actionContext.Response = CreateResponse(actionContext, 101, "请检查oauth认证参数");
                    return;
                }
                apptoken = new AppTokenBusiness().GetByAccessToken(tokenString);
    
    
                if (apptoken == null)
                {
                    actionContext.Response = CreateResponse(actionContext, 102, "未找到指定的accessToken");
                    return;
                }
    
    
                if (apptoken.ExpireDate < DateTime.Now)
                {
                    actionContext.Response = CreateResponse(actionContext, 104, "accessToken已过期");
                    return;
                }
    
                if (apptoken.Status == 1)
                {
                    actionContext.Response = CreateResponse(actionContext, 105, "APPID已更正,当前accessToken已失效");
                    return;
                }
               Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(apptoken.AppId), null);
                base.OnAuthorization(actionContext);
            }
    
    }}

    调用如下:

       /// <summary>
            /// 例子
            /// </summary>
        
            /// <returns></returns>
            [HttpPost, AppOauthAuthentication]
            public int test()
            {   
    return 1;
    }

    而简单的一个仿微信的Oauth2.0,就完成了。

    而关于Oauth2.0对于内部系统调用,以及APP怎样搭建一个通用的Oauth2.0,在明天再写,结合三种场景,搭建整一套的NET Oauth2.0系统,并完成跨平台。

    由于公司项目,是整一套的,暂时没整理实现开放,有需要交流的可以加我微信,BON184195873,扫描以下二维码,备注:CNBLOGS即可以。

  • 相关阅读:
    python调用linux DBus
    python查看单个应用的内存
    Azkaban
    H2 数据库引擎
    【IDEA与sql执行计划】
    【用draw.io 绘制ER图,非鸭蛋型ER图~】
    MySQL&Oracle数据库进阶学习【未完】
    【关于Typora 在线图床配置】
    什么是文件描述符
    同步个人vs code 习惯性配置 到码云(Gitee)
  • 原文地址:https://www.cnblogs.com/wzb153/p/6272525.html
Copyright © 2020-2023  润新知