中间件
中间件是一个函数,嵌入在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'", }))
传递空的 XSSProtection
, ContentTypeNosniff
, XFrameOptions
或 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", }))