自己開發實現OAuth做webapi認證,oauthwebapi
看到園子裡面有人寫的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