自己開發實現OAuth做webapi認證


看到園子里面有人寫的OAuth,就想把自己實現的OAuth也分享一下,關於OAuth協議這里就不再贅述。

一、作為認證服務器,首先需要提供一個可以通過appid/appsecret來獲取token這樣的一個接口,於是便有了以下代碼。

    public class AuthController : ApiController
    {
        [HttpGet]
        public HttpResponseMessage Token(string appid = "", string appsecret = "")
        {
            ApiResponseEntity rep;
            var isv = AppManage.Instance.GetAppISV(appid, appsecret);
            if (isv != null)
            {
                string token = TokenManage.Instance.CreateToken(appid);

                rep = new ApiResponseEntity
                {
                    Status = InterfaceStatus.Success,
                    BizData = new
                    {
                        AccessToken = token
                    }
                };
            }
            else
            {
                rep = new ApiResponseEntity()
                {
                    Status = InterfaceStatus.Parm_Missing,
                    Message = "param error"
                };
            }
            return rep.ToHttpResponseMessage();
        }
}
View Code

創建token的算法可以自行實現,我是將新生成的Guid做了一下md5處理,代碼如下:

public string CreateToken(string appid)
        {
            string token = Guid.NewGuid().ToString().ToMd5();
            Set(token, appid);
            return token;
        }
View Code

上文可以看到,在生成token了以后,就一個SetToken,就是將token存儲在緩存里面,並設置了一定時間的生存周期,代碼如下:

public void Set(string token, string appid)
        {
            var config = ServerConfigManage.Instance.GetServerConfig();
            string key = string.Format(RedisCacheKey.App_Token, token);
            RedisNetHelper.Set<string>(key, appid, DateTime.Now.AddSeconds(config.TokenSurvivalTime));
        }
View Code

為什么要用token做key,是因為token的變更會導致isv token驗證失效,但是用token做key就可以在存活周期內,這個key都可以使用,避免了多線程獲取token,或是其他原因導致的token失效。作為認證服務器,還需要提供一個RefreshToken這樣的接口,用來給刷新token的存活周期,代碼相似這里就不再贅述。

 

二、在Api做驗證的時候,就需要開始對Token進行驗證了,代碼如下:

 public class OAuthHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            ApiResponseEntity repEntity = null;
            string appid = "";
            string ip = RequestHelper.GetWebClientIp();
            if (!OAuthValidate.IpValidate(ip))
            {
                repEntity = new ApiResponseEntity
                {
                    Status = InterfaceStatus.IllegalIp,
                    Message = "ip access limit"
                };
            }
            else
            {
                string token = "";
                string url = request.RequestUri.AbsoluteUri;
                var routeData = request.GetRouteData();
                string controller = routeData.Values["controller"].ToString().ToLower();
                string action = routeData.Values["action"].ToString().ToLower();
                if (controller.Equals("auth") && action.Equals("token"))
                {
                    return base.SendAsync(request, cancellationToken);
                }

                if (request.Method == HttpMethod.Get)
                {
                    var query = request.RequestUri.ParseQueryString();
                    token = query["token"];
                }

                if (token == null || token.Length == 0)
                {
                    repEntity = new ApiResponseEntity
                    {
                        Status = InterfaceStatus.Token_Faild,
                        Message = "token invalid"
                    };
                }
                else
                {
                    appid = TokenManage.Instance.Get(token);
                    if (appid == null || appid.Length == 0)
                    {
                        repEntity = new ApiResponseEntity
                        {
                            Status = InterfaceStatus.Token_Faild,
                            Message = "token invalid"
                        };
                    }
                    else
                    {
                        if (!OAuthValidate.ApiValidate
                            (
                            string.Format("{0}/{1}", controller, action),
                            appid
                            ))
                        {
                            repEntity = new ApiResponseEntity
                            {
                                Status = InterfaceStatus.No_Access,
                                Message = "api access limit"
                            };
                        }
                    }
                }
            }

            if (repEntity != null)
            {
                var tsc = new TaskCompletionSource<HttpResponseMessage>();
                tsc.SetResult(repEntity.ToHttpResponseMessage());
                return tsc.Task;
            }
            else
            {
                return base.SendAsync(request, cancellationToken);
            }
        }
    }
View Code

使用比較傳統的方式,繼承於DelegatingHandler,然后進行處理,首先是做的IP驗證,然后再進行token有效期驗證,最后再進行Api的權限調用驗證。驗證的代碼如下:

 public static bool IpValidate(string ip)
        {
            var config = ServerConfigManage.Instance.GetServerConfig();
            bool isPass = true;
            if (isPass && config.IsStartIpWhiteList)
            {
                isPass = config.IpWhiteList.Contains(ip);
            }
            if (isPass && config.IsStartIpBlackList)
            {
                isPass = !config.IpBlackList.Contains(ip);
            }
            return isPass;
        }

        public static bool ApiValidate(string api, string appid)
        {
            var config = ServerConfigManage.Instance.GetServerConfig();
            if (config.IsStartApiControl)
            {
                var apis = AppManage.Instance.GetAppApiResource(appid);
                return apis != null && apis.Contains(api);
            }
            return true;
        }
View Code

GetServerConfig()是從DB/Cache里面獲取服務器的自定義配置,然后看是否開啟ip白名單/黑名單,下面的代碼同理,是否開啟權限驗證。

那認證服務器到這里實際上就結束了,關於isv申請appid/appsecret。然后用戶同意授權以后,存儲appid和user之間的關聯關系,就需要看客自行實現了。

 

另外有一個擴展代碼這里也提一下,就是關於ApiResponseEntity的返回值處理,代碼如下:

public static HttpResponseMessage ToHttpResponseMessage(this ResponseEntity rep, bool isEncrypt = false)
        {
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent
                    (
                    isEncrypt
                    ? EncryptHelper.Base64Replace(EncryptHelper.AESEncryptBase64(JsonHelper.ToJson(rep), Config.ApiEncryptKey))
                    : JsonHelper.ToJson(rep), 
                    System.Text.Encoding.UTF8,
                    "application/json"
                    )
            };
        }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM