Spring Security 的实现机制使得它可以轻易的添加安全响应头。3.7.1 静态头例如配置如下自定义头:X-Custom-Security-Header: header-value我们可以通过 StaticHeadersWriter 对象添加:@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.headers(headers -> headers.addHeaderWriter(new StaticHeadersWriter("X-Custom-Security-Header","header-value"))); }}3.7.2 动态添加当Spring Security 的默认配置方式不能满足我们添加响应头的需求时,我们可以通过 HeadersWriter 实例动态添加。例如动态添加 X-Frame-Options 头:@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.headers(headers -> headers.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))); }}3.7.3 为指定请求添加响应头有时候我们只想为某个确定的请求添加响应头,例如只针对对登录页面的请求,这是可以通过 DelegatingRequestMatcherHeaderWriter 实现。例如:@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { RequestMatcher matcher = new AntPathRequestMatcher("/login"); DelegatingRequestMatcherHeaderWriter headerWriter = new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter()); http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.disable()).addHeaderWriter(headerWriter)); }}
我们先来看下第一部分,请求行:GET / HTTP/1.1请求行里的 GET 是请求方法。请求方法主要是告诉服务器端,客户端要对资源实行什么样的具体操作,方便服务器进行响应的处理。HTTP 1.0 规定的方法: GET,POST,HEAD;HTTP 1.1 新增的请求方法:OPTIONS,PUT,DELETE,TRACE,CONNECT;HTTP 规定的主要请求如下表所示,我们主要使用的实际上就是 get,post 这两个请求。常用的请求方法序号请求方法方法描述1GET用来获取服务器的信息。2POST用于创建一个文件,请求是非幂等的。3HEAD通过这个来获取响应的报头文件,不包含的具体内容。4PUT主要是用来更新文件,这个方法对服务器来讲,应该是幂等的。5DELETE这个命令是用来请求让服务器端来删除特定的信息。6OPTIONS这个方法可以让客户端可以查看服务器可以提供的请求方法等信息。7TRACE这个主要用于测试和诊断,可以回显服务器的信息。8CONNECTHTTP/1.1协议中预留的请求方法,不常使用。Get 后面的 / 是来标明请求的资源信息,我们这里是想访问慕课网的主页,所以写 /。 HTTP/1.1 指的是 HTTP 的协议版本。Tips:HTTP 是在 1990 左右提出的协议,距今已经有几十年的历史了。广泛使用的版本有 1.0,1.1,现在也有 2.0 的版本,不过还没有普及。除此之外,对安全要求高的一些网站,也有的开始采用 HTTPS 协议进行传输。HTTPS 提供了更多的安全校验,是利用 SSL/TLS 技术进行加密的,相对于普通的 HTTP,更加安全,隐私更不容易泄露。好了说完了请求行,让我们来介绍一下请求头部。
请求没有权限,通常返回的响应头部会包含 WWW-Authenticate 的头,浏览器遇到这种响应一般会弹出一个对话框,让用户重新提交用户名和密码进行认证。服务端响应HTTP/1.1 401 UnauthorizedWWW-Authenticate: Basic; realm="Secured area"客户端重新提交认证GET / HTTP/1.1Authorization: Basic j3VsbCBkb25lOnlvdhBmb3Vu89B0aGUgZWFzdGVyIoUnZwo=
本节的目标是了解 Nginx 的基本配置。关于 Nginx 的配置,主要是以下 5 个方面:初始配置基本语法http 服务配置tcp/udp反向代理每个部分其实有比较多的扩展内容,今天我们会讲解初始配置以及配置文件的基本语法,后续的 http 服务配置、tcp/udp 配置和反向代理配置会在下一节中介绍。
在配置好 Swagger Editor 基本配置信息和 Swagger Editor 接口配置信息之后,我们就可以在项目中生成 Swagger-UI 界面了。我们生成 Swagger-UI 界面需要依赖 http-server ,我们在编辑器的控制台中输入以下命令即可: http-server swagger-editor .yml文件路径即我们为 Swagger Editor 指定需要使用的 .yml 配置源文件即可,生成后的界面如下图所示。
本节内容介绍了 Http 请求 11 个阶段中的最后几个,分别是 try_files 阶段、content 阶段和 log 阶段,同时还有对应阶段中生效的指令。这些在配置静态资源访问时非常有用,因为主要是涉及到读取静态资源的内容。最后的 log 模块也是非常重要的一步,良好的日志记录有助于我们后续排查问题以及分析系统性能瓶颈。今天 Http 请求的 11 个处理阶段正式讲完,后面还需要多多深入每个阶段的指令学习和实验,彻底掌握 Nginx 的 Http 模块。
public class TestServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap .group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<NioSocketChannel>() { protected void initChannel(NioSocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); //1.Netty提供的针对Http的编解码 pipeline.addLast(new HttpServerCodec()); //2.自定义处理Http的业务Handler pipeline.addLast(new TestHttpServerHandler()); } }); ChannelFuture channelFuture = serverBootstrap.bind(8080).sync(); channelFuture.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }}代码说明:这个是 Netty 的基本模板类,跟我们之前写的并没有什么不同,只是它给我们提了一个特殊的类 HttpServerCodec,从字面上都能猜到它就是针对 Http 服务的编解码器。
ngx_http_limit_req_module 模块主要用于处理突发流量,它基于漏斗算法将突发的流量限定为恒定的流量。如果请求容量没有超出设定的极限,后续的突发请求的响应会变慢,而对于超过容量的请求,则会立即返回 503(默认)错误。该模块模块中比较重要的指令有:limit_req_zone 指令,定义共享内存, key 关键字以及限制速率Syntax: limit_req_zone key zone=name:size rate=rate [sync];Default: —Context: httplimit_req 指令,限制并发连接数Syntax: limit_req zone=name [burst=number] [nodelay | delay=number];Default: —Context: http, server, locationlimit_req_log_level 指令,设置服务拒绝请求发生时打印的日志级别Syntax: limit_req_log_level info | notice | warn | error;Default:limit_req_log_level error;Context: http, server, locationlimit_req_status 指令, 设置服务拒绝请求发生时返回状态码Syntax: limit_req_status code;Default: limit_req_status 503;Context: http, server, location
REST 全称是 Representational State Transfer 的缩写,中文翻译是表述性状态转移。Roy Thomas Fielding 首次在他的博士论文中提出的 REST 这个概念,他把互联网软件的架构原则,定名为 REST。Roy Thomas Fielding 提出 REST 的概念,是为了得到一个以网络为基础的应用软件的架构,得到一个功能强、性能好、适宜通信的架构。REST 指的是一组架构约束条件和原则,如果一个架构符合 REST 的约束条件和原则,我们就称它为 RESTful 架构。虽然 REST 本身受 Web 技术的影响很深,但理论上,REST 架构风格并不是绑定在 HTTP 应用上。目前 HTTP 是唯一与 REST 相关的应用领域,因此 REST 通常是指基于 HTTP 实现的 REST。
很多时候我们的路径是包含参数信息的,而规则的应用往往也和参数信息想匹配。例如针对用户信息的 RESTful 资源地址 /user/{userId}。我们可以在表达式中使用这些参数。例如,假设我们的扩展表达式方法如下:public class WebSecurity { public boolean checkUserId(Authentication authentication, int id) { ... }}此方法的 id 参数需要传入路径中的 userId 的值,那我们在配置表达式时可用如下方式:<http> <intercept-url pattern="/user/{userId}/**" access="@webSecurity.checkUserId(authentication,#userId)"/> ...</http>或者用 Java 的方式配置http .authorizeRequests(authorize -> authorize .antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)") ... );上述写法的结果都是将 userId 作为参数传入了方法之中,加入用户访问的 URL 地址为 /user/123/resource,则传入的 id 参数为 123。
前端应用的部署更加简单,我们直接在云服务器上下载 nginx 然后解压。打开网址 http://nginx.org/en/download.html ,点击下图中的链接下载即可。nginx 下载链接下载解压后,将前端页面直接放到 nginx/html 目录下即可。当然如果有很多网页,可以先在该目录下建立子目录便于归类网页。我们建立 shop-front 目录(表示商城系统的前端项目),然后将网页放入其中,效果如下:商城系统前端项目目录内容注意还需要修改 goods.html 中访问的后端 URL 地址,假设云服务器的公网 IP 为 x.x.x.x ,则修改为:实例:$.ajax({ type: "GET", url: "http://x.x.x.x:8080/goods", //后端接口地址 dataType: "json", contentType: "application/json; charset=utf-8", success: function (res) { $.each(res, function (i, v) { row = "<tr>"; row += "<td>" + v.id + "</td>"; row += "<td>" + v.name + "</td>"; row += "<td>" + v.price + "</td>"; row += "<td>" + v.pic + "</td>"; row += "</tr>"; $("#goodsTable").append(row); }); }, error: function (err) { console.log(err); } });此处解释下后端地址 http://x.x.x.x:8080/goods , HTTP 代表协议, x.x.x.x 代表云服务器公网地址, 8080 是我们后端项目的启动端口,由于我们没有在配置文件中设置,所以默认就是 8080 ,最后 goods 是控制器中设定的后端接口路径。双击 nginx.exe 启动 nginx ,由于 nginx 默认启动端口是 80 ,所以此时访问 http://x.x.x.x ,效果如下,说明 nginx 启动成功!nginx 已启动成功
通用的首部字段指的是请求和响应的首部都能使用的字段。请求示例GET / HTTP/1.1Host: www.imooc.comConnection: keep-alive请求实体内容响应示例HTTP/1.1 200 OKDate: Web 29 Apr 2020 08:08:08 GMTConnection: keep-alive响应实体内容示例中请求和响应的协议首部均有 Connection 字段,这类即为公共首部字段。
默认情况下,CSRF Token 存储在 HttpSession 中,使用 HttpSessionCsrfTokenRepository 对象维护。如果需要进行扩展,比如不仅要在 HTTP 请求中携带 Token,也需要在 JS 应用中应用 Token,那需要通过如下方式:在配置类中构造并注入 CookieCsrfTokenRepository 对象。@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) { http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())); }}
前面我们刚开始使用 Nginx 时只是用了 http 指令块,因为是针对 http 请求进行处理。这进行的是四层反向代理,转发 TCP 或者 UDP 协议的报文。针对该层的处理,Nginx 是使用了 stream 模块进行处理,对应的是 stream 指令块,它和 http 指令块非类似,用法几乎一致。stream 指令块里面可以包含 server 指令块,server 指令块里面也可以包含 listen 指令块指定监听的端口、还可以包含 proxy_pass指令,对该端口监听的 tcp 或者 udp 报文进行转发。总之,和 http 指令块大部分用法一致。...stream { ... server { listen 3440; # 转发四层流量 proxy_pass 192.168.1.103:3440; } ...}...此外,从nginx-1.11.2版开始, ngx_stream_core_module,也同 http 模块一样支持变量,部分支持变量如下,这和 http 模块也是类似的,甚至连变量名都非常相似:$binary_remote_addr: 二进制格式的客户端地址$bytes_received: 从客户端接收到的字节数$bytes_sent: 发往客户端的字节数$hostname: 连接域名$msec: 毫秒精度的当前时间$nginx_version: nginx的版本$pid: worker进程号$protocol: 通信协议(UDP or TCP)$remote_addr: 客户端ip$remote_port: 客户端端口$server_addr: 接受连接的服务器ip,计算此变量需要一次系统调用。所以避免系统调用,在listen指令里必须指定具体的服务器地址并且使用参数bind。$server_port: 接受连接的服务器端口$session_time: 毫秒精度的会话时间(版本1.11.4开始)$status: 会话状态(版本1.11.4开始), 可以是一下几个值:200成功400不能正常解析客户端数据403禁止访问500服务器内部错误502网关错误,比如上游服务器无法连接503服务不可用,比如由于限制连接等措施导致$time_iso8601: ISO 8601时间格式$time_local: 普通日志格式的时间戳
在 access 阶段,我们可以通 allow 和 deny 指令来允许和拒绝某些 ip 的访问权限,指令的用法如下:Syntax: allow address | CIDR | unix: | all;Default: —Context: http, server, location, limit_exceptSyntax: deny address | CIDR | unix: | all;Default: —Context: http, server, location, limit_except实例allow 和 deny 指令后面可以跟具体 ip 地址,也可以跟一个 ip 段, 或者所有(all)。location / { deny 192.168.1.1; allow 192.168.1.0/24; allow 10.1.1.0/16; allow 2001:0db8::/32; deny all;}
本节讲述了如何在 Android 中发起 http 请求,HttpURLConnection 的使用方式非常简单,但是需要你对 http 协议本身有一定的了解,作为开发者这个是必备的知识点。当然对于大型项目而言,需要涉及到 DNS、缓存、安全校验等等高级操作,这时候 HttpURLConnection 会显得有点输出乏力,这就需要第三方的框架出场了,万变不离其宗,所有的框架到了底层其实都是一样的基本原理,所以还是希望大家能够熟悉 http 协议,并掌握 HttpURLConnection 的用法。
我们运行启动类,启动 spring-boot-profile 应用,控制台会发现如下提示:Tomcat started on port(s): 8080 (http) with context path ''可以看出, Spring Boot 应用默认启动端口是 8080 ,默认项目路径是空。我们可以通过修改 resources/application.properties 来自定义项目启动配置:实例:# 启动端口server.port=8000# 项目路径server.servlet.context-path=/spring-boot-profile再次启动应用,控制台提示变为:Tomcat started on port(s): 8000 (http) with context path '/spring-boot-profile'此时项目对应的访问路径为: http://127.0.0.1:8000/spring-boot-profile , 使用浏览器访问效果如下:浏览器显示返回数据
Object.is() 被称为同值相等的比较,在两个值进行比较时用到了很多规则,比如上面 === 在判断带符号的 0 时返回的都是 true,NaN 和任何值都不相等,但 Object.is() 给出了截然相反的结果:Object.is(0, -0); // falseObject.is(NaN, NaN); // true下面我们来看下,Object.is() 都有哪些规则:当操作值都没有被定义时,这时它们的值是 undefined ,通过 Object.is() 判断的结果为 true。let alet bObject.is(a,b) // trueObject.is() 也是严格区分大小写的。Object.is('Imooc', 'Imooc') // trueObject.is('Imooc', 'imooc') // false操作值的类型必须相同,无论是什么值,只要类型不一样就会返回 false。Object.is(null, 'null') // falseObject.is(undefined, 'undefined') // false判断引用类型的值时,引用类型的地址相同时,则相等,与 == 和 === 判断的结果是一样的。let a = {"name": "imooc"}let b = aObject.is(a, b) // trueObject.is({"name": "imooc"}, {"name": "imooc"}) // falseObject.is(window, window) // true, 只有一个window全局变量操作数是 0、+0、-0 的比较,0 和 +0 是相等的,因为正号可以省略,但是 0 和 -0 是不相等的,这样就和 === 判断的结果不一样了。Object.is(0, +0) // trueObject.is(0, -0) // false当两个操作值是 NaN 时,使用 Object.is() 返回的结果是 true 这个和 === 返回的结果不一样,如果计算的结果是 NaN 的话,返回的结果也是 true。Object.is(NaN, NaN) // trueObject.is(NaN, 0/0) // true
首先准备 nginx.conf,中间简单配置几条 expires 指令用作测试:...http{ server { listen 8000; location / { default_type text/plain; expires 10m; #expires -1h; return 200 '8000, server\n'; } }}...下面观察请求结果:# 使用 expires 10m 配置,可以看到Expires值正好为10分钟后[shen@shen ~]$ curl http://180.76.152.113:8000 -IHTTP/1.1 200 OKServer: nginx/1.17.6Date: Thu, 06 Feb 2020 11:37:17 GMTContent-Type: text/plainContent-Length: 13Connection: keep-aliveExpires: Thu, 06 Feb 2020 11:47:17 GMTCache-Control: max-age=600# 使用 expires -1h 配置, -1h表示环境一个小时前过期了,所以返回Cache-Control的值为no-cache[shen@shen ~]$ curl http://180.76.152.113:8000 -IHTTP/1.1 200 OKServer: nginx/1.17.6Date: Thu, 06 Feb 2020 11:37:32 GMTContent-Type: text/plainContent-Length: 13Connection: keep-aliveExpires: Thu, 06 Feb 2020 10:37:32 GMTCache-Control: no-cache
Spring Security 提供了一些默认 HTTP 安全性相关的响应头。这些默认响应头如下:
访问 http://127.0.0.1:8080/goods ,返回内容如下,可见后端接口已经可用。浏览器显示返回数据
创建文件 main.py,它是 Flask 程序的入口,源代码由如下部分构成:
默认情况下,CSRF 保护功能已被开启,如果需要关闭,可通过如下方式配置:@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) { http.csrf(csrf -> csrf.disable()); }}
Nginx 的压缩配置主要是用在与浏览交互中,对网页、css、js等静态资源进行压缩,通过消耗 cpu 的计算资源来节约大量的带宽,提高传输效率,给用户良好的体验。Nginx 中的 ngx_http_gzip_module 就是专门处理这里压缩功能的模块。其中部分重要指如下:gzip: 是否打开 gzip 压缩功能;Syntax: gzip on | off;Default: gzip off;Context: http, server, location, if in locationgzip_buffers: 设置压缩所需要的缓冲区大小;Syntax: gzip_buffers number size;Default: gzip_buffers 32 4k|16 8k;Context: http, server, locationgzip_comp_level: 设置压缩级别,从1-9;越大压缩率越高,同时消耗cpu资源也越多;Syntax: gzip_comp_level level;Default: gzip_comp_level 1;Context: http, server, locationgzip_types:需要压缩的文件格式 text/html默认会压缩,不用添加;Syntax: gzip_types mime-type ...;Default: gzip_types text/html;Context: http, server, locationgzip_min_length: 压缩文件最小大小;Syntax: gzip_min_length length;Default: gzip_min_length 20;Context: http, server, location一个常见的压缩配置如下: # 开启gzip压缩 gzip on; # http的协议版本 gzip_http_version 1.0; # IE版本1-6不支持gzip压缩,关闭 gzip_disable 'MSIE[1-6].'; #需要压缩的文件格式 gzip_types text/css text/javascript application/javascript image/jpeg image/png image/gif; #设置为4个8K内存作为压缩结果流缓存 gzip_buffers 4 8k; #压缩文件最小大小 gzip_min_length 1k; #压缩级别1-9 gzip_comp_level 9; #给响应头加个vary,告知客户端能否缓存 gzip_vary on; #反向代理时使用 gzip_proxied off;注意: gzip 的开启需适应特定的场景,比如大文件和图片的传输就不是和开启 gzip 功能,压缩效果不明显的同时还白白耗费系统的资源,所以使用时需要慎重考虑。
使用 httpBasic 测试基本认证:mvc.perform(get("/").with(httpBasic("user","password")))这一步相当于为请求增加了以下认证头:Authorization: Basic dXNlcjpwYXNzd29yZA==
指的是 HTTP 报文设计的简单易懂,对新手很友好,很容易上手,降低了学习门槛。
打开终端并运行下面两条命令即可下载并解压 Nginx :$ wget http://Nginx.org/download/nginx-1.17.6.tar.gz$ tar -xzf Nginx-1.17.6.tar.gz
3XX 代表重定向,代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的 Location 域中指明。
当一个 HTTP 请求打进服务器之后,一般的流程是:网关层(例如Ngnix)最先获取请求,然后路由转发到具体的Web服务,经过一段业务逻辑之后,可能还会查询数据库,最后将处理的结果返回给浏览器客户端。对于后端开发程序员来说,日常的工作就集中在服务器端,特别是流程图中的"Web业务服务"这块,例如基于 Spring 框架、Django 框架或者ThinkPHP 框架进行业务逻辑开发和上线。(HTTP 请求进入服务器端后的解析流程图)