zoukankan      html  css  js  c++  java
  • Nginx集群之SSL证书的WebApi令牌验证

    目录

    1       大概思路... 1

    2       Nginx集群之SSL证书的WebApi令牌验证... 1

    3       Openssl生成SSL证书... 2

    4       编写.NET WebApi的OnAuthorization身份验证... 2

    5       编写.NET WebApi的ActionFilterAttribute令牌验证... 4

    6       编写.NET WebApi的服务端... 6

    7       编写.NET WebApi的客户端... 7

    8       部署WebApi到局域网内3台PC机... 13

    9       Nginx集群配置搭建... 13

    10     运行结果... 15

    11     总结... 16

    1       大概思路

    l  Nginx集群之SSL证书的WebApi令牌验证

    l  Openssl生成SSL证书

    l  编写.NET WebApi的OnAuthorization身份验证

    l  编写.NET WebApi的ActionFilterAttribute令牌验证

    l  编写.NET WebApi的服务端

    l  编写.NET WebApi的客户端

    l  部署WebApi到局域网内3台PC机

    l  Nginx集群配置搭建

    l  运行结果

    l  总结

    2       Nginx集群之SSL证书的WebApi令牌验证

    Nginx在WebApi集群,除了OAUTH身份验证外,针对移动端的手机、平板电脑等,还经常使用Token令牌验证,通过服务器授权发出有效期的Token,客户端通过此Token在当前有效期内,进行访问获取信息数据。

    Token验证在很多方面都广泛应用,举一个实际应用场景:A客户想通过接收邮件或者短信网址打开一个URL的PDF报表,但是又不想安装APP、或者访问我们的系统,连登录都不想登录。这时候,便可以使用一个有效期的Token,然后结合URL发送给用户,过了有效期,当前URL就失效。便可以解决用户临时访问的问题。

    以下是本文讲述的主要结构图:

    客户端输入用户名密码服务器,通过了用户名密码验证,其中一台WebApi服务器生成一个Token并返回https的响应。客户端收到Token保存在本地,带上token发出ajax请求、WebRequest请求,经过Action过滤器的检验,访问Action并返回数据。

    Token令牌身份验证机制:

    Nginx集群之SSL证书的WebApi令牌验证,如下图所示:

    3       Openssl生成SSL证书

    请参照《Nginx集群之SSL证书的WebApi微服务》

    http://www.cnblogs.com/yongfeng/p/7921905.html

    4       编写.NET WebApi的OnAuthorization身份验证

    CustomAuthorizeAttribute.cs

    using System.Web.Http;
    using System.Web.Http.Controllers;
    
    namespace SSLWebApi.Controllers
    {
        public class CustomAuthorizeAttribute : AuthorizeAttribute
        {
            public override void OnAuthorization(HttpActionContext actionContext)
            {
                //判断用户是否登录
                if (actionContext.Request.Headers.Authorization != null)
                {
                    string userInfo = System.Text.Encoding.Default.GetString(System.Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter));
                    //用户验证逻辑  
                    if (string.Equals(userInfo, string.Format("{0}:{1}", "zhyongfeng", "123456")))
                    {
                        IsAuthorized(actionContext);
                    }
                    else
                    {
                        HandleUnauthorizedRequest(actionContext);
                    }
                }
                else
                {
                    HandleUnauthorizedRequest(actionContext);
                }
            }
            protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
            {
                var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
                challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
                throw new System.Web.Http.HttpResponseException(challengeMessage);
            }
        }
    }

    生成Token

    BaseController.cs

    using SSLWebApi.Models;
    using System;
    using System.Web;
    using System.Web.Http;
    
    namespace SSLWebApi.Controllers
    {
        /// <summary>
        /// BaseController继承BaseController则需要身份验证
        /// </summary>
        [CustomAuthorize]
        [RoutePrefix("api/Base")]
        public class BaseController : ApiController
        {
            [HttpGet]
            [Route("Login")]
            public string Login(string userId)
            {
                if (HttpRuntime.Cache.Get(userId) == null)
                {
                    return CreateToken(userId);
                }
                else
                {
                    HttpRuntime.Cache.Remove(userId);
                    return CreateToken(userId);
                }
            }
    
            /// <summary>
            /// 生成token
            /// </summary>
            /// <param name="userId"></param>
            /// <returns></returns>
            private string CreateToken(string userId)
            {
                Token token = new Token();
                token.UserId = userId;
                token.SignToken = Guid.NewGuid();
                token.Seconds = 12;
                token.ExpireTime = DateTime.Now.AddSeconds(token.Seconds);
                HttpRuntime.Cache.Insert(token.UserId, token, null, token.ExpireTime, TimeSpan.Zero);
                return token.SignToken.ToString();
            }
        }
    }

    5       编写.NET WebApi的ActionFilterAttribute令牌验证

    WebApiSecurityFilter.cs

    using SSLWebApi.Models;
    using System;
    using System.Linq;
    using System.Net.Http;
    using System.Web;
    using System.Web.Http.Controllers;
    using System.Web.Http.Filters;
    
    namespace SSLWebApi.Filter
    {
        public class WebApiSecurityFilter : ActionFilterAttribute
        {
            public override void OnActionExecuting(HttpActionContext actionContext)
            {
    
                if (actionContext.ActionDescriptor.ActionName == "Login")
                {
                    //登录成功则生成token
                    base.OnActionExecuting(actionContext);
                    return;
                }
                else
                {
                    //判断token令牌
                    HttpRequestMessage request = actionContext.Request;
                    string staffid = request.Headers.Contains("userid") ? HttpUtility.UrlDecode(request.Headers.GetValues("userid").FirstOrDefault()) : string.Empty;
                    string timestamp = request.Headers.Contains("timestamp") ? HttpUtility.UrlDecode(request.Headers.GetValues("timestamp").FirstOrDefault()) : string.Empty;
                    string nonce = request.Headers.Contains("nonce") ? HttpUtility.UrlDecode(request.Headers.GetValues("nonce").FirstOrDefault()) : string.Empty;
                    string signature = request.Headers.Contains("signature") ? HttpUtility.UrlDecode(request.Headers.GetValues("signature").FirstOrDefault()) : string.Empty;
    
                    if (String.IsNullOrEmpty(staffid) || String.IsNullOrEmpty(timestamp) || String.IsNullOrEmpty(nonce) || String.IsNullOrEmpty(signature))
                    {
                        //令牌检验不通过
                        actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
                        return;
                    }
                    else
                    {
                        //令牌检验token失效
                        if (HttpRuntime.Cache.Get(staffid) == null)
                        {
                            actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
                            return;
                        }
                        else
                        {
                            //停牌检验2:判断timespan是否失效
                            Token token = (Token)HttpRuntime.Cache.Get(staffid);
                            double ts1 = 0;
                            bool timespanvalidate = double.TryParse(timestamp, out ts1);
                            double ts2 = (token.ExpireTime - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds;
                            bool flag = (ts2 - ts1) > token.Seconds ? true : false;
                            bool tokenFlag = (token.SignToken.ToString() == signature) ? true : false;
                            if (timespanvalidate && (!flag) && tokenFlag)
                            {
                                //时间转换成功、时间有效、token值相等
                                //令牌通过
                                base.OnActionExecuting(actionContext);
                                return;
                            }
                            else
                            {
                                actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
                                return;
                            }
                        }
                    }
                }
            }
    
        }
    }

    6       编写.NET WebApi的服务端

    UserController.cs

    using System;
    using System.Net;
    using System.Web.Http;
    
    namespace SSLWebApi.Controllers
    {
        [RoutePrefix("api/User")]
        public class UserController : ApiController
        {
            /// <summary>
            /// 获取当前用户信息
            /// </summary>
            /// <param name="msg"></param>
            /// <returns></returns>
            [HttpPost]
            [Route("PostMessage")]
            public string PostMessage(dynamic obj)
            {
                return string.Format("当前输入的消息是:{0}", Convert.ToString(obj.msg));
            }
    
            [Route("GetMachine")]
            public string GetMachine()
            {
                string AddressIP = string.Empty;
                foreach (IPAddress _IPAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
                {
                    if (_IPAddress.AddressFamily.ToString() == "InterNetwork")
                    {
                        AddressIP = _IPAddress.ToString();
                    }
                }
                return string.Format("当前WebApi部署的IP是:{0}", AddressIP);
            }
        }
    }

    7       编写.NET WebApi的客户端

    WebApiHelper.cs

    using Newtonsoft.Json;
    using System;
    using System.IO;
    using System.Net;
    using System.Text;
    
    namespace SSLWebApiClient.Common
    {
        public class WebApiHelper
        {
            /// <summary>
            /// Post请求
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="url">url</param>
            /// <param name="data">数据</param>
            /// <param name="userid">帐户</param>
            /// <param name="signature">数字签名</param>
            /// <returns></returns>
            public static string Post(string url, string data, string userid, string signature)
            {
                return PostData(url, data, userid, signature);
            }
    
            /// <summary>
            /// Post请求
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="url">url</param>
            /// <param name="data">数据</param>
            /// <param name="userid">帐户</param>
            /// <param name="signature">数字签名</param>
            /// <returns></returns>
            public static T Post<T>(string url, string data, string userid, string signature)
            {
                return JsonConvert.DeserializeObject<T>(Post(url, data, userid, signature));
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="webApi"></param>
            /// <param name="queryStr"></param>
            /// <param name="userid"></param>
            /// <param name="signature"></param>
            /// <returns></returns>
            public static string Get(string webApi, string queryStr, string userid, string signature)
            {
                return GetData(webApi, queryStr, userid, signature);
            }
    
            /// <summary>
            /// Get请求
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="webApi"></param>
            /// <param name="query"></param>
            /// <param name="queryStr"></param>
            /// <param name="userid"></param>
            /// <param name="signature"></param>
            /// <returns></returns>
            public static T Get<T>(string webApi, string queryStr, string userid, string signature)
            {
                return JsonConvert.DeserializeObject<T>(GetData(webApi, queryStr, userid, signature));
            }
    
            /// <summary>  
            /// 获取时间戳  
            /// </summary>  
            /// <returns></returns>  
            private static string GetTimeStamp()
            {
                TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                return ts.TotalSeconds.ToString();
            }
    
    
            /// <summary>  
            /// 获取随机数
            /// </summary>  
            /// <returns></returns>  
            private static string GetRandom()
            {
                Random rd = new Random(DateTime.Now.Millisecond);
                int i = rd.Next(0, int.MaxValue);
                return i.ToString();
            }
            /// <summary>
            /// Post请求
            /// </summary>
            /// <param name="url"></param>
            /// <param name="data"></param>
            /// <param name="userid">用户名称</param>
            /// <param name="signature">数字签名</param>
            /// <returns></returns>
            private static string PostData(string url, string data, string userid, string signature)
            {
                try
                {
                    byte[] bytes = Encoding.UTF8.GetBytes(data);
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    
                    string timeStamp = GetTimeStamp();
                    string nonce = GetRandom();
                    //加入头信息
                    //当前请求用户
                    request.Headers.Add("userid", userid);
                    //发起请求时的时间戳(单位:秒)
                    request.Headers.Add("timestamp", timeStamp);
                    //发起请求时的时间戳(单位:秒)
                    request.Headers.Add("nonce", nonce);
                    //当前请求内容的数字签名
                    request.Headers.Add("signature", signature);
    
                    //写数据
                    request.Method = "POST";
                    request.ContentLength = bytes.Length;
                    request.ContentType = "application/json";
                    request.GetRequestStream().Write(bytes, 0, bytes.Length);
                    //读数据
                    request.Timeout = 300000;
                    request.Headers.Set("Pragma", "no-cache");
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    Stream streamReceive = response.GetResponseStream();
                    StreamReader streamReader = new StreamReader(streamReceive, Encoding.UTF8);
                    string strResult = streamReader.ReadToEnd();
                    
                    //关闭流
                    //reqstream.Close();
                    streamReader.Close();
                    streamReceive.Close();
                    request.Abort();
                    response.Close();
                    return strResult;
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
            }
    
            /// <summary>
            /// Get请求
            /// </summary>
            /// <param name="webApi"></param>
            /// <param name="queryStr"></param>
            /// <param name="userid"></param>
            /// <param name="signature"></param>
            /// <returns></returns>
            private static string GetData(string webApi, string queryStr, string userid, string signature)
            {
                try
                {
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(webApi + "?" + queryStr);
                    string timeStamp = GetTimeStamp();
                    string nonce = GetRandom();
                    //加入头信息
                    //当前请求用户
                    request.Headers.Add("userid", userid);
                    //发起请求时的时间戳(单位:秒)
                    request.Headers.Add("timestamp", timeStamp);
                    //发起请求时的时间戳(单位:秒)
                    request.Headers.Add("nonce", nonce);
                    //当前请求内容的数字签名
                    request.Headers.Add("signature", signature);
    
                    request.Method = "GET";
                    request.ContentType = "application/json";
                    request.Timeout = 90000;
                    request.Headers.Set("Pragma", "no-cache");
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    Stream streamReceive = response.GetResponseStream();
                    StreamReader streamReader = new StreamReader(streamReceive, Encoding.UTF8);
                    string strResult = streamReader.ReadToEnd();
    
                    streamReader.Close();
                    streamReceive.Close();
                    request.Abort();
                    response.Close();
                    return strResult;
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
            }
    
        }
    }

    Program.cs

    using System;
    using System.IO;
    using System.Net;
    using System.Text;
    using Newtonsoft.Json;
    namespace SSLWebApiClient
    {
        class Program
        {
            static void Main(string[] args)
            {
                string basicUrl = "http://localhost:20107";
                string html = string.Empty;
                for (int i = 0; i < 1; i++)
                {
                    //https协议基本认证 Authorization
                    string url = basicUrl + "/api/base/Login?userId=zhyongfeng";
                    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
                    NetworkCredential credential = new NetworkCredential("zhyongfeng", "123456");
                    req.Credentials = credential;
                    HttpWebResponse response = (HttpWebResponse)req.GetResponse();
                    Stream responseStream = response.GetResponseStream();
                    StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8);
                    html = streamReader.ReadToEnd().Replace(""", "");
                    Console.WriteLine("Token服务器保存时间为12s");
                    Console.WriteLine(String.Format("服务器返回的Token值为:{0}", html));
                }
                //token设置了12s有效期
                for (int j = 0; j < 5; j++)
                {
                    System.Threading.Thread.Sleep(1000);
                    string url = basicUrl + "/api/user/PostMessage";
                    Console.WriteLine(Common.WebApiHelper.Post(url, JsonConvert.SerializeObject(new { msg = "hello" }), "zhyongfeng", html));
                }
                for (int j = 0; j < 10; j++)
                {
                    System.Threading.Thread.Sleep(1000);
                    string url = basicUrl + "/api/user/GetMachine";
                    Console.WriteLine(Common.WebApiHelper.Get(url, null, "zhyongfeng", html));
                }
                Console.Read();
            }
    
        }
    }

    8       部署WebApi到局域网内3台PC机

    将WebApi部署到以下10.92.202.56的3台PC机

    9       Nginx集群配置搭建

    通过自主义域名zhyongfeng.com:80端口进行负载均衡集群访问,则访问C:WindowsSystem32driversetchosts,添加下列“本机IP 自定义的域名”:

    10.93.85.66     zhyongfeng.com

    Nginx的集群配置:

    #user  nobody;
    worker_processes  1;
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
        #server {
        #    listen       80;
        #    server_name  localhost;
        #    location / {
        #        root   html;
        #        index  index.html index.htm;
        #    }
        #    error_page   500 502 503 504  /50x.html;
        #    location = /50x.html {
        #        root   html;
        #    }
        #}
    
        upstream zhyongfeng.com {
            server    10.92.202.56:560;
            server    10.92.202.57:570; 
            server    10.92.202.58:580;
        }
        server {
            listen       80;
            server_name  zhyongfeng.com;
            rewrite ^(.*)$  https://$host$1 permanent;
        }
        # HTTPS server
        #
        server {
            listen       443 ssl;
            server_name  zhyongfeng.com;
            ssl_certificate      server.crt;
            ssl_certificate_key  server_nopass.key;
        #    ssl_session_cache    shared:SSL:1m;
        #    ssl_session_timeout  5m;
        #    ssl_ciphers  HIGH:!aNULL:!MD5;
        #    ssl_prefer_server_ciphers  on;
            location / {
                proxy_pass   http://zhyongfeng.com;
            }
        }
    }

    运行CMD:

    D:DTLDownLoads
    ginx-1.10.2>start nginx
    
    D:DTLDownLoads
    ginx-1.10.2>nginx -s reload

    10             运行结果

    因只有其中一台计算生成了相应的Token值(这里只做其中一台,如果要三台都能够响应,可以用redis做相应的Token值存储)

    其中一台10.92.202.58生成了Token值,运行结果如下:

    • 访问指定的http://10.92.202.56:560

    因Token值设定了12s后失效,则返回“远程服务器返回错误:<403>已禁止”,运行效果如下:

    11             总结

    Nginx基于SSL协议下,客户端利用 http basic身份验证,访问WebApi获得Token,通过Token值获取相应的权限数据,使系统的安全性有了保障,同时灵活运用Token身份令牌,可以实现时效性的数据访问。基于Token的身份验证,针对前后端分离有着很大的作用。例如手机移动端、平板电脑。

    源代码下载:

    http://download.csdn.net/download/ruby_matlab/10146669

    PDF下载:

    Nginx集群之SSL证书的WebApi令牌验证.pdf

  • 相关阅读:
    JAVA 设计模式 组合模式
    JAVA 设计模式 模板方法模式
    SpringBoot 数据篇之使用JDBC
    [Spring]01_环境配置
    [spring]03_装配Bean
    [Spring]04_最小化Spring XML配置
    [Quartz笔记]玩转定时调度
    [Spring]支持注解的Spring调度器
    asp.net core 系列 13 日志
    asp.net core 系列 12 选项 TOptions
  • 原文地址:https://www.cnblogs.com/yongfeng/p/7988218.html
Copyright © 2011-2022 走看看