Web API接口安全验证

Web API接口安全验证

在上篇随笔《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)一种方式是提供公开的接口调用,不需要传入用户令牌、或者对参数进行加密签名的,这种接口一般较少,只是提供一些很常规的数据显示而已

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

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移除就可以了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值