Nginx集群之基于Redis的WebApi身份验证
目录
1 大概思路... 1
2 Nginx集群之基于Redis的WebApi身份验证... 1
3 Redis数据库... 2
4 Visualbox虚拟机ubuntu下的redis部署... 3
5 编写.NET WebApi的OnAuthorization身份验证... 6
6 编写.NET WebApi的ActionFilterAttribute令牌验证... 8
7 编写.NET WebApi的服务端... 10
8 编写.NET WebApi的客户端... 11
9 部署WebApi到本机... 17
10 Nginx集群配置搭建... 18
11 运行结果... 19
12 总结... 20
1 大概思路
l Nginx集群之基于Redis的WebApi身份验证
l Redis数据库
l Visualbox虚拟机ubuntu下的redis部署
l 编写.NET WebApi的OnAuthorization身份验证
l 编写.NET WebApi的ActionFilterAttribute令牌验证
l 编写.NET WebApi的服务端
l 编写.NET WebApi的客户端
l 部署WebApi到本机
l Nginx集群配置搭建
l 运行结果
l 总结
2 Nginx集群之基于Redis的WebApi身份验证
Nginx在集群上使用Redis数据库进行身份验证,达到了支持集群、分布式。在此基础上能够实现单点登录、时效性的访问,结合WebApi最大限度地发挥了后台身份验证的管理。
基于Redis的原因有几个:第一点是Redis是基于内存的数据库访问起来比较快,能够设置数据库存储的时效性;第二点,Redis数据库的语法比较简单、轻巧,在Linux、Windows服务器均可以一键安装完成;第三点,Redis支持数据的备份,即master-slave模式的数据备份。
以下是本文讲述的主要结构图:
客户端输入用户名密码服务器,通过了用户名密码验证,其中一台WebApi服务器生成一个Token并返回https的响应。客户端收到Token保存在本地,带上token发出ajax请求、WebRequest请求,经过Action过滤器的检验,访问Action并返回数据。
Token令牌身份验证机制:
Nginx集群之基于Redis的WebApi身份验证,如下图所示:
3 Redis数据库
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
具体可以在以下网址进行学习:
http://www.runoob.com/redis/redis-tutorial.html
4 Visualbox虚拟机ubuntu下的redis部署
(已安装Oracle VM VirtualBox、ubuntu-17.10-desktop-amd64.iso)
Redis安装可参考:http://www.runoob.com/redis/redis-install.html
- 虚拟机
因网络的限制,本文采取的是“网络地址转换(NAT)”方式,进行redis数据库连接访问。有条件的可以采用“桥接网上”的方式,便可以使多台PC机在同一redis数据库进行访问,达到集群的效果。
Linux的ubuntu打开6379端口:
- sudo iptables -I INPUT -p tcp --dport 6379 -j ACCEPT
具体如下所示:
登录Redis服务器,设置密码:
- redis-cli
- config set requirepass 123456
- 主机
Windows安装Telnet客户端,进行测试连接成功与否
运行CMD,输入命令行如下:
- Microsoft Windows [版本 6.1.7601]
- 版权所有 (c) 2009 Microsoft Corporation。保留所有权利。
- C:\Users\zhyongfeng>telnet 10.93.85.66 6379
测试成功
5 编写.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 ServiceStack.Redis;
- 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);
- //redis使用sudo iptables -A INPUT -p tcp --dport 6379 -j ACCEPT打开6379的端口
- //visualbox虚拟机使用“设置->网络->高级->端口转发”创建商品规则
- //连接远程visualbox虚拟机ubtuntu的redis,形成一个分布式的登录验证
- string remoteIp = "10.93.85.66";
- int remotePort = ;
- string remoteDbPassword = "";
- //RedisClient(地址,端口,密码,0)
- using (var client = new RedisClient(remoteIp, remotePort, remoteDbPassword, ))
- {
- string strToken = token.SignToken.ToString();
- if (client.Exists(userId) > )
- client.Del(userId);
- if (client.Add(userId, strToken))
- {
- //设置key的过期时间为12s
- client.Expire(userId, );
- return token.SignToken.ToString();
- }
- else
- return string.Format("远程虚拟机{0}的redis创建token失败", remoteIp);
- }
- }
- }
- }
6 编写.NET WebApi的ActionFilterAttribute令牌验证
WebApiSecurityFilter.cs
- using ServiceStack.Redis;
- 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
- {
- string remoteIp = "10.93.85.66";
- int remotePort = ;
- string remoteDbPassword = "";
- using (var client = new RedisClient(remoteIp, remotePort, remoteDbPassword, ))
- {
- //令牌检验是否存在这个用户
- if (client.Exists(staffid) > )
- {
- string tempStr = System.Text.Encoding.UTF8.GetString(client.Get(staffid));
- if (tempStr.Trim('"').Equals(signature))
- {
- //时间转换成功、时间有效、token值相等
- //令牌通过
- base.OnActionExecuting(actionContext);
- return;
- }
- else
- {
- actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
- return;
- }
- }
- else
- {
- actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
- return;
- }
- }
- }
- }
- }
- }
- }
7 编写.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);
- }
- }
- }
8 编写.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://zhyongfeng.com";
- 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("Redis Token服务器保存时间为12s");
- Console.WriteLine(String.Format("Redis服务器返回的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();
- }
- }
- }
9 部署WebApi到本机
将WebApi部署到以下10.93.85.66(因网络限制,所以这里只做单个集群,虚拟机ubuntu中的数据redis主要是NAT网络连接方式,使用端口转发进行访问)
10 Nginx集群配置搭建
通过自主义域名zhyongfeng.com:80端口进行负载均衡集群访问,则访问C:\Windows\System32\drivers\etc\hosts,添加下列“本机IP 自定义的域名”:
- 10.93.85.66 zhyongfeng.com
Nginx的集群配置:
- worker_processes 1;
- events {
- worker_connections 1024;
- }
- http {
- include mime.types;
- default_type application/octet-stream;
- sendfile on;
- keepalive_timeout 65;
- upstream zhyongfeng.com {
- server 10.93.85.66:20107;
- }
- server {
- listen 80;
- server_name localhost;
- location / {
- proxy_pass http://zhyongfeng.com;
- }
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }
- }
- }
运行CMD:
- D:\DTLDownLoads\nginx-1.10.2>start nginx
- D:\DTLDownLoads\nginx-1.10.2>nginx -s reload
11 运行结果
WebApi经过Action过滤器,生成了Token值,并存储到Redis,运行结果如下:
12 总结
Nginx集群使用Redis数据库,客户端利用 http basic身份验证,访问WebApi获得Token并将Token存储到Redis内在数据库,通过Token值获取相应的权限数据,这样子可以做到单点登录,集群分布式的身份验证效果。既方便了用户在整个业务领域的系统操作,同时可以为整个公司、集团等各个区域的系统进行统一有效的身份验证管理。
源代码下载:
http://download.csdn.net/download/ruby_matlab/10155491
PDF下载:
Nginx集群之基于Redis的WebApi身份验证.pdf
Nginx集群之基于Redis的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 Openssl生成SSL证书... 2 4 编写.NE ...
- Nginx集群之SSL证书的WebApi微服务
目录 1 大概思路... 1 2 Nginx集群之SSL证书的WebApi微服务... 1 3 HTTP与HTTPS(SSL协议)... 1 4 Ope ...
- Nginx集群配置与redis的session共享策略
一.什么是Nginx? Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器.Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Ramb ...
- 对Elastic集群内部配置TLS加密通信及身份验证
1.介绍 官方宣布从6.8和7.1开始,免费提供多项安全功能.其中包括tls加密通信,基于角色访问控制等功能. 可以使用企业CA证书来完成这一步骤,但是一般情况下,我们可以通过elasticsearc ...
- 扎实基础之从零开始-Nginx集群分布式.NET应用
1 扎实基础之快速学习Nginx Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.其特点是占有内存少 ...
- Nginx集群之WCF分布式身份验证(支持Soap)
目录 1 大概思路... 1 2 Nginx集群之WCF分布式身份验证... 1 3 BasicHttpBinding.ws2007HttpBinding. 2 4 ...
- 庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群
庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群 一.简介 前面的两篇文章,我们已经介绍了Net Core项目基于Docker容器部署在Linux服 ...
- Redis+Tomcat+Nginx集群实现Session共享,Tomcat Session共享
Redis+Tomcat+Nginx集群实现Session共享,Tomcat Session共享 ============================= 蕃薯耀 2017年11月27日 http: ...
随机推荐
- 【docker 一】入门实践、环境部署、基本操作指令、镜像库、数据卷
简述 `docker是如火如荼的容器技术,今后会陆续上传关于微服务技术的学习笔记,希望能和大家一起学习一起分享!` docker环境搭建 1.获取最新版的Docker安装包 $ wget -qO- h ...
- vue.js之生命周期,防止闪烁,计算属性的使用,vue实例简单方法和循环重复数据
摘要:今天是比较糟糕的一天没怎么学习,原因是学校的wifi连不上了~~.今天学习一下vue的生命周期,如何防止闪烁(也就是用户看得到花括号),计算属性的使用,vue实例简单方法,以及当有重复数据时如何 ...
- Flask-admin 笔记一 (快速启用)
1,快速启用 1) 安装flask-admin pip install flask-admin 2) 配置使用 from flask import Flask from flask_admin i ...
- 基于跳跃表的 ConcurrentSkipListMap 内部实现(Java 8)
我们知道 HashMap 是一种键值对形式的数据存储容器,但是它有一个缺点是,元素内部无序.由于它内部根据键的 hash 值取模表容量来得到元素的存储位置,所以整体上说 HashMap 是无序的一种容 ...
- 哈希表(散列)HashTable实现
近期刷Leetcode发现凡是找字符串中反复字符或者数组中找反复数据的时候就不知道从何下手了. 所以决定学习一下哈希表解题.哈希表的原理主要是解决分类问题,hash表是介于链表和二叉树之间的一种中间结 ...
- iKcamp团队制作|基于Koa2搭建Node.js实战(含视频)☞ 路由koa-router
路由koa-router--MVC 中重要的环节:Url 处理器
- 【 全干货 】5 分钟带你看懂 Docker !
欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者丨唐文广:腾讯工程师,负责无线研发部地图测试. 导语:Docker,近两年才流行起来的超轻量级虚拟机,它可以让你轻松完成持续集成.自动交付 ...
- shell脚本小案例
1.获取远程ftp数据到本地目录 #!/bin/bash ftp -n<<! open 135.0.24.19 user exchange exchange binary cd /idep ...
- Xss过滤,只json型数据过滤,图片文件不过滤,采用jsoup
package com.huaji.fes.filter; import javax.servlet.http.HttpServletRequest;import javax.servlet.http ...
- 如何修改maven默认仓库(即repository)的路径
如何修改maven默认仓库(即repository)的路径 1 在maven的安装目录下,修改Eclipse(或IntelliJ IDEA)的MAVEN的存储位置,点击Browser按钮,选择set ...