localStorage使用总结 - 谢灿勇 - 博客园

mikel阅读(870)

来源: localStorage使用总结 – 谢灿勇 – 博客园

一、什么是localStorage、sessionStorage

在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。

 

二、localStorage的优势与局限

localStorage的优势

1、localStorage拓展了cookie的4K限制

2、localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数据库,相比于cookie可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的

localStorage的局限

1、浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性

2、目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换

3、localStorage在浏览器的隐私模式下面是不可读取的

4、localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡

5、localStorage不能被爬虫抓取到

localStorage与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,sessionStorage中的键值对会被清空

这里我们以localStorage来分析

 

三、localStorage的使用

localStorage的浏览器支持情况:

这里要特别声明一下,如果是使用IE浏览器的话,那么就要UserData来作为存储,这里主要讲解的是localStorage的内容,所以userData不做过多的解释,而且以博主个人的看法,也是没有必要去学习UserData的使用来的,因为目前的IE6/IE7属于淘汰的位置上,而且在如今的很多页面开发都会涉及到HTML5\CSS3等新兴的技术,所以在使用上面一般我们不会去对其进行兼容

首先在使用localStorage的时候,我们需要判断浏览器是否支持localStorage这个属性

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
            return false;
        }else{
            //主逻辑业务
        }
复制代码

 

localStorage的写入,localStorage的写入有三种方法,这里就一一介绍一下

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
            return false;
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.a=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(typeof storage["a"]);
            console.log(typeof storage["b"]);
            console.log(typeof storage["c"]);
        }
复制代码

 

运行后的结果如下:

这里要特别说明一下localStorage的使用也是遵循同源策略的,所以不同的网站直接是不能共用相同的localStorage

最后在控制台上面打印出来的结果是:

不知道各位读者有没有注意到,刚刚存储进去的是int类型,但是打印出来却是string类型,这个与localStorage本身的特点有关,localStorage只支持string类型的存储。

localStorage的读取

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.a=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(typeof storage["a"]);
            console.log(typeof storage["b"]);
            console.log(typeof storage["c"]);
            //第一种方法读取
            var a=storage.a;
            console.log(a);
            //第二种方法读取
            var b=storage["b"];
            console.log(b);
            //第三种方法读取
            var c=storage.getItem("c");
            console.log(c);
        }
复制代码

 

这里面是三种对localStorage的读取,其中官方推荐的是getItem\setItem这两种方法对其进行存取,不要问我这个为什么,因为这个我也不知道

我之前说过localStorage就是相当于一个前端的数据库的东西,数据库主要是增删查改这四个步骤,这里的读取和写入就相当于增、查的这两个步骤

下面我们就来说一说localStorage的删、改这两个步骤

改这个步骤比较好理解,思路跟重新更改全局变量的值一样,这里我们就以一个为例来简单的说明一下

 

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.b=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(storage.a);
            // console.log(typeof storage["a"]);
            // console.log(typeof storage["b"]);
            // console.log(typeof storage["c"]);
            /*分割线*/
            storage.a=4;
            console.log(storage.a);
        }
复制代码

 

这个在控制台上面我们就可以看到已经a键已经被更改为4了

localStorage的删除

1、将localStorage的所有内容清除

复制代码
var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            console.log(storage);
            storage.clear();
            console.log(storage);
复制代码

 

2、 将localStorage中的某个键值对删除

 

复制代码
var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            console.log(storage);
            storage.removeItem("a");
            console.log(storage.a);
复制代码

 

控制台查看结果

localStorage的键获取

复制代码
var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            for(var i=0;i<storage.length;i++){
                var key=storage.key(i);
                console.log(key);
            }
复制代码

 

使用key()方法,向其中出入索引即可获取对应的键

 

四、localStorage其他注意事项

一般我们会将JSON存入localStorage中,但是在localStorage会自动将localStorage转换成为字符串形式

这个时候我们可以使用JSON.stringify()这个方法,来将JSON转换成为JSON字符串

示例:

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            var data={
                name:'xiecanyong',
                sex:'man',
                hobby:'program'
            };
            var d=JSON.stringify(data);
            storage.setItem("data",d);
            console.log(storage.data);
        }
复制代码

 

读取之后要将JSON字符串转换成为JSON对象,使用JSON.parse()方法

复制代码
var storage=window.localStorage;
            var data={
                name:'xiecanyong',
                sex:'man',
                hobby:'program'
            };
            var d=JSON.stringify(data);
            storage.setItem("data",d);
            //将JSON字符串转换成为JSON对象输出
            var json=storage.getItem("data");
            var jsonObj=JSON.parse(json);
            console.log(typeof jsonObj);
复制代码

打印出来是Object对象

另外还有一点要注意的是,其他类型读取出来也要进行转换

跟我一起学.NetCore之Swagger让前后端不再烦恼及界面自定义

mikel阅读(768)

来源: 跟我一起学.NetCore之Swagger让前后端不再烦恼及界面自定义

前言

随着前后端分离开发模式的流行,接口对接、联调成为常事,前端同事会经常问:我需要调哪个接口?这个接口数据格式是啥?条件都传啥? 对于一些紧急接口可能会采取沟通对接,然后补文档,其他的都会回一句:看文档。 那难道要一边开发一边写文档吗?早些年是这样的,但对于后端同事就很不自在了,代码敲的正起劲,突然又要去写文档(不然前端同事会时刻催或是来沟通),这样效率显然不是很高。而Swagger就能很舒服的解决问题(当然也有其他方式,挑一个比较火的),用Swagger大概会有以下好处:

  • 良好的可视化界面,在线查看即可(可自定义);
  • 前后端对接方便,避免边撸代码边写文档;
  • 可以直接进行API运行,提高自测效率;
  • 文档生成方便,结合第三方API接口管理平台(YApi等)轻松生成文档(软件文件备案需要好多文档的)。不写文档,更多时间撸代码~~~

正文

直接上案例演示,老规矩,还是熟悉的WebApi项目:

运行演示:

如果注册组件时设置的名称和注册中间件时设置的json地址中的名称不一样,就会报以下错误:

轻松三步走完成Swagger的集成:安装包->注册组件->注册中间件;

但是运行起来的时候还需要手动输入Url地址,不太友好,希望运行起来就直接是Swagger的页面,如下优化一下代码:

这样就完事了吗? 当然没有,这样前端同事还得时刻找你问:我要用哪个接口?接口参数的字段都是啥意思?因为接口列表虽然展示出来了,但是不知道接口功能,传入的条件参数字段分别代表什么意思。

来,加个注释解决烦恼:

新加的用户接口已经自动列出来了,但是我们在代码中已经注释,Swagger界面还是没有显示,那是因为还没有指定Swagger的注释来源呢。这里先注意一个问题,如果Action不显示指定HttpMethod(如:HttpGet、HttpPost等),Swagger会报错,如下:

来,我们的注释还没显示出来呢,继续往下看看↓↓↓

  1. 先针对API项目和Model项目配置生成项目对应的xml文件;

  2. 增加代码,分别指定配置文件解析注释,然后运行演示;

这样就已经完成在Swagger在线文档中添加文字说明了,解决前后端对接的麻烦事。但是在编译运行的时候,会出现很多警告,提示没有注释(CS1591):

这是因为生成xml文件要求都需要注释导致,作为程序员的洁癖,当然不允许这种情况发生,小红框标识提示代码为1591,我们在项目右键->属性->生成界面中增加该代码即可,如下:

再编译运行一下,是不是警告没了,整洁就是舒服~~~~~

这里有个小细节,在配置xml读取注释时,如果不使用第二个参数,默认控制器的注释是不管用的,这里就不运行演示了,代码说明如下:

Swagger的集成使用差不多了,后续会在认证的时候做扩展。

对于Swagger的页面,开发已经是绝对能满足了,但总有一些审美比较严格,或是有一些页面定制化的需求,所以自定义页面就避免不了;小伙伴会发现,项目中只是安装了Swagger的包,没有对应的静态文件,那是怎么访问的呢?嗖的一下,灵光一闪,小伙伴一定想到之前文件系统中说到的嵌入文件(编译到程序集中的文件),默认情况下,Swagger的相关文件都是嵌入文件,所以在项目中看不到。自定义页面有三种方式:

  • Swagger的页面是前后端分离的,可直接下载下来扩展;
  • 直接写一个主页页面,然后注入相关JS,最后将其改为嵌入文件,显示指定加载页面;
  • 通过Swagger默认提供的API,注入相关的Js文件进行页面自定义;

相对来说,第一种比较清晰,而且代码与后端代码没有啥关系,所以这里就以第一种为例进行演示,其余两种留给小伙伴探索探索。

大概步骤:

  1. 先下载Swagger前后端项目文件(下载地址:https://github.com/swagger-api/swagger-ui/);
  2. 将下载下来的dist中文件拷贝到WebApi中的wwwroot目录;

  3. WebApi开启静态文件处理能力,即注册静态文件中间件;

  4. 修改静态文件(文件在本地,想怎么搞就怎么搞);

  5. 运行看效果:

这里只是提供思路,样式不好看别说我(小伙伴不会说,对不对),美化只是时间问题嘛,自定义界面就是这么简单。

补充两个小技巧:

  • 通常有一些接口已经过时,但有不能马上废弃,可以给一个提示;加[Obsolete]特性代表其已经废弃,但还可以用,有一个替换的过渡期。
  • 有时候有些接口不想显示在Swagger文档中,可以加[ApiExplorerSettings(IgnoreApi = true)]

总结

看到这篇小伙伴可能会好奇,这么简单的集成,为什么要长篇大论的说那么多。其实我是站在我之前学习的角度,想让每个用法都清楚的表达出来,在使用的时候也知道为什么会这样,所以后续的文章会循序渐进,不会一下跳到Docker部署,网关使用之类的主题,如果没有学过Docker,看着文档也很懵,就算照着案例做出来,收获也不是很大;别急,该说的都会一一到来,同时我也会不断学习,争取分享更多技术知识。下一节说说Jwt认证集成使用。

跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt) - Code综艺圈 - 博客园

mikel阅读(917)

来源: 跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt) – Code综艺圈 – 博客园

前言

撸码需谨慎,裸奔有风险。经常在一些技术交流群中了解到,还有很多小伙伴的项目中Api接口没有做任何安全机制验证,直接就裸奔了,对于一些临时项目或是个人小项目还好,其余的话,建议小伙伴们酌情考虑都加上,毕竟接口安全这事可大可小。

通常会采用session、cookie、jwt、ids4等方式进行接口安全认证或授权,这里就先拿jwt说事,ids4知识点比较多,后续单独整理整理;对于session和cookie的方式就留给小伙伴们研究吧,因为最近接触或是和朋友聊到的项目中,使用的不多,所以就不单独拿出来细说了。

正文

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。—官网翻译

主要用于系统中授权认证,使得在数据交换过程中通过Token方式进行校验,相对比较安全;而Token包含三部分,每部分用点(.)进行拼接,内容结构为:Header.Payload.Signature

  • Header(头)

    一个Json对象,通过Base64URL算法将其转换为一个字符串。里面有两个属性,alg和typ分别代表签名算法和生成token的类型。

  • Payload(负载)

    一个Json对象,通过Base64URL算法将其转换为一个字符串。里面默认有一些官方定义的信息,也可以将自定义信息加入到其中,但是由于不是加密的,强烈不介意将敏感信息放在其中。默认属性大概如下:

iss (issuer):签发人

exp (expiration time):过期时间

sub (subject):主题

aud (audience):接受方

nbf (Not Before):生效时间

iat (Issued At):签发时间

jti (JWT ID):唯一编号

  • Signature(签名)

    这个是对前两部分的内容进行签名,需要一个秘钥,再通过Header中指定的签名算法生成签名。比如指定算法为HMACSHA256时,生成的签名为:

    img

收,理论知识就先提及这么多,如果需要详细了解的,可以进管网(https://jwt.io/introduction)瞅瞅,英文版怕怕?别啊,翻译工具那么多,应该难不倒小伙伴。接下来就通过代码演示方式说说jwt使用方式及涉及到的相关知识点吧。

这里还是在上一篇文章的例子上进行举例演示(跟我一起学.NetCore之Swagger让前后端不再烦恼及界面自定义),不是偷懒,而是利用Swagger方便测试,另外就是针对认证对Swagger进行一个小知识点的扩展。

img

以上Swagger内容不是必须,如果小伙伴不想用Swagger进行测试,直接使用类似于Postman的工具也是可以的,测试方式不冲突,所以小伙伴们别跑,咱们继续往下看↓↓↓

使用组件的经典三步走:安装包->注册组件->注册中间件;jwt集成使用如下:

img

img

通过以上简单三步操作,已经将Jwt集成到项目中,接下来开始用它来保护我们的Api接口:

img

如上,通过简单的在接口上增加[Authorize]特性就能保护起来啦,现在只能带“身份证”才能玩了,那系统中得有一个颁发”身份证”的地方,供系统识别验证,一般都会将其放在登录接口的地方,当用户验证成功之后,就生成对应的Token给客户端,客户端拿着这个Token就可以当“身份证”来调用接口啦,看如下代码:

img

这个错误应该是刚开始经常遇到的,必须要求密钥是大于等于16个字符,否则就会失败。修改密钥长度如下:

img

然后运行,重新登录获取Token,如下图:

img

可以看到,上面代码中的公共信息部分应该提取到公共配置信息中,不然要改几个地方,所以在程序开发过程中,小伙伴么尽量不要硬编码,不然就等于给自己找维护工作。

Token已经生成了,那怎么用?现在Swagger不支持输入Token,可以使用postman类似的工具进行接口测试,如下:

img

输入Token,成功获取信息:

img

Token的使用本质其实是在Header中增加了一个Authorization头,如下图:

img

Authorization中的值为Bearer+’ ‘+Token,中间一定要有一个空格。同理,前端调用API接口的时候也是在请求头中增加Authorization即可。

拿到Token,就可以访问受保护的API了,现在应该了解一下生成的Token是否和刚开始说的理论一样,同时可以通过jwt官网进行解析查看具体内容:

img

通过官网解析看看内容:

img

通过上面得知,没有经过业务加密的Token,是可以进行解析的,所以不建议把一些敏感信息放在Payload中。但是对于认证来说是安全,因为校验签名是需要密钥的,而密钥是存在服务器端,一般人肯定是不知道的。

对于Token的过期,有一个点,即过期滑动时间,如果不设置,默认是五分钟,即当设置Token有效期到期时,不会马上失效,而是再过五分钟才失效,如下例:

img

过了几分钟后还是没有过期,如下:

img

过六分之后,再访问,发现Token才失效,如果觉得过期滑动时间不满足需求,可以进行设置,如下:

img

这里运行效果就不截图了,小伙伴试试吧!!! 这里关于Jwt的集成先说这么多,肯定是没有说完的,还有权限验证那块,单独整理一篇说吧,内容也不少。

Swagger小扩展

既然都用到Swagger,肯定是希望在Swagger上直接测试,来来来,继续往下看看↓↓↓

在注册Swagger组件时进行相关配置:

img

运行结果如下:

img

点击小锁,弹出框可进行Token输入:

img

输入Token授权之后就可以调用保护的接口,可以很方便的进行测试,这里就不截图演示了,小伙伴们动手试试吧。

附加知识点:

由于SecurityRequirementsOperationFilter默认将securitySchemaName 设置为”oauth2″,所以在Swagger中加入描述的时候需要指定第一个参数为”oauth2″,源码如下:

img

也可以换成其他方式,参照实现的方式2,两种方式差不多,如下图:

img

注意点:

  • 生成Token时的密钥大于或等于16个字符;
  • 生成的Token中默认不加密,敏感信息不要放入其中,如果有需要,可以将Token进行加密;
  • Token传输如果是外网,建议用Https,防止中途被拦截;
  • Token防止泄露,可以将有效期设置短一点;

总结

关于权限的验证单独再整理一篇分享,这里就先到这吧;旅游或回家的小伙伴们应该都回到自己的住处了吧,好好休息休息,又要开始撸码啦。

JWT With NetCore WebApi - 扫地僧2015 - 博客园

mikel阅读(831)

来源: JWT With NetCore WebApi – 扫地僧2015 – 博客园

1 什么是JWT?

JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

 

2 JWT 的应用场景是什么?

身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的

3 JWT的结构

JWT包含了使用.分隔的三部分: Header 头部 Payload 负载 Signature 签名

其结构看起来是这样的Header.Payload.Signature

Header

在header中通常包含了两部分:token类型和采用的加密算法。{ “alg”: “HS256”, “typ”: “JWT”} 接下来对这部分内容使用 Base64Url 编码组成了JWT结构的第一部分。

Payload

Token的第二部分是负载,它包含了claim, Claim是一些实体(通常指的用户)的状态和额外的元数据,有三种类型的claim:reservedpublic 和 private.Reserved claims: 这些claim是JWT预先定义的,在JWT中并不会强制使用它们,而是推荐使用,常用的有 iss(签发者),exp(过期时间戳), sub(面向的用户), aud(接收方), iat(签发时间)。 Public claims:根据需要定义自己的字段,注意应该避免冲突 Private claims:这些是自定义的字段,可以用来在双方之间交换信息 负载使用的例子:{ “sub”: “1234567890”, “name”: “John Doe”, “admin”: true} 上述的负载需要经过Base64Url编码后作为JWT结构的第二部分。

Signature

创建签名需要使用编码后的header和payload以及一个秘钥,使用header中指定签名算法进行签名。例如如果希望使用HMAC SHA256算法,那么签名应该使用下列方式创建: HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) 签名用于验证消息的发送者以及消息是没有经过篡改的。 完整的JWT 完整的JWT格式的输出是以. 分隔的三段Base64编码,与SAML等基于XML的标准相比,JWT在HTTP和HTML环境中更容易传递。 下列的JWT展示了一个完整的JWT格式,它拼接了之前的Header, Payload以及秘钥签名:

 

4 如何使用JWT?

在身份鉴定的实现中,传统方法是在服务端存储一个session,给客户端返回一个cookie,而使用JWT之后,当用户使用它的认证信息登陆系统之后,会返回给用户一个JWT,用户只需要本地保存该token(通常使用local storage,也可以使用cookie)即可。 当用户希望访问一个受保护的路由或者资源的时候,通常应该在Authorization头部使用Bearer模式添加JWT,其内容看起来是下面这样:Authorization: Bearer <token>

因为用户的状态在服务端的内存中是不存储的,所以这是一种无状态的认证机制。服务端的保护路由将会检查请求头Authorization中的JWT信息,如果合法,则允许用户的行为。由于JWT是自包含的,因此减少了需要查询数据库的需要。 JWT的这些特性使得我们可以完全依赖其无状态的特性提供数据API服务,甚至是创建一个下载流服务。因为JWT并不使用Cookie的,所以你可以使用任何域名提供你的API服务而不需要担心跨域资源共享问题(CORS)。 下面的序列图展示了该过程:

5 为什么要使用JWT?

相比XML格式,JSON更加简洁,编码之后更小,这使得JWT比SAML更加简洁,更加适合在HTML和HTTP环境中传递。 在安全性方面,SWT只能够使用HMAC算法和共享的对称秘钥进行签名,而JWT和SAML token则可以使用X.509认证的公私秘钥对进行签名。与简单的JSON相比,XML和XML数字签名会引入复杂的安全漏洞。 因为JSON可以直接映射为对象,在大多数编程语言中都提供了JSON解析器,而XML则没有这么自然的文档-对象映射关系,这就使得使用JWT比SAML更方便

6 在NetCore WebApi中怎么用?

WebAPI使用NetCore2.1创建,无身份认证信息

Startup.cs中的Configure方法中配置使用Authentication

1 app.UseAuthentication();

接下来我们需要新建一个文件夹Models,在文件夹下面新建一个类JwtSettings.cs

复制代码
 1     public class JwtSettings
 2     {
 3         //token是谁颁发的
 4         public string Issuer { get; set; }
 5         //token可以给哪些客户端使用
 6         public string Audience { get; set; }
 7         //加密的key
 8         public string SecretKey{get;set;}
 9 
10     }
复制代码

然后我们需要在appsettings.json中配置jwt参数的值 【注意】 SecretKey必须大于16个,是大于,不是大于等于

复制代码
 1 {
 2   "Logging": {
 3     "IncludeScopes": false,
 4     "Debug": {
 5       "LogLevel": {
 6         "Default": "Warning"
 7       }
 8     },
 9     "Console": {
10       "LogLevel": {
11         "Default": "Warning"
12       }
13     }
14   },
15   
16   "JwtSettings":{
17     "Issuer":"http://localhost:44319",
18     "Audience":"http://localhost:44319",
19     "SecretKey":"Hello-key-----ztb"
20   }
21 }
复制代码

这时候我们重新回到Startup.cs的ConfigureServices方法下,将appsettings.json中的文件读取到JwtSettings中,进行Bind,然后设置jwt参数

 

复制代码
 1 public void ConfigureServices(IServiceCollection services)
 2         {
 3             //将appsettings.json中的JwtSettings部分文件读取到JwtSettings中,这是给其他地方用的
 4             services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings"));
 5 
 6             //由于初始化的时候我们就需要用,所以使用Bind的方式读取配置
 7             //将配置绑定到JwtSettings实例中
 8             var jwtSettings=new JwtSettings();
 9             Configuration.Bind("JwtSettings",jwtSettings);
10 
11             services.AddAuthentication(options=>{
12                 //认证middleware配置
13                 options.DefaultAuthenticateScheme=JwtBearerDefaults.AuthenticationScheme;
14                 options.DefaultChallengeScheme=JwtBearerDefaults.AuthenticationScheme;
15             })
16             .AddJwtBearer(o=>{
17                 //主要是jwt  token参数设置
18                 o.TokenValidationParameters=new Microsoft.IdentityModel.Tokens.TokenValidationParameters{
19             //Token颁发机构
20                     ValidIssuer =jwtSettings.Issuer,
21             //颁发给谁
22                     ValidAudience =jwtSettings.Audience,
23                     //这里的key要进行加密,需要引用Microsoft.IdentityModel.Tokens
24                     IssuerSigningKey=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))
25             //ValidateIssuerSigningKey=true,
26             ////是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
27             //ValidateLifetime=true,
28             ////允许的服务器时间偏移量
29             //ClockSkew=TimeSpan.Zero
30 
31                 };
32             });
33 
34 
35             services.AddMvc();
36         }
复制代码

如果我们需要安装nuget包的话,只要在【查看】-》【命令面板】中输入NuGet Package Manager,即可进入package安装,输入Microsoft.AspNetCore.Authentication.JwtBearer即可进行安装

首先我们新建一个ViewModel文件夹,并在ViewModel文件夹下面新建LoginViewModel.cs

复制代码
 1 using System.ComponentModel.DataAnnotations;
 2 
 3 namespace JwtAuthSample
 4 {
 5     public class LoginViewModel
 6     {
 7         //用户名
 8         [Required]
 9         public string User { get; set; }
10         //密码
11         [Required]
12         public string Password { get; set; }
13 
14     }
15 }
复制代码

接下来在Controllers文件夹下新建控制器AuthorizeController.cs,完整代码如下

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using Microsoft.AspNetCore.Mvc;
 6 
 7 //引用命名空间
 8 using System.Security.Claims;
 9 using Microsoft.IdentityModel.Tokens;
10 using Microsoft.Extensions.Options;
11 using System.Text;
12 using System.IdentityModel.Tokens.Jwt;
13 
14 namespace JwtAuthSample.Controllers
15 {
16     [Route("api/[controller]")]
17     public class AuthorizeController : Controller
18     {
19         private JwtSettings _jwtSettings;
20 
21         public AuthorizeController(IOptions<JwtSettings> _jwtSettingsAccesser)
22         {
23             _jwtSettings=_jwtSettingsAccesser.Value;
24         }
25 
26         [HttpPost]
27         public IActionResult Token([FromBody]LoginViewModel viewModel)
28         {
29             if(ModelState.IsValid)//判断是否合法
30             {
31                 if(!(viewModel.User=="ztb"&&viewModel.Password=="123456"))//判断账号密码是否正确
32                 {
33                     return BadRequest();
34                 }
35 
36 
37                 var claim=new Claim[]{
38                     new Claim(ClaimTypes.Name,"ztb"),
39                     new Claim(ClaimTypes.Role,"admin")
40                 };
41 
42                 //对称秘钥
43                 var key=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
44                 //签名证书(秘钥,加密算法)
45                 var creds=new SigningCredentials(key,SecurityAlgorithms.HmacSha256);
46                 
47                 //生成token  [注意]需要nuget添加Microsoft.AspNetCore.Authentication.JwtBearer包,并引用System.IdentityModel.Tokens.Jwt命名空间
48                 var token=new JwtSecurityToken(_jwtSettings.Issuer,_jwtSettings.Audience,claim,DateTime.Now,DateTime.Now.AddMinutes(30),creds);
49 
50                 return Ok(new {token=new JwtSecurityTokenHandler().WriteToken(token)});
51 
52             }
53 
54             return BadRequest();
55         }
56     }
57 }
复制代码

 

PostMan测试获取token

 

这样可以成功获取token,下面来做权限校验

在需要授权的api上新增 [Authorize] 标记

比如万年values控制器

我们分别使用携带token和不携带token访问values接口

1 携带token访问,返回了想要的数据

 

2 未携带token,返回401

Demo地址:https://gitee.com/shaolinsaodiseng/JWTDemo.git

 

.NetCore WebAPI采坑之路(持续更新) - 长沙大鹏 - 博客园

mikel阅读(1748)

来源: .NetCore WebAPI采坑之路(持续更新) – 长沙大鹏 – 博客园

1、WebAPI新增日志过滤器or中间件后Action读取到的请求Body为空问题

案例:

自定义了一个中间件,用于记录每次访问webapi的入参,以及引用了Swagger。

先看下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
       {
           if (env.IsDevelopment())
           {
               app.UseDeveloperExceptionPage();
           }
           else
           {
               app.UseHsts();
           }
           app.UseMvc();
           //各种中间件 
           app.UseVisitLogger();
           app.UsAirExceptionHandler();
           app.UseHttpsRedirection();
           //SignalR
           app.UseSignalR(routes =>
           {
               routes.MapHub<TriageHub>("/triage", (options) =>
               {
                   //指定采取WebSoket协议进行双工通讯
                   options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
               });
           });
      
           //swagger
           app.UseSwagger();
           app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json""TestWebAPI"));
       }

上面这段代码看上去是不是人畜无害,没啥问题。 恩,F5启动后,我们试下执行post方式的webapi。执行结果是ok的,但是UseVisitLogger中间件里的

日志记录却没有记录webapi的访问日志。

我们把Startup下的Configure方法改成下面这种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
     {
         if (env.IsDevelopment())
         {
             app.UseDeveloperExceptionPage();
         }
         else
         {
             app.UseHsts();
         }      
         //各种中间件 
         app.UseVisitLogger();
         app.UsAirExceptionHandler();
         app.UseHttpsRedirection();
         //SignalR
         app.UseSignalR(routes =>
         {
             routes.MapHub<TriageHub>("/triage", (options) =>
             {
                 //指定采取WebSoket协议进行双工通讯
                 options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
             });
         });
         app.UseMvc();
         //swagger
         app.UseSwagger();
         app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json""TestWebAPI"));
     }

我们把UseMvc()放置 各类中间件的后面,再来试下webapi的post方式(post的入参是一个模型,不是基础数据类型!)

 

执行结果如上图。。。这一次api执行失败,但是日志记录成功了,如下图:

哎。。。是不是很坑。。目前先记录下,还要想办法怎么去解决

———————————————————————解决新增自定义中间件后执行带模型Post请求400问题———————————-

原因: 因为自定义中间件里有一个是写日志的,会将HttpContext对象的Request.Body对象(一个Steam对象)的读取位置改变。原来的日志中间件是这样写的,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public async Task Invoke(HttpContext context)
  {
      visitLog = new VisitLog();
      HttpRequest request = context.Request;
      visitLog.Url = request.Path.ToString();
      visitLog.Headers = request.Headers.ToDictionary(k => k.Key, v => string.Join(";", v.Value.ToList()));
      visitLog.Method = request.Method;
      visitLog.ExcuteStartTime = DateTime.Now;
      using (StreamReader reader = new StreamReader(request.Body))
      {
          visitLog.RequestBody = reader.ReadToEnd();
      }
      context.Response.OnCompleted(ResponseCompletedCallback, context);
      await _next(context);
  }

上面的 requst.Body 在通过ReadToEnd后实际上的context.Request.Body的读取位置已经是最后了,在后续的中间件接收到的context因为读取位置不是0而是内容的

长度,所以怎么都读不到数据。这就是为什么post请求会出现400错误,因为到action那里的时候确实Requst.Body的内容他啥也没读到啊!!

现在找到了问题所在就好处理了,处理方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public async Task Invoke(HttpContext context)
      {
          visitLog = new VisitLog();
          HttpRequest request = context.Request;
          visitLog.Url = request.Path.ToString();
          visitLog.Headers = request.Headers.ToDictionary(k => k.Key, v => string.Join(";", v.Value.ToList()));
          visitLog.Method = request.Method;
          visitLog.ExcuteStartTime = DateTime.Now;
          var originalRequestBody = request.Body;
          var requestBodyStream = new MemoryStream();
          await request.Body.CopyToAsync(requestBodyStream);
          //设置当前流的位置未0
          requestBodyStream.Seek(0, SeekOrigin.Begin);
          //这里ReadToEnd执行完毕后requestBodyStream流的位置会从0到最后位置(即request.ContentLength)
          visitLog.RequestBody = new StreamReader(requestBodyStream).ReadToEnd();
          //需要将流位置重置偏移到0,不然后续的action读取不到request.Content的值
          requestBodyStream.Seek(0, SeekOrigin.Begin);
          context.Response.OnCompleted(ResponseCompletedCallback, context);
          context.Request.Body = requestBodyStream;
          await _next(context);
          context.Request.Body = originalRequestBody;
      }

好啦,我们把流的读取位置重新偏移下就好了!

 2、.NetCore项目智能提示英文更改为中文的方法

.NetCore下载后,在VS2017开发时候,智能提示是下图这样的:

很不爽是吧,尤其是我们这种英文渣。解决方案呢其实就是需要将框架里的Nuget包的注释xml文件替换成中文版的(至少我是这么做的,其他办法未知..)

首先从C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5.1 这个目录下将zh-Hans文件夹下的所有文件copy到

C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1目录下。 这里我是因为用的.NetCore2.1,如果

是2.0则将目录里的2.1更改成2.0。 然后重新打开VS2017就可以发现智能提示是中文啦~~~~

 3、字符集GB2312引用错误处理

在对csv文件读取时候乱码,后来发现.NetCore需要安装System.Text.Encoding.CodePages这个Nuget包,然后在类的构造函数里加上这一段代码System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance)

 4、Linux上部署.NetCore站点端口号与launchSettings.json设置不一致的解决办法

在Linux上部署站点绝对会出现这个问题,除非你的端口号恰巧就和默认的站点一致。不然你在launchSettings.json上的端口设置好后发现在Linux上启动你的应用后监听的端口不一致。

如何解决呢?没办法,在dotnet命令后加urls参数。如:

1
dotnet HY.Admin.Host.dll --urls http://*:5001

上面就是指定了端口5001,结果如下:

 

三、netcore跨平台之 Linux配置nginx负载均衡 - 流月无双 - 博客园

mikel阅读(891)

来源: 三、netcore跨平台之 Linux配置nginx负载均衡 – 流月无双 – 博客园

前面两章讲了netcore在linux上部署以及配置nginx,并让nginx代理webapi。

这一章主要讲如何配置负载均衡,有些步骤在前两章讲的很详细了,所以这一章我就不会一个个截图了。

因为本人只有一个服务器。所以我会在同一台服务器上部署两套差不多的实例。

同样的代码,我们在Program.cs进行了修改,如图所示:

这里我把原来的端口6666改成了8888

 

然后你可以改一改你的接口部分的代码,便于让你更好的看到效果。

这里把value1和value2改成value3和value4,这里是为了看到测试效果,在实际的开发中这里不用改。

 

然后发布和上传到服务器,如何发布和上传,我在第一章有讲到:https://www.cnblogs.com/dengbo/p/11878766.html

注意的是你同样的地方新建一个新的目录保存你新上传的程序,netcore是我第一章建立的,netcore1是新建的,

你把你新的发布包放在netcore即可。如图:

上传结束后,在这个目录中运行你的程序,输入下面的命令

dotnet WebApiTest.dll   --server.urls "http://*:8888"

如图所示

 

然后去看看你的接口是否正常

 

 

好了,这里的准备工作完成了,下面我们进入到nginx的配置的目录中

输入下面的命令:

cd /usr/local/nginx/conf

然后对文件进行编辑

vim nginx.conf

 

我们需要在这里修改一下配置。

在如图的server的平级添加如下的代码

upstream NgWebApi {
                server localhost:6666;
                server localhost:8888;
    }

上面的 NgWebApi是随意写的名称,不要纠结这里。

然后在修改 proxy_pass后面的内容:

proxy_pass http://NgWebApi;

最终的结果如下:

 

这样你就修改完成,输入:wq退出并保存即可。

最后检查并重启nginx

/usr/local/nginx/sbin/nginx -t
/usr/local/nginx/sbin/nginx -s reload

最后不要忘记把你的8888端口的webapi启动一下。

这里我务必要提醒你,请进入到你的程序的目录中执行这段代码,

cd /root/netcore1
dotnet WebApiTest.dll   --server.urls "http://*:8888"

启动如下:

 

 

好了,配置结束了,下面我们来测试下

 

还是昨天的那个网站进行测试   https://www.sojson.com/httpRequest/

 

 

 

多次发送请求会出现下面的响应

 

 

看到上面两个请求,就说明你配置成功了,是不是很简单。

上面这种配置,系统会采用默认的轮询访问不同的端口,nginx作为强大的反向代理,强大的远远不止这里

下面简单讲讲分发策略。

1)、轮询 ——轮流处理请求(这是系统默认的)

      每个请求按时间顺序逐一分配到不同的应用服务器,如果应用服务器down掉,自动剔除它,剩下的继续轮询,如果您的服务器都差不多,建议这个。 

2)、权重 ——谁的设置的大,谁就承担大部分的请求

      通过配置权重,指定轮询几率,权重和访问比率成正比,用于应用服务器性能不均的情况,有时候你买的服务器可能参差不齐,有的性能强大

    有的一般,你可以通过设置权重,把服务器性能强大权重设置大一点,这样可以合理分配压力。 

3)、ip_哈希算法

      每一次的请求按访问ip的hash结果分配,这样每个访客固定访问一个应用服务器,可以解决session共享的问题。

 

 

关于权重的策略,如下图示的 你只要加一个  weight=6 即可这里不一定是6,是整数都行。

 

 

然后保存即可

这里不要忘记重启nginx,以及运行8888端口的程序了,如果你不会,可以看前面的部分

最后我们看看效果

结果和上面的测试结果差不多,唯一不同的是出现下面这个结果的次数要大于另外一个的。

 

 

到这里就结束了,感谢观看。

二、netcore跨平台之 Linux部署nginx代理webapi - 流月无双 - 博客园

mikel阅读(930)

来源: 二、netcore跨平台之 Linux部署nginx代理webapi – 流月无双 – 博客园

二、netcore跨平台之 Linux部署nginx代理webapi

上一章,我们讲了在linux上安装netcore环境,以及让netcore在linux上运行。

这一章我们开始讲在linux上配置nginx,以及让nginx反向代理我们的webapi。

什么nginx???

Nginx(“engine x”)是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。

在高连接并发的情况下,Nginx是Apache服务器不错的替代品,而且现在越来越多的公司都开始用nginx,学习nginx是程序员必不可少的一部分。

 

现实用xshell登录到我们的linux系统中。

输入如下命令

安装编译工具和对应的库文件,输入下面的命令,回车即可

yum -y install make zlib zlib-devel gcc-c++ libtool  openssl openssl-devel

如下图示,就表示安装好了。

 

接下来,我们正式安装Nginx,我们这次选择安装1.6.2版本。

(1)下载nginx,输入下面命令,回车即可。

wget http://nginx.org/download/nginx-1.6.2.tar.gz

nginx比较小,下载起来还是蛮快的,如下图所示,表示下载完成。

 

 

 

(2)解压nginx压缩包

tar zxvf nginx-1.6.2.tar.gz

(3)进入到nginx目录中

cd nginx-1.6.2

(4)编译和安装,分别执行下面的命令

./configure --prefix=/usr/local/nginx
make

 

 

(5)查看版本

/usr/local/nginx/sbin/nginx -v

看到下面这个表示成功了

(6)启动nginx

 

/usr/local/nginx/sbin/nginx

然后在你的电脑上用浏览器访问即可

注意这里的默认监听端口是80,所以输入ip加上80端口即可

出现如下这种视图就表示成功了。

 

 

 

 

接下来就是很关键的配置了,其实安装还是很简单的。

进入到你的nginx的安装目录下

分别输入如下命令,即可查看到您的配置信息

cd  /usr/local/nginx/conf
ll

 

编辑您的nginx.conf文件,我这里用vim进行编辑

vim nginx.conf

出现如图所示:

我们主要看如下图所示红色标记的部分

 

 

listen:表示当前的代理服务器监听的端口,默认的是监听80端口。注意,如果配置了多个server,这个listen要配置不一样。

server_name:表示监听到之后需要转到哪里去,这时我们直接转到本地,这时是直接到nginx文件夹内。

location:表示匹配的路径,这时配置了/表示所有请求都被匹配到这里

root:里面配置了root这时表示当匹配这个请求的路径时,将会在这个文件夹内寻找相应的文件,这里对我们之后的静态文件伺服很有用。

index:当没有指定主页时,默认会选择这个指定的文件,它可以有多个,并按顺序来加载,如果第一个不存在,则找第二个,依此类推。

然后我们把上面的location进行了修改

用键盘的上下左右键把光标移动到如下位置

 

然后输入键盘的 i(键盘的字母i)进入到编辑模式(这里是给不懂linux说的,懂得人请无视)

然后用键盘的退回 ←把这里的都删除

 

然后在输入  proxy_pass http://localhost:6666;(这里接上一章已经配置好的netcore webapi)

 

 

然后点击键盘的ESC退出编辑状态

然后输入:wq退出并保存

:wq

 

这样就保存配置结束了,conf配置其实很复杂,网上有很多根据不同需求进行的配置

大家有兴趣可以去看看,我这里只说入门的部分,额,其实入门都算不上,能用就行,哈哈哈

最后执行下面两个命令

这里是检查配置

/usr/local/nginx/sbin/nginx -t

 

最后一个就是重启运行nginx

/usr/local/nginx/sbin/nginx -s reload

 

 

哦,最后我们看看效果,额

这里一定不要忘记启动我的netcore程序了

输入命令进入到我们的项目中

cd /root/netcore/

然后输入启动程序的命令

dotnet WebApiTest.dll

如下图所示,就表示启动成功了

 

 

 

然后,我们用测试工具测试一下我们的接口

如图所示,我们把以前的6666接口换成了80端口,这里还是访问成功了,这就表示我们的nginx代理成功了

 

 

 

 

 

到这里就基本结束了,不过大家一定有个奇怪的地方每次启动netcore,每次关了你的xshell后就不能访问了,

这里建议使用下面的命令,使之成为后台进程即可

nohup dotnet  WebApiTest.dll  &

over !!!!!!!!!!!!!!!!!!!!!!!!

一、netcore跨平台之 Linux上部署netcore和webapi - 流月无双 - 博客园

mikel阅读(943)

来源: 一、netcore跨平台之 Linux上部署netcore和webapi – 流月无双 – 博客园

一、netcore跨平台之 Linux上部署netcore和webapi

这几天闲着的时候在linux上部署了一下netcore webapi,下面就纪要一下这个过程。

中间遇到不少的坑,心里都是泪啊。

话不多说,开始干活。

————————————————————————

第一步,你得先创建一个netcore的接口,这个我就简单创建一个接口。

关于开发工具,我用的是vs2017,当然最新的vs2019也出来了,你可以用新的,都没关系。

开始选择创建项目,如图所示,这个入门的程序员都应该懂

 

 

选择API

 

 

点击确定按钮就创建成功。

如图打开 Program.cs

 

 

在这里添加一段代码

 

代码添加后

 

 

这样代码就写好了。

接下来就是发布。

 

选择文件夹,选择你要发布的项目的位置。

 

点击高级配置如下,注意下,这里的目标框架是2.2版本,所以我们在linux上安装的也是2.2。

这里我就遇到过坑,我vs发布的是2.0的版本,结果我linux是2.2,就各种运行报错,后来改成2.2就好了。

 

最后保存并发布就好了。

netcore项目的创建和发布就这样结束了。

第二步,你得准备一个linux服务器,然后安装环境

如果你条件允许,可以直接在阿里云或者腾讯云、华为云、百度云上买一个服务器。

新用户是白菜价哦,(这里真不是打广告)当然你可以在你电脑上安装一个VMware虚拟机。

具体安装步骤百度一下一大把,这里就不演示了。

我就在在百度云买了一个linux服务器,嗯,价格还算便宜,毕竟新用户,为什么用百度云??

当然不是因为他好,而是我阿里云已经不是新用户了

好了,我们继续。

用xshell登录到你的linux服务器上。(如果不懂linux,没关系,你总会百度吧)

登录成功后,你可以在直接输入如下命令

sudo yum install dotnet-sdk-2.2

 

点击确认,你需要等一段时间,如果你服务器网速很差,那么你可以会等很久。

如下图示,遇到这里你需要点击敲一下你的键盘上的 y 回车即可

这个时候系统开始慢慢的下载了,请耐心等待即可。

 

终于下载完成了

 

你可以输入下面的命令看看是否成功

dotnet --version

显示如下,表示按照成功

 

 

 

然后我们把发布包上传到服务器上来

我这里用的是xftp工具,当然也有其他工具可,下图所示是我安装的两个工具,大家可以去下载安装。

 

这里给大家提供一些我在网盘保存的一些工具

Xshell+Xftp真正破解版    https://pan.baidu.com/s/1Ew1XPg11sakpc8mvK6QsHg

打开xftp并连接到服务器,如下所示

 

 

 

我这里用的root权限,这里进来就直接就是root根目录了

然后右键点击创建一个目录用来保存你上传的netcore文件,嗯,就取名netcore吧

 

 

 

 

 

 

然后在左边找到你刚刚发布的那个包的位置,并且点击右边的netcore进入到对应的目录中

 

 

 

然后全选左边的所有文件,并右键然后点击传输,如图所示

 

 

然后文件就开始传输了

 

 

 

 

等下面的传输没有记录了,那么恭喜你,你传递完成了。

 

有人可能会问为啥这么多文件,我vs2017本来没有2.2版本,后来我在本机安装了2.2,结果发布后就这么多……

然后我们再次回到xshell看看

输入命令 ll 可以看到我们新加了一个文件 netcore

 

然后输入命令cd  进入到我们上传的这个发布包中。

然后在输入命令 ll

cd netcore
ll

我们可以找到WebApiTest.dll文件

 

然后输入命令

dotnet WebApiTest.dll

如图所示,就恭喜你你的项目运行正常。

 

然后你Ctrl+C结束掉这个程序,这里只能在服务器内部访问,外面是不能访问的。

然后输入如下命令

dotnet WebApiTest.dll   --server.urls "http://*:6666"

如图所示

 

然后用postman或者一些在线工具访问这个接口,如图所示,那么恭喜你成功了

这个测试工具的网站:https://www.sojson.com/httpRequest/

 

 

好了,到这里netcore在linux上配置就基本完成了,接下来下一篇我们开始讲nginx的配置,以及让netcore运行在nginx下。

 

NodePort,LoadBalancer还是Ingress?我该如何选择 - kubernetes - justmine - 博客园

mikel阅读(1207)

来源: NodePort,LoadBalancer还是Ingress?我该如何选择 – kubernetes – justmine – 博客园

原文:http://mp.weixin.qq.com/s/dHaiX3H421jBhnzgCCsktg

当我们使用k8s集群部署好应用的Service时,默认的Service类型是ClusterIP,这种类型只有 Cluster 内的节点和 Pod 可以访问。如何将应用的Service暴露给Cluster外部访问呢,Kubernetes 提供了多种类型的 Service,如下:

ClusterIP


ClusterIP服务是Kuberntets的默认服务。它在集群内部生成一个服务,供集群内的其他应用访问。外部无法访问。

ClusterIP服务的 YAML 文件如下:

apiVersion: v1
kind: Service
metadata:  
  name: my-internal-service
selector:    
  app: my-app
spec:
  type: ClusterIP
  ports:  
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP

如果不能从互联网访问ClusterIP服务,那我们还介绍它干啥?其实,我们可以使用Kubernetes proxy来访问它!

 

开启Kubernetes Proxy:

$ kubectl proxy --port=8080

现在可以通过Kubernetes API使用下面这个地址来访问这个服务:

http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/

为了访问上面定义的服务,可以使用下面这个地址:

http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/

使用场景

在某些场景下,你会使用Kubernetes proxy来访问服务:

  1. 调试服务,或者是因为某些原因需要从电脑直接连接服务;
  2. 允许内部流量,显示内部仪表盘等。

这个访问需要你作为一个已验证的用户去运行kubectl,所以不要通过这种方式将服务发布到互联网,或者是在生产环境下使用。

NodePort


NodePort服务是让外部流量直接访问服务的最原始方式。NodePort,顾名思义,在所有的节点(虚拟机)上开放指定的端口,所有发送到这个端口的流量都会直接转发到服务。

NodePort服务的YAML文件如下:

apiVersion: v1
kind: Service
metadata:  
 name: my-nodeport-service
selector:   
 app: my-app
spec:
 type: NodePort
 ports:  
 - name: http
  port: 80
  targetPort: 80
  nodePort: 30036
  protocol: TCP

从本质上来看,NodePort服务有两个地方不同于一般的“ClusterIP”服务。首先,它的类型是“NodePort”。还有一个叫做“nodePort”的端口,能在节点上指定开放哪个端口。如果没有指定端口,它会选择一个随机端口。大多数时候应该让Kubernetes选择这个端口,就像谷歌领导人Thockin说的,关于能使用哪些端口,有很多注意事项。

使用场景

这种方式有一些不足:

  1. 一个端口只能供一个服务使用;
  2. 只能使用30000–32767的端口;
  3. 如果节点 / 虚拟机的IP地址发生变化,需要进行处理。

因此,我不推荐在生产环境使用这种方式来直接发布服务。如果不要求运行的服务实时可用,或者在意成本,这种方式适合你。例如用于演示的应用或是临时运行就正好用这种方法。

LoadBalancer


LoadBalancer服务是发布服务到互联网的标准方式。在GKE中,它会启动一个Network Load Balancer,分配一个单独的IP地址,将所有流量转发到服务中。

 

使用场景

如果你想直接发布服务,这是默认方式。指定端口的所有流量都会转发到服务中,没有过滤,也没有路由。这意味着你几乎可以发送任意类型的流量到服务中,比如HTTP、TCP、UDP、Websockets、gRPC等等。

这里最大的不足是,使用LoadBalancer发布的每个服务都会有一个自己的IP地址,你需要支付每个服务的LoadBalancer 费用,这是一笔不小的开支。

Ingress


Ingress实际上不是一种服务。相反,它在多个服务前面充当“智能路由”的角色,或者是集群的入口。

使用Ingress可以做很多事情,不同类型的Ingress控制器有不同的功能。

默认的GKE ingress控制器会启动一个 HTTP(S) Load Balancer,可以通过基于路径或者是基于子域名的方式路由到后端服务。例如,可以通过foo.yourdomain.com 发送任何东西到foo服务,或者是发送yourdomain.com/bar/路径下的任何东西到bar服务。

对于使用第 7 层HTTP Load Balancer 的GKE上的Ingress对象,其YAML文件如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  backend:
    serviceName: other
    servicePort: 8080
  rules:
  - host: foo.mydomain.com
    http:
      paths:
      - backend:
          serviceName: foo
          servicePort: 8080
  - host: mydomain.com
    http:
      paths:
      - path: /bar/*
        backend:
          serviceName: bar
          servicePort: 8080

使用场景

Ingress可能是发布服务最强大的方式,同时也是最复杂的。Ingress控制器的类型很多,如 Google Cloud Load Balancer,Nginx,Contour,Istio等等。还有一些Ingress控制器插件,比如证书管理器,可以自动为服务提供SSL认证。

如果想在同一个IP地址下发布多个服务,并且这些服务使用相同的第 7 层协议(通常是 HTTP),Ingress是最有用的。如果使用原生的GCP集成,只需要支付一个负载均衡器的费用。因为Ingress是“智能”的,你可以得到很多开箱即用的特性(比如SSL、认证、路由等)。

实践


NodePort: kubernetes实践之运行aspnetcore webapi微服务  

Ingress:等待正在更新中…..