• 自己开发实现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
    本人对代码不做任何知识产权限制,也不保证所有的代码皆为原创。
  • 相关阅读:
    博客图片上传picgo工具安装配置github图传使用
    安装配置hexo icarus主题配置
    通往远方的道路总是漫长的
    java 关于值引用、地址引用的问题
    Java设计模式の迭代器模式
    Java设计模式の模版方法模式
    vim 常用快捷键(整理版)
    Java设计模式の责任链模式
    Java设计模式の代理模式
    java rmi远程方法调用实例
  • 原文地址:https://www.cnblogs.com/selfteam/p/4923098.html
Copyright © 2020-2023  润新知