中间件

中间件是一个函数,嵌入在HTTP 的请求和响应之间。它可以获得 Echo#Context 对象用来进行一些特殊的操作, 比如记录每个请求或者统计请求数。

Action的处理在所有的中间件运行完成之后。

中间件类型

Root Level (Before router)

Echo#Pre() 用于注册一个在路由执行之前运行的中间件,可以用来修改请求的一些属性。比如在请求路径结尾添加或者删除一个’/‘来使之能与路由匹配。

下面的这几个内建中间件应该被注册在这一级别:

  • AddTrailingSlash
  • RemoveTrailingSlash
  • MethodOverride

注意: 由于在这个级别路由还没有执行,所以这个级别的中间件不能调用任何 echo.Context 的 API。

Root Level (After router)

大部分时间将用到 Echo#Use() 在这个级别注册中间件。 这个级别的中间件运行在路由处理完请求之后,可以调用所有的 echo.Context API。

下面的这几个内建中间件应该被注册在这一级别:

  • BodyLimit
  • Logger
  • Gzip
  • Recover
  • BasicAuth
  • JWTAuth
  • Secure
  • CORS
  • Static

Group Level

当在路由中创建一个组的时候,可以为这个组注册一个中间件。例如,给 admin 这个组注册一个 BasicAuth 中间件。

用法

e := echo.New()
admin := e.Group("/admin", middleware.BasicAuth())

也可以在创建组之后用 admin.Use()注册该中间件。

Route Level

当你创建了一个新的路由,可以选择性的给这个路由注册一个中间件。

用法

e := echo.New()
e.GET("/", <Handler>, <Middleware...>)

BasicAuth (基本认证) 中间件

BasicAuth 中间件提供了 HTTP 的基本认证方式。

  • 对于有效的请求则继续执行后面的处理。
  • 对于无效的请求,返回”401 - Unauthorized”响应。

用法

e.Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
	if username == "joe" && password == "secret" {
		return true, nil
	}
	return false, nil
}))

demo

现在用一个小demo来演示BasicAuth的用法

package main

import (
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"net/http"
)

func main() {
	e := echo.New()
	e.Use(middleware.BasicAuth(func(username string, password string, context echo.Context) (b bool, e error) {
		if username == "eric" && password == "123" {
			return true, nil
		}
		return false, nil
	}))
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "OK")
	})
	e.Logger.Fatal(e.Start(":8000"))
}

这时在访问所有的路由之前会跳出一个对话框填写用户名跟密码

填写eric跟123即可进入

自定义配置

用法

e.Use(middleware.BasicAuthWithConfig(middleware.BasicAuthConfig{}))

配置

BasicAuthConfig struct {
  // Skipper 定义了一个跳过中间间的函数
  Skipper Skipper

  // Validator 是一个用来验证 BasicAuth 是否合法的函数
  // Validator 是必须的.
  Validator BasicAuthValidator

  // Realm 是一个用来定义 BasicAuth 的 Realm 属性的字符串
  // 默认是 "Restricted"
  Realm string
}

默认的Skipper是一个返回值为false的函数

BodyLimit(请求体限制) 中间件

BodyLimit 中间件用于设置允许的请求体的最大长度,如果请求体的大小超过了该值,则返回”413 - Request Entity Too Large”响应。 这个限制的判断取决于请求头的 Content-Length 和实际读取到的请求体内容两方面,尽可能的保证安全。

限制可以指定 4x 或者 4xB,x是”K, M, G, T, P”中的一个,前面的数字不一定是4可以是任意大小的数字

用法

e := echo.New()
e.Use(middleware.BodyLimit("2M"))

CORS(访问控制) 中间件

CORS 中间件实现了 CORS 的标准。CORS 提供给 web 服务器跨站的访问控制,使得跨站的数据传输更安全。

使用

e.Use(middleware.CORS())

自定义配置

使用

e := echo.New()
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
  AllowOrigins: []string{"https://labstack.com", "https://labstack.net"},
  AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
}))

配置

// CORSConfig defines the config for CORS middleware.
CORSConfig struct {
  // Skipper defines a function to skip middleware.
  Skipper Skipper

  // AllowOrigin defines a list of origins that may access the resource.
  // Optional. Default value []string{"*"}.
  AllowOrigins []string `json:"allow_origins"`

  // AllowMethods defines a list methods allowed when accessing the resource.
  // This is used in response to a preflight request.
  // Optional. Default value DefaultCORSConfig.AllowMethods.
  AllowMethods []string `json:"allow_methods"`

  // AllowHeaders defines a list of request headers that can be used when
  // making the actual request. This in response to a preflight request.
  // Optional. Default value []string{}.
  AllowHeaders []string `json:"allow_headers"`

  // AllowCredentials indicates whether or not the response to the request
  // can be exposed when the credentials flag is true. When used as part of
  // a response to a preflight request, this indicates whether or not the
  // actual request can be made using credentials.
  // Optional. Default value false.
  AllowCredentials bool `json:"allow_credentials"`

  // ExposeHeaders defines a whitelist headers that clients are allowed to
  // access.
  // Optional. Default value []string{}.
  ExposeHeaders []string `json:"expose_headers"`

  // MaxAge indicates how long (in seconds) the results of a preflight request
  // can be cached.
  // Optional. Default value 0.
  MaxAge int `json:"max_age"`
}

默认配置

DefaultCORSConfig = CORSConfig{
  Skipper:      defaultSkipper,
  AllowOrigins: []string{"*"},
  AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
}

CSRF Middleware(跨站请求伪造)

CSRF(Cross-site request forgery 跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。 跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

使用

e.Use(middleware.CSRF())

自定义配置

使用

e := echo.New()
e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
  TokenLookup: "header:X-XSRF-TOKEN",
}))

上面例子将使用X-XSRF-TOKEN 请求头取出 CSRF 的 token 值。

获取 CSRF Token

服务器端

服务器端可以使用 ContextKey从 Echo#Context 拿到 CSRF token 然后通过模版传给客户端。

客户端

客户端可以通过 CSRF cookie 拿到 token 值。

配置

// CSRFConfig defines the config for CSRF middleware.
CSRFConfig struct {
  // Skipper defines a function to skip middleware.
  Skipper Skipper

  // TokenLength is the length of the generated token.
  TokenLength uint8 `json:"token_length"`
  // Optional. Default value 32.

  // TokenLookup is a string in the form of "<source>:<key>" that is used
  // to extract token from the request.
  // Optional. Default value "header:X-CSRF-Token".
  // Possible values:
  // - "header:<name>"
  // - "form:<name>"
  // - "query:<name>"
  TokenLookup string `json:"token_lookup"`

  // Context key to store generated CSRF token into context.
  // Optional. Default value "csrf".
  ContextKey string `json:"context_key"`

  // Name of the CSRF cookie. This cookie will store CSRF token.
  // Optional. Default value "csrf".
  CookieName string `json:"cookie_name"`

  // Domain of the CSRF cookie.
  // Optional. Default value none.
  CookieDomain string `json:"cookie_domain"`

  // Path of the CSRF cookie.
  // Optional. Default value none.
  CookiePath string `json:"cookie_path"`

  // Max age (in seconds) of the CSRF cookie.
  // Optional. Default value 86400 (24hr).
  CookieMaxAge int `json:"cookie_max_age"`

  // Indicates if CSRF cookie is secure.
  // Optional. Default value false.
  CookieSecure bool `json:"cookie_secure"`

  // Indicates if CSRF cookie is HTTP only.
  // Optional. Default value false.
  CookieHTTPOnly bool `json:"cookie_http_only"`
}

默认配置

DefaultCSRFConfig = CSRFConfig{
  Skipper:      defaultSkipper,
  TokenLength:  32,
  TokenLookup:  "header:" + echo.HeaderXCSRFToken,
  ContextKey:   "csrf",
  CookieName:   "_csrf",
  CookieMaxAge: 86400,
}

Logger(日志) 中间件

Logger 中间件记录了每一个请求的信息。

用法

e.Use(middleware.Logger())

输出样例

{"time":"2017-01-12T08:58:07.372015644-08:00","remote_ip":"::1","host":"localhost:1323","method":"GET","uri":"/","status":200, "latency":14743,"latency_human":"14.743µs","bytes_in":0,"bytes_out":2}

自定义配置

用法

e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
  Format: "method=${method}, uri=${uri}, status=${status}\n",
}))

输出样例

method=GET, uri=/, status=200

配置

LoggerConfig struct {
  // Skipper 定义了一个跳过中间件的函数.
  Skipper Skipper
  
  // 日志的格式可以使用下面的标签定义。:
  //
  // - time_unix
  // - time_unix_nano
  // - time_rfc3339
  // - time_rfc3339_nano
  // - id (Request ID - Not implemented)
  // - remote_ip
  // - uri
  // - host
  // - method
  // - path
  // - referer
  // - user_agent
  // - status
  // - latency (In microseconds)
  // - latency_human (Human readable)
  // - bytes_in (Bytes received)
  // - bytes_out (Bytes sent)
  // - header:<name>
  // - query:<name>
  // - form:<name>
  //
  // 例如 "${remote_ip} ${status}"
  //
  // 可选。默认值是 DefaultLoggerConfig.Format.
  Format string `json:"format"`

  // Output 是记录日志的位置。
  // 可选。默认值是 os.Stdout.
  Output io.Writer
}

默认配置

DefaultLoggerConfig = LoggerConfig{
  Skipper: defaultSkipper,
  Format: `{"time":"${time_rfc3339_nano}","remote_ip":"${remote_ip}","host":"${host}",` +
    `"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` +
    `"latency_human":"${latency_human}","bytes_in":${bytes_in},` +
    `"bytes_out":${bytes_out}}` + "\n",
  Output: os.Stdout
}

Redirect 中间件

HTTPS 重定向

HTTPS 重定向中间件将 http 请求重定向到 https。例如,http://laily.net 将被重定向到 https://laily.net。

使用

e := echo.New()
e.Pre(middleware.HTTPSRedirect())

HTTPS WWW 重定向

HTTPS WWW 重定向将 http 请求重定向到带 www 的https 请求。例如,http://laily.net 将被重定向到 https://www.laily.net。

使用

e := echo.New()
e.Pre(middleware.HTTPSWWWRedirect())

HTTPS NonWWW 重定向

HTTPS NonWWW 将 http 请求重定向到不带 www 的 https 请求。例如,http://www.laily.net 将被重定向到 https://laily.net。

使用

e := echo.New()
e.Pre(middleware.HTTPSNonWWWRedirect())

WWW 重定向

将不带 www 的请求重定向到带 www 的请求。

例如,http://laily.net 重定向到 http://www.laily.net

使用

e := echo.New()
e.Pre(middleware.WWWRedirect())

NonWWW 重定向

将带 www 的请求重定向到不带 www 的请求。

例如,http://www.laily.net 重定向到 http://laily.net

使用

e := echo.New()
e.Pre(middleware.NonWWWRedirect())

Secure 中间件

Secure 中间件用于阻止跨站脚本攻击(XSS),内容嗅探,点击劫持,不安全链接等其他代码注入攻击。

使用

e.Use(middleware.Secure())

自定义配置

用法

e := echo.New()
e.Use(middleware.SecureWithConfig(middleware.SecureConfig{
	XSSProtection:         "",
	ContentTypeNosniff:    "",
	XFrameOptions:         "",
	HSTSMaxAge:            3600,
	ContentSecurityPolicy: "default-src 'self'",
}))

传递空的  XSSProtectionContentTypeNosniffXFrameOptions 或 ContentSecurityPolicy 来禁用这项保护。

配置

SecureConfig struct {
  // Skipper 定义了一个跳过该中间件的函数。
  Skipper Skipper
  
  // XSSProtection 通过设置`X-XSS-Protection`头
  // 来提供XSS攻击的防护。
  // 可选。默认值 "1; mode=block"。
  XSSProtection string `json:"xss_protection"`

  // ContentTypeNosniff 通过设置`X-Content-Type-Options`头
  // 来防止内容嗅探。
  // 可选。默认值 "nosniff"。
  ContentTypeNosniff string `json:"content_type_nosniff"`

  // XFrameOptions 被用来指示是否允许浏览器在<fram>,<iframe>或者<object>中渲染页面。
  // 网站可以通过这样来避免点击劫持,保证网站的内容不会被其他站点嵌入。
  // 可选。默认值 "SAMEORIGIN".
  // 可使用的值:
  // `SAMEORIGIN` - 页面只能在同域名的页面下被渲染。
  // `DENY` - 页面不允许在 frame 中显示。
  // `ALLOW-FROM uri` - 页面只能在指定域名的 frame 中显示。
  XFrameOptions string `json:"x_frame_options"`

  // HSTSMaxAge 设置 `Strict-Transport-Security` 头来指示浏览器需要记住这个网站只能通过HTTPS来访问的时间(单位秒)。
  // 这样可以减少遭受中间人攻击(HITM)的几率。
  // 可选。默认值 0。
  HSTSMaxAge int `json:"hsts_max_age"`

  // HSTSExcludeSubdomains 不会在`Strict Transport Security`中设置`includeSubdomains`标签。
  // 即从安全规则中排除所有子域名。
  // header, excluding all subdomains from security policy. It has no effect
  // 只有在HSTSMaxAge 被设置为非0值时该参数才有效。
  // 可选。默认值 false。
  HSTSExcludeSubdomains bool `json:"hsts_exclude_subdomains"`

  // ContentSecurityPolicy 用来设置 `Content-Security-Policy` 头。
  // `Content-Security-Policy` 用来定义页面可以加载哪些资源,减少XSS等通过运行不安全代码的注入攻击。
  // 可选。默认值 "".
  ContentSecurityPolicy string `json:"content_security_policy"`
}

默认配置

DefaultSecureConfig = SecureConfig{
  Skipper:            defaultSkipper,
  XSSProtection:      "1; mode=block",
  ContentTypeNosniff: "nosniff",
  XFrameOptions:      "SAMEORIGIN",
}

Trailing Slash 中间件

添加尾部斜杠

在请求的 uri 后加上反斜杠

使用

e := echo.New()
e.Pre(middleware.AddTrailingSlash())

去除尾部斜杠

在请求的 uri 后去除反斜杠

用法

e := echo.New()
e.Pre(middleware.RemoveTrailingSlash())

自定义中间件

定义

firstMiddle.go

package firstMiddle

import (
	"fmt"
	"github.com/labstack/echo"
)

type Config struct {
	Skipper bool
	Reaml string
}

var (
	DefaultConfig =Config{
		Skipper: true,
		Reaml: "first",
	}
)

func FirstMiddle(conf Config) echo.MiddlewareFunc {
	// 默认配置
	if conf.Reaml == "" {
		conf.Reaml = DefaultConfig.Reaml
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			request :=c.Request()
			method := request.Method
			fmt.Println("=======" + method + "========")
			if conf.Reaml == "first" {
				return next(c)     // 执行next(c)会往下执行
			} else {
				return nil    // 不会往下执行
			}
		}
	}
}

注册

e.Pre(firstMiddle.FirstMiddle(firstMiddle.Config{
    Reaml: "hahaha",
}))

 

posted @ 2019-05-25 10:38  Jin同学  阅读(329)  评论(0)    收藏  举报