zoukankan      html  css  js  c++  java
  • 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即可以。

  • 相关阅读:
    git线上操作
    IDEA快捷方式
    Java 四种线程池
    java 获取当前天之后或之前7天日期
    如何理解AWS 网络,如何创建一个多层安全网络架构
    申请 Let's Encrypt 通配符 HTTPS 证书
    GCE 部署 ELK 7.1可视化分析 nginx
    使用 bash 脚本把 AWS EC2 数据备份到 S3
    使用 bash 脚本把 GCE 的数据备份到 GCS
    nginx 配置 https 并强制跳转(lnmp一键安装包)
  • 原文地址:https://www.cnblogs.com/wzb153/p/6272525.html
Copyright © 2011-2022 走看看