目录

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令牌验证.pdf

Nginx集群之SSL证书的WebApi令牌验证的更多相关文章

  1. Nginx集群之SSL证书的WebApi身份验证

    目录 1       大概思路... 1 2       Nginx集群之SSL证书的WebApi身份验证... 1 3       AuthorizeAttribute类... 2 4       ...

  2. Nginx集群之SSL证书的WebApi微服务

    目录 1       大概思路... 1 2       Nginx集群之SSL证书的WebApi微服务... 1 3       HTTP与HTTPS(SSL协议)... 1 4       Ope ...

  3. Nginx集群之基于Redis的WebApi身份验证

    目录 1       大概思路... 1 2       Nginx集群之基于Redis的WebApi身份验证... 1 3       Redis数据库... 2 4       Visualbox ...

  4. 扎实基础之从零开始-Nginx集群分布式.NET应用

    1       扎实基础之快速学习Nginx Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.其特点是占有内存少 ...

  5. Nginx集群之WCF分布式身份验证(支持Soap)

    目录 1       大概思路... 1 2       Nginx集群之WCF分布式身份验证... 1 3       BasicHttpBinding.ws2007HttpBinding. 2 4 ...

  6. linux 下nginx 集群CAS单点登录实现

    1.单点登录服务器CAS应用配置于tomcat下. 1)key生成: keytool -genkey -alias mycas -keyalg RSA -keysize 2048 -keystore ...

  7. Nginx集群之.Net打造WebApp(支持IOS和安卓)

    目录 1       大概思路... 1 2       Nginx集群之.Net打造WebApp(支持IOS和安卓) 1 3       安卓模拟器... 1 4       MUI框架... 3 ...

  8. nginx 集群介绍

    nginx 集群介绍 完成一次请求的步骤 1)用户发起请求 2)服务器接受请求 3)服务器处理请求(压力最大) 4)服务器响应请求 缺点:单点故障 单台服务器资源有限 单台服务器处理耗时长 ·1)部署 ...

  9. 庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群

    庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群 一.简介      前面的两篇文章,我们已经介绍了Net Core项目基于Docker容器部署在Linux服 ...

随机推荐

  1. javascript设计模式——职责链模式

    前面的话 职责链模式的定义是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.职责链模式的名字非常形象,一 ...

  2. vue.js介绍,常用指令,事件,以及制作简易留言版

    一.vue是什么? 一个mvvm框架(库).和angular类似,比较容易上手.小巧,让我们的代码更加专注于业务逻辑,而不是去关注DOM操作 二.vue和angular之间的区别 vue--简单易学 ...

  3. mybatis延迟加载一对多

    1.实体类 package cn.bdqn.bean; import java.util.Set; /** *国家的实体类 */ public class Country { private Inte ...

  4. mongoDB之数据库操作

    mongoDB中的数据库操作 查看数据库名称: db 查看所有数据库: show dbs 切换数据库: use 数据库名称 注意:如果数据库不存在,则指向数据库,但不会创建.直到插入数据或者是创建集合 ...

  5. 单点登录(SSO)的设计

    一.前言 什么是SSO? SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.https://baike.baidu.c ...

  6. windows远程桌面连接的时候不显示本地盘符

    近期远程异地pc机部署项目,远程连上后不显示本地盘符,勾选驱动器也无效,试下例如以下方法 在远程主机的文件地址栏里面键入: \\tsclient\D 后面再加入上对应的盘符,你的盘符的名称是什么盘就加 ...

  7. 彻底弄懂 JavaScript 执行机制

    本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定 ...

  8. Ajax顺序执行

    循环中的Ajax 在前后端分离的项目中,Ajax是连接前后端的枢纽. 需求:有一个需要循环发起n次的请求,但是n次循环传参不同,我并不知道n是多少,并且要求能够保证返回顺序.JSONP用同步锁无效 示 ...

  9. 关于模式识别中的domain generalization 和 domain adaptation

    今晚听了李文博士的报告"Domain Generalization and Adaptation using Low-Rank Examplar Classifiers",讲的很精 ...

  10. 开源组件NanUI一周年 - 使用HTML/CSS/JS来构建.Net Winform应用程序界面

    NanUI是什么 NanUI基于ChromiumFX项目进行开发,它能让你在你的Winform应用程序中使用HTML5/CSS3/Javascript等网页技术来呈现用户界面(类似Electron). ...