Web API应用架构设计分析(2)

简介:

在上篇随笔《Web API应用架构设计分析(1)》,我对Web API的各种应用架构进行了概括性的分析和设计,Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端(包括浏览器,手机和平板电脑等移动设备)的框架,本篇继续这个主题,介绍如何利用ASP.NET Web API 来设计Web API层以及相关的调用处理。

1、Web API的接口访问分类

Web API接口的访问方式,大概可以分为几类:

1)一个是使用用户令牌,通过Web API接口进行数据访问。这种方式,可以有效识别用户的身份,为用户接口返回用户相关的数据,如包括用户信息维护、密码修改、或者用户联系人等与用户身份相关的数据。

2)一种是使用安全签名进行数据提交。这种方式提交的数据,URL连接的签名参数是经过安全一定规则的加密的,服务器收到数据后也经过同样规则的安全加密,确认数据没有被中途篡改后,再进行数据修改处理。因此我们可以为不同接入方式,如Web/APP/Winfrom等不同接入方式指定不同的加密秘钥,但是秘钥是双方约定的,并不在网络连接上传输,连接传输的一般是这个接入的AppID,服务器通过这个AppID来进行签名参数的加密对比,这种方式,类似微信后台的回调处理机制,它们就是经过这样的处理。

3)一种方式是提供公开的接口调用,不需要传入用户令牌、或者对参数进行加密签名的,这种接口一般较少,只是提供一些很常规的数据显示而已。

下面图示就是这几种接入方式的说明和大概应用场景。

 

2、Web API使用安全签名的实现

首先我们为用户注册的时候,需要由我们认可的终端发起,也就是它们需要进行安全签名,后台确认签名有效性,才能正常实现用户注册,否则遭到伪造数据,系统就失去原有的意义了。

复制代码
复制代码
    /// <summary>
    /// 注册用户信息接口
    /// </summary>
    public interface IUserApi
    {
        /// <summary>
        /// 注册用户处理,包括用户名,密码,身份证号,手机等信息
        /// </summary>
        /// <param name="json">注册用户信息</param>
        /// <param name="signature">加密签名字符串</param>
        /// <param name="timestamp">时间戳</param>
        /// <param name="nonce">随机数</param>
        /// <param name="appid">应用接入ID</param>
        /// <returns></returns>
        ResultData Add(UserJson json,
            string signature, string timestamp, string nonce, string appid);
    }
复制代码
复制代码

其实我们获得用户的令牌,也是需要进行用户安全签名认证的,这样我们才有效保证用户身份令牌获取的合法性。

复制代码
复制代码
    /// <summary>
    /// 系统认证等基础接口
    /// </summary>
    public interface IAuthApi
    {
        /// <summary>
        /// 注册用户获取访问令牌接口
        /// </summary>
        /// <param name="username">用户登录名称</param>
        /// <param name="password">用户密码</param>
        /// <param name="signature">加密签名字符串</param>
        /// <param name="timestamp">时间戳</param>
        /// <param name="nonce">随机数</param>
        /// <param name="appid">应用接入ID</param>
        /// <returns></returns>
        TokenResult GetAccessToken(string username, string password,
            string signature, string timestamp, string nonce, string appid);
    }
复制代码
复制代码

上面介绍到的参数,我们提及了几个参数,一个是加密签名字符串,一个是时间戳,一个是随机数,一个是应用接入ID,我们一般的处理规则如下所示。

1)Web API 为各种应用接入,如APP、Web、Winform等接入端分配应用AppID以及通信密钥AppSecret,双方各自存储。
2)接入端在请求Web API接口时需携带以下参数:signature、 timestamp、nonce、appid,签名是根据几个参数和加密秘钥生成。
3) Web API 收到接口调用请求时需先检查传递的签名是否合法,验证后才调用相关接口。

加密签名在服务端(Web API端)的验证流程参考微信的接口的处理方式,处理逻辑如下所示。

1)检查timestamp 与系统时间是否相差在合理时间内,如10分钟。
2)将appSecret、timestamp、nonce三个参数进行字典序排序
3)将三个参数字符串拼接成一个字符串进行SHA1加密
4)加密后的字符串可与signature对比,若匹配则标识该次请求来源于某应用端,请求是合法的。

C#端代码校验如下所示。

复制代码
复制代码
        /// <summary>
        /// 检查应用接入的数据完整性
        /// </summary>
        /// <param name="signature">加密签名内容</param>
        /// <param name="timestamp">时间戳</param>
        /// <param name="nonce">随机字符串</param>
        /// <param name="appid">应用接入Id</param>
        /// <returns></returns>
        public CheckResult ValidateSignature(string signature, string timestamp, string nonce, string appid)
        {
            CheckResult result = new CheckResult();
            result.errmsg = "数据完整性检查不通过";

            //根据Appid获取接入渠道的详细信息
            AppInfo channelInfo = BLLFactory<App>.Instance.FindByAppId(appid);
            if (channelInfo != null)
            {
                #region 校验签名参数的来源是否正确
                string[] ArrTmp = { channelInfo.AppSecret, timestamp, nonce };

                Array.Sort(ArrTmp);
                string tmpStr = string.Join("", ArrTmp);

                tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
                tmpStr = tmpStr.ToLower();

                if (tmpStr == signature && ValidateUtil.IsNumber(timestamp))
                {
                    DateTime dtTime = timestamp.ToInt32().IntToDateTime();
                    double minutes = DateTime.Now.Subtract(dtTime).TotalMinutes;
                    if (minutes > timspanExpiredMinutes)
                    {
                        result.errmsg = "签名时间戳失效";
                    }
                    else
                    {
                        result.errmsg = "";
                        result.success = true;
                        result.channel = channelInfo.Channel;
                    }
                }
                #endregion
            }
            return result;
        }
复制代码
复制代码

一旦我们完成对安全签名进行成功认证,也就是我们对数据提交的来源和完整性进行了确认,就可以进行更多和安全性相关的操作了,如获取用户的访问令牌信息的操作如下所示。

第一步是验证用户的签名是否符合要求,符合要求后进行用户信息的比对,并生成用户访问令牌数据JSON,返回给调用端即可。

 

3、Web API使用安全令牌的实现

通过上面的接口,我们获取到的用户访问令牌,以后和用户相关的信息调用,我们就可以通过这个令牌参数进行传递就可以了,这个令牌带有用户的一些基础信息,如用户ID,过期时间等等,这个Token的设计思路来源于JSON Web Token (JWT),具体可以参考http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html,以及GitHub上的项目https://github.com/jwt-dotnet/jwt

由于Web API的调用,都是一种无状态方式的调用方式,我们通过token来传递我们的用户信息,这样我们只需要验证Token就可以了。

JWT的令牌生成逻辑如下所示

令牌生成后,我们需要在Web API调用处理前,对令牌进行校验,确保令牌是正确有效的。

检查的代码,就是把令牌生成的过程逆反过来,获取相应的信息,并且对令牌签发的时间进行有效性判断,一般可以约定一个失效时间,如1天或者7天,也不用设置太短。

复制代码
复制代码
        /// <summary>
        /// 检查用户的Token有效性
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public CheckResult ValidateToken(string token)
        {
            //返回的结果对象
            CheckResult result = new CheckResult();
            result.errmsg = "令牌检查不通过";

            if (!string.IsNullOrEmpty(token))
            {
                try
                {
                    string decodedJwt = JsonWebToken.Decode(token, sharedKey);
                    if (!string.IsNullOrEmpty(decodedJwt))
                    {
                        #region 检查令牌对象内容
                        dynamic root = JObject.Parse(decodedJwt);
                        string username = root.name;
                        string userid = root.iss;
                        int jwtcreated = (int)root.iat;

                        //检查令牌的有效期,7天内有效
                        TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
                        int timestamp = (int)t.TotalDays;
                        if (timestamp - jwtcreated > expiredDays)
                        {
                            throw new ArgumentException("用户令牌失效.");
                        }

                        //成功校验
                        result.success = true;
                        result.errmsg = "";
                        result.userid = userid;
                        #endregion
                    }
                }
                catch (Exception ex)
                {
                    LogTextHelper.Error(ex);
                }
            }
            return result;
        }
复制代码
复制代码

一般来说,访问令牌不能永久有效,对于访问令牌的重新更新问题,可以设置一个规则,只允许最新的令牌使用,并把它存储在接口缓存里面进行对比,应用系统退出的时候,就把内存里面的Token移除就可以了。

4、ASP.NET Web API的开发

 上面我们定义了一般的Web API接口,以及实现相应的业务实现,如果我们需要创建Web API层,还需要构建一个Web API项目的。

创建好相应的项目后,可以为项目添加一个Web API基类,方便控制共同的接口。

然后我们就可以在Controller目录上创建更多的应用API控制器了。

最后我们为了统一所有的API接口都是返回JSON方式,我们需要对WebApiConfig里面的代码进行设置下。

复制代码
复制代码
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 配置和服务
            config.SetCorsPolicyProviderFactory(new CorsPolicyFactory());
            config.EnableCors();

            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { action = "post", id = RouteParameter.Optional }
            );

            // Remove the JSON formatter
            //config.Formatters.Remove(config.Formatters.JsonFormatter);

            // Remove the XML formatter
            config.Formatters.Remove(config.Formatters.XmlFormatter);
        }
    }
复制代码
复制代码

5、Web API 接口的测试

接下来我们要做的就是需要增加业务接口,以便进行具体的测试了,建议使用Winform项目,对每个接口进行一个测试,或者也可以考虑使用单元测试的方式,看个人喜好吧。

例如我们如果要测试用户登陆的接口的话,我们的测试代码如下所示。

复制代码
复制代码
        /// <summary>
        /// 生成签名字符串
        /// </summary>
        /// <param name="appSecret">接入秘钥</param>
        /// <param name="timestamp">时间戳</param>
        /// <param name="nonce">随机数</param>
        private string SignatureString(string appSecret, string timestamp, string nonce)
        {
            string[] ArrTmp = { appSecret, timestamp, nonce };

            Array.Sort(ArrTmp);
            string tmpStr = string.Join("", ArrTmp);

            tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
            return tmpStr.ToLower();
        }

        private TokenResult GetTokenResult()
        {
            string timestamp = DateTime.Now.DateTimeToInt().ToString();
            string nonce = new Random().NextDouble().ToString();
            string signature = SignatureString(appSecret, timestamp, nonce);

            string appended = string.Format("&signature={0}&timestamp={1}&nonce={2}&appid={3}", signature, timestamp, nonce, appId);
            string queryUrl = url + "Auth/GetAccessToken?username=test&password=123456" + appended;

            HttpHelper helper = new HttpHelper();
            string token = helper.GetHtml(queryUrl);
            Console.WriteLine(token);
            TokenResult tokenResult = JsonConvert.DeserializeObject<TokenResult>(token);
            return tokenResult;
        }
复制代码
复制代码

如果我们已经获得了令牌,我们根据令牌传递参数给连接,并获取其他数据的测试处理代码如下所示。

复制代码
复制代码
            //获取访问令牌
            TokenResult tokenResult = GetTokenResult();

            string queryUrl = url + "/Contact/get?token=" + tokenResult.access_token;
            HttpHelper helper = new HttpHelper();
            string result = helper.GetHtml(queryUrl);
            Console.WriteLine(result);
复制代码
复制代码

如果需要POST数据的话,那么调用代码如下所示。

复制代码
复制代码
            //使用POST方式
            var data = new
            {
                name = "张三",
                certno = "123456789",
            };
            var postData = data.ToJson();

            queryUrl = url + "/Contact/Add?token=" + tokenResult.access_token;
            helper = new HttpHelper();
            helper.ContentType = "application/json";
            result = helper.GetHtml(queryUrl, postData, true);
            Console.WriteLine(result);
复制代码
复制代码

Web API后台,会自动把POST的JSON数据转换为对应的对象的。

如果是GET方式,我们可能可以直接通过浏览器进行调试,如果是POST方式,我们需要使用一些协助工具,如Fiddler等处理工具,但是最好的方式是自己根据需要弄一个测试工具,方便测试。

以下就是我为了自己Web API 接口开发的需要,专门弄的一个调试工具,可以自动组装相关的参数,包括使用安全签名的参数,还可以把所有参数数据进行存储。

 转:http://www.cnblogs.com/wuhuacong/p/4620300.html

本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/4843955.html ,如需转载请自行联系原作者
相关文章
|
16小时前
|
应用服务中间件 API nginx
使用Python和Flask构建RESTful Web API
使用Python和Flask构建RESTful Web API
5 0
|
16小时前
|
Python
使用Python构建一个简单的Web应用
使用Python构建一个简单的Web应用
5 1
|
17小时前
|
运维 Kubernetes Cloud Native
构建高效云原生应用:采用微服务架构与容器化技术
【5月更文挑战第28天】 在当今数字化转型的浪潮中,企业正迅速采纳云原生技术以保持竞争力。本文深入探讨了构建高效云原生应用的关键要素,重点分析了微服务架构和容器化技术如何共同推动应用的敏捷性、可扩展性和可靠性。通过具体案例分析,揭示了这些技术在实际业务场景中的应用效果及其带来的显著改进。
|
16小时前
|
应用服务中间件 nginx
蓝易云 - 编写Dockerfile制作Web应用系统nginx镜像
这是一个基本的例子,你可能需要根据自己的应用进行调整。例如,你可能需要添加更多的配置,或者使用不同的Nginx版本。
10 2
|
1天前
|
Cloud Native Devops 持续交付
构建未来:云原生架构在现代企业中的应用与挑战
【5月更文挑战第27天】 随着数字化转型的深入,企业对信息技术基础设施的要求不断提高。云原生架构作为一种新兴的技术范式,以其灵活性、可扩展性和敏捷性成为推动企业技术革新的关键力量。本文旨在探讨云原生架构的核心组件、实施策略以及面临的主要挑战,为企业采纳云原生技术提供参考。
|
1天前
|
Cloud Native 安全 持续交付
构建未来应用的基石:云原生架构的演进与实践
【5月更文挑战第27天】 在数字化转型的浪潮中,企业急需灵活、高效且可靠的解决方案以应对市场的快速变化。云原生架构应运而生,其不仅重塑了软件开发和运维的模式,也为企业提供了持续交付和自动化管理的可能性。本文将探讨云原生技术的发展历程,解析其关键组件如容器化、微服务及持续集成/持续部署(CI/CD),并分享如何在实践中有效利用这些技术来构建和维护高度可扩展的系统。
|
1天前
|
监控 Cloud Native 持续交付
构建未来:云原生架构在现代企业中的应用与挑战构建高效微服务架构:策略与实践
【5月更文挑战第27天】 随着数字化转型的深入,企业对技术的依赖日益增强。云原生技术以其灵活性、可扩展性和敏捷性成为推动企业IT架构现代化的关键力量。本文将探讨云原生架构的核心概念、实施策略以及在采纳过程中可能遇到的挑战。通过分析案例和最佳实践,旨在为读者提供如何在保持业务连续性的同时,利用云原生技术加速创新的见解。 【5月更文挑战第27天】 在当前软件开发的快速迭代和市场需求多变的背景下,微服务架构以其灵活性、可扩展性和容错性成为企业技术选型的热门。本文将探讨如何构建一个高效的微服务系统,包括关键的设计原则、常用的技术栈选择、以及实施过程中的最佳实践。我们将重点分析如何通过合理的服务划分、
|
1天前
|
运维 Cloud Native 持续交付
构建未来:云原生架构在现代企业中的应用
【5月更文挑战第27天】随着云计算的不断成熟和企业数字化转型的深入,云原生架构已成为推动业务敏捷性、可扩展性及运维效率的关键。本文将探讨云原生的核心组件,包括容器化、微服务、持续集成/持续部署(CI/CD)、以及无服务器计算,并分析如何利用这些技术构建一个高效、弹性和可维护的现代化应用平台。通过实际案例,我们将讨论企业在采纳云原生架构过程中面临的挑战与解决策略,以及这一转型对业务成长和竞争力提升所带来的潜在影响。
|
1天前
|
Cloud Native 安全 云计算
构建未来:云原生架构在现代企业中的应用与挑战
【5月更文挑战第27天】 随着数字化转型的浪潮席卷全球,云原生技术以其灵活性、可扩展性和敏捷性成为推动企业技术创新的关键力量。本文深入探讨了云原生架构的核心组件和运作机制,分析了其在提高业务效率、降低成本及支持快速迭代方面的显著优势。同时,针对企业在采纳云原生过程中可能遇到的安全、团队技能匹配等挑战进行剖析,并提出相应的解决策略。通过实际案例分析,文章旨在为读者提供一个关于如何有效利用云原生架构以赋能业务发展的清晰视角。
|
1天前
|
Cloud Native 持续交付 API
构建未来:云原生架构在现代企业中的应用
【5月更文挑战第27天】 随着数字化转型的深入,企业对于信息技术的需求日益复杂多变。本文聚焦于云原生架构,一种旨在构建和运行可在公共云、私有云及混合云环境中扩展的应用程序。通过分析云原生的核心组件如容器化、微服务、持续集成/持续部署(CI/CD)、以及声明式API,探讨了如何利用这些技术提高企业的敏捷性、可维护性和资源优化。此外,文中还将展示云原生架构如何助力企业实现更快的产品迭代,更高的运营效率,以及更强的市场竞争力。

热门文章

最新文章