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", "")))
- {
- 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 = ;
- 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 = ;
- bool timespanvalidate = double.TryParse(timestamp, out ts1);
- double ts2 = (token.ExpireTime - new DateTime(, , , , , , )).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(, , , , , , );
- return ts.TotalSeconds.ToString();
- }
- /// <summary>
- /// 获取随机数
- /// </summary>
- /// <returns></returns>
- private static string GetRandom()
- {
- Random rd = new Random(DateTime.Now.Millisecond);
- int i = rd.Next(, 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, , bytes.Length);
- //读数据
- request.Timeout = ;
- 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 = ;
- 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 = ; i < ; 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", "");
- 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 = ; j < ; j++)
- {
- System.Threading.Thread.Sleep();
- string url = basicUrl + "/api/user/PostMessage";
- Console.WriteLine(Common.WebApiHelper.Post(url, JsonConvert.SerializeObject(new { msg = "hello" }), "zhyongfeng", html));
- }
- for (int j = ; j < ; j++)
- {
- System.Threading.Thread.Sleep();
- 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:\Windows\System32\drivers\etc\hosts,添加下列“本机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\nginx-1.10.2>start nginx
- D:\DTLDownLoads\nginx-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令牌验证的更多相关文章
- Nginx集群之SSL证书的WebApi身份验证
目录 1 大概思路... 1 2 Nginx集群之SSL证书的WebApi身份验证... 1 3 AuthorizeAttribute类... 2 4 ...
- Nginx集群之SSL证书的WebApi微服务
目录 1 大概思路... 1 2 Nginx集群之SSL证书的WebApi微服务... 1 3 HTTP与HTTPS(SSL协议)... 1 4 Ope ...
- Nginx集群之基于Redis的WebApi身份验证
目录 1 大概思路... 1 2 Nginx集群之基于Redis的WebApi身份验证... 1 3 Redis数据库... 2 4 Visualbox ...
- 扎实基础之从零开始-Nginx集群分布式.NET应用
1 扎实基础之快速学习Nginx Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.其特点是占有内存少 ...
- Nginx集群之WCF分布式身份验证(支持Soap)
目录 1 大概思路... 1 2 Nginx集群之WCF分布式身份验证... 1 3 BasicHttpBinding.ws2007HttpBinding. 2 4 ...
- linux 下nginx 集群CAS单点登录实现
1.单点登录服务器CAS应用配置于tomcat下. 1)key生成: keytool -genkey -alias mycas -keyalg RSA -keysize 2048 -keystore ...
- Nginx集群之.Net打造WebApp(支持IOS和安卓)
目录 1 大概思路... 1 2 Nginx集群之.Net打造WebApp(支持IOS和安卓) 1 3 安卓模拟器... 1 4 MUI框架... 3 ...
- nginx 集群介绍
nginx 集群介绍 完成一次请求的步骤 1)用户发起请求 2)服务器接受请求 3)服务器处理请求(压力最大) 4)服务器响应请求 缺点:单点故障 单台服务器资源有限 单台服务器处理耗时长 ·1)部署 ...
- 庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群
庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群 一.简介 前面的两篇文章,我们已经介绍了Net Core项目基于Docker容器部署在Linux服 ...
随机推荐
- Cs Round#54 E Late Edges
题意:给定一个无向图,你从结点1开始走,每经过一条边需要1的时间,每条边都有一个开放时间,只有当目前所用的时间大于等于开放时间时,这条边才可以被经过.每一单位时间你都必须经过一条边,问最快什么时候可以 ...
- mac cocos2dx android
1. localhost:proj.android mxhd4$ ./build_native.sh 报错 Compile++ thumb : cocosdenshion_static <= ...
- uva11059(最大乘积)
Problem D - Maximum Product Time Limit: 1 second Given a sequence of integers S = {S1, S2, ..., Sn}, ...
- Best time to buy and sell stocks IV
题目 https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ Say you have an array for which ...
- Mongodb 3.4 + Centos6.5 配置 + mysql.sql转为csv 或 json导入和导出Mongo (64位系统)
Centos下通过yum安装步骤如下: 声明:相对比那些用源码安装,少了配置和新建log和data目录,这种简单粗暴, ,创建仓库文件, vi /etc/yum.repos.d/mongodb-org ...
- linux 虚拟机模拟配置网络路由环境-简版
前言:网络路由不管是平常在家里,还是在公司中,都是必需配置的,所以还是非常重要的,今天小编就给大家做个配置网络路由配置的小实验,仅供大家参考. 一.首先,来简单介绍一下网络路由. 1. 网络路由: ...
- [CSS]第一项和最后一项样式
列表项的第一项距离顶部0.2rem,最后一项距离底部0.5rem .item:first-child { padding-top: .2rem; } .item:last-child { paddin ...
- Java Web Session设置
一.前言 在做 java web项目时,我们很多时候都要用到 Session,那么我就简单的写一下 Session 的写法. 二.代码实现 Servlet Session 的设置 package co ...
- InfluxDB:cannot use field in group by clause
最近在使用InfluxDB时,发现一个很奇怪的问题,一个本来正常的功能,做了一次改动后,就不能正常显示了. 一.查询语句 SELECT MEMORY FROM "ACM_PROCESS_MO ...
- SparkStreaming读取Kakfa数据时发生OffsetOutOfRangeException异常
参考文章:http://www.jianshu.com/p/791137760c14 运行SparkStreming程序一段时间后,发现产生了异常: ERROR JobScheduler: Error ...