自己开发实现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(); } }
创建token的算法可以自行实现,我是将新生成的Guid做了一下md5处理,代码如下:

public string CreateToken(string appid) { string token = Guid.NewGuid().ToString().ToMd5(); Set(token, appid); return token; }
上文可以看到,在生成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)); }
为什么要用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); } } }
使用比较传统的方式,继承于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; }
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" ) }; }
本人对代码不做任何知识产权限制,也不保证所有的代码皆为原创。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
2014-10-30 最坏的设计 写最合理的程序