网站统计,广州公司注册官网,如何在手机上制作app软件,马鞍山广播电视台中间件编程jwt认证
在不改变原有方法的基础上#xff0c;添加自己的业务逻辑。相当于grpc中的拦截器一样#xff0c;在不改变grpc请求的同时#xff0c;插入自己的业务。
简单例子
func Sum(a, b int) int {return a b
}func LoggerMiddleware(in func(a, b int) int) f…中间件编程jwt认证
在不改变原有方法的基础上添加自己的业务逻辑。相当于grpc中的拦截器一样在不改变grpc请求的同时插入自己的业务。
简单例子
func Sum(a, b int) int {return a b
}func LoggerMiddleware(in func(a, b int) int) func(a, b int) int {return func(a, b int) int {log.Println(logger middleware)return in(a, b)}
}func main() {f : SumLoggerMiddleware(f)(1, 2)
}D:\goProject\gobasic\goGingo run main.go
2023/10/07 12:21:58 logger middlewaregin 中间件编程
Gin 内置支持了中间件的逻辑
使用use。或者路由分组的时候也可以使用
r : gin.New()
r.User(gin.Logger(), gin.Recovery())跨域和jwt认证
什么是跨域
由于浏览器的同源策略限制进而产生跨域拦截问题。同源策略是浏览器最核心也最基本的安全功能所谓同源即指在同一个域就是两个页面具有相同的协议protocol主机host和端口号port。
同源策略Same origin policy是一种约定它是浏览器最核心也最基本的安全功能如果缺少了同源策略则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的浏览器只是针对同源策略的一种实现。同源策略分为两种
DOM 同源策略禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况不同域名的 iframe 是限制互相访问的。XMLHttpRequest 同源策略禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。
同源策略在解决浏览器访问安全的同时也带来了跨域问题当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
使用cors解决跨域问题
CORSCross-origin resource sharing跨域资源共享是一个 W3C 标准定义了在必须访问跨域资源时浏览器与服务器应该如何沟通。CORS 背后的基本思想就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通从而决定请求或响应是应该成功还是应该失败。CORS 需要浏览器和服务器同时支持。整个CORS通信过程都是浏览器自动完成不需要用户参与。对于开发者来说CORS通信与同源的AJAX通信没有差别代码完全一样。浏览器一旦发现AJAX请求跨源就会自动添加一些附加的头信息有时还会多出一次附加的请求但用户不会有感觉浏览器将CORS请求分成两类简单请求simple request和非简单请求not-so-simple request
简单请求
请求方法为 HEAD、GET、POST中的一种。HTTP头信息不超过一下几种 AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
对于简单请求浏览器回自动在请求的头部添加一个 Origin 字段来说明本次请求来自哪个源协议 域名 端口服务端则通过这个值判断是否接收本次请求。如果 Origin 在许可范围内则服务器返回的响应会多出几个头信息
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Content-Length
Access-Control-Allow-Origin: *
Content-Type: text/html; charsetutf-8实际上后续我们就是通过在服务端配置这些参数来处理跨域请求的。
非简单请求 非简单请求是那种对服务器有特殊要求的请求比如请求方法是 PUT 或 DELETE 或者 Content-Type 字段的类型是 application/json。 或者修改了请求头
非简单请求的CORS请求会在正式通信之前增加一次HTTP查询请求称为预检请求preflight预检请求其实就是我们常说的 OPTIONS 请求表示这个请求是用来询问的。头信息里面关键字段 Origin 表示请求来自哪个源除 Origin 字段预检请求的头信息包括两个特殊字段
//该字段是必须的用来列出浏览器的CORS请求会用到哪些HTTP方法
Access-Control-Request-Method
//该字段是一个逗号分隔的字符串指定浏览器CORS请求会额外发送的头信息字段.
Access-Control-Request-Headers浏览器先询问服务器当前网页所在的域名是否在服务器的许可名单之中以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复浏览器才会发出正式的 XMLHttpRequest 请求否则就报错。
golang配置cors解决跨域问题
上述介绍了两种跨域请求其中出现了几种特殊的 Header 字段CORS 就是通过配置这些字段来解决跨域问题的 Access-Control-Allow-Origin 该字段是必须的。它的值要么是请求时Origin字段的值要么是一个*表示接受任意域名的请求 Access-Control-Allow-Methods 该字段必需它的值是逗号分隔的一个字符串表明服务器支持的所有跨域请求的方法。注意返回的是所有支持的方法而不单是浏览器请求的那个方法。这是为了避免多次预检请求。 Access-Control-Allow-Headers 如果浏览器请求包括Access-Control-Request-Headers字段则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串表明服务器支持的所有头信息字段不限于浏览器在预检中请求的字段。 Access-Control-Expose-Headers 该字段可选。CORS请求时XMLHttpRequest对象的response只能拿到6个基本字段Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段就必须在Access-Control-Expose-Headers里面指定。 Access-Control-Allow-Credentials 该字段可选。它的值是一个布尔值表示是否允许发送Cookie。默认情况下Cookie不包括在CORS请求之中。设为true即表示服务器明确许可Cookie可以包含在请求中一起发给服务器。这个值也只能设为 true如果服务器不要浏览器发送Cookie删除该字段即可。 Access-Control-Max-Age 该字段可选用来指定本次预检请求的有效期单位为秒在此期间不用发出另一条预检请求
以gin框架为例子配置跨域中间件
package middlewareimport (github.com/gin-contrib/corsgithub.com/gin-gonic/gin
)func Cors() gin.HandlerFunc {return cors.New(cors.Config{// 允许所有域名访问AllowAllOrigins: true,// 允许请求头中的字段AllowHeaders: []string{Origin, Content-Length, Content-Type,},// 允许请求的方法AllowMethods: []string{GET, POST, PUT, DELETE, HEAD, OPTIONS,},})
}
然后在路由分组或者引擎初始化的时候可以添加。 一般在路由分组时候添加
JWT认证中间件
Json Web TokenJWT是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准RFC7519。JWT一般可以用作独立的身份验证令牌可以包含用户标识、用户角色和权限等信息以便于从资源服务器获取资源也可以增加一些额外的其它业务逻辑所必须的声明信息特别适用于分布式站点的登录场景。
JWT的构成
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ如上面的例子所示JWT就是一个字符串由三部分构成
Header头部Payload数据Signature签名
header
JWT的头部承载两个信息
声明类型这里是JWT声明加密的算法
{typ: JWT,alg: HS256
}然后将头部进行Base64编码该编码是可以对称解码的构成了第一部分。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9payload
载荷就是存放有效信息的地方。定义细节如下
iss令牌颁发者。表示该令牌由谁创建该声明是一个字符串
sub: Subject Identifieriss提供的终端用户的标识在iss范围内唯一最长为255个ASCII个字符区分大小写
audAudience(s)令牌的受众分大小写的字符串数组
expExpiration time令牌的过期时间戳。超过此时间的token会作废 该声明是一个整数是1970年1月1日以来的秒数
iat: 令牌的颁发时间该声明是一个整数是1970年1月1日以来的秒数
jti: 令牌的唯一标识该声明的值在令牌颁发者创建的每一个令牌中都是唯一的为了防止冲突它通常是一个密码学随机值。这个值相当于向结构化令牌中加入了一个攻击者无法获得的随机熵组件有利于防止令牌猜测攻击和重放攻击。也可以新增用户系统需要使用的自定义字段
然后将其进行Base64编码得到Jwt的第二部分
JTdCJTBBJTIwJTIwJTIyc3ViJTIyJTNBJTIwJTIyMTIzNDU2Nzg5MCUyMiUyQyUwQSUyMCUyMCUyMm5hbWUlMjIlM0ElMjAlMjJKb2huJTIwRG9lJTIyJTBBJTdESignature
这个部分需要Base64编码后的Header和Base64编码后的Payload使用 . 连接组成的字符串然后通过Header中声明的加密方式进行加密$secret 表示用户的私钥然后就构成了jwt的第三部分。
// javascript
var encodedString base64UrlEncode(header) . base64UrlEncode(payload);
var signature HMACSHA256(encodedString, $secret);将这三部分用 . 连接成一个完整的字符串就构成了 jwt
JWT几个特点
JWT 默认是不加密不能将秘密数据写入 JWT。JWT 不仅可以用于认证也可以用于交换信息。有效使用 JWT可以降低服务器查询数据库的次数。JWT 的最大缺点是由于服务器不保存 session 状态因此无法在使用过程中废止某个 token或者更改 token 的权限。也就是说一旦 JWT 签发了在到期之前就会始终有效除非服务器部署额外的逻辑。JWT 本身包含了认证信息一旦泄露任何人都可以获得该令牌的所有权限。为了减少盗用JWT 的有效期应该设置得比较短。对于一些比较重要的权限使用时应该再次对用户进行认证。为了减少盗用JWT 不应该使用 HTTP 协议明码传输要使用HTTPS 协议传输。
跨域配置 jwt认证案例
jwt加签验签
package jwt_pluginimport github.com/golang-jwt/jwt/v4
// 实际项目放配置文件
var key zxvfffsa123// payload 添加自定义字段
type Data struct {Name string json:nameAge int json:ageGender int json:genderjwt.RegisteredClaims
}
//签名
func Sign(data jwt.Claims) (string, error) {token : jwt.NewWithClaims(jwt.SigningMethodHS256, data)sign, err : token.SignedString([]byte(key))if err ! nil {return , err}return sign, err
}// 验签func Verify(sign string, data jwt.Claims) error {_, err : jwt.ParseWithClaims(sign, data, func(token *jwt.Token) (interface{}, error) {return []byte(key), nil})return err
}
登录认证
api 路由
package routersimport (github.com/gin-gonic/gingithub.com/golang-jwt/jwt/v4goGin/jwt_pluginnet/httptime
)func initLogin(group *gin.RouterGroup) {v1 : group.Group(/v1){// 正常应该post登录v1.GET(/login, login.Login)}
}func Login(c *gin.Context) {data : jwt_plugin.Data{Name: golang,Age: 17,Gender: 1,RegisteredClaims: jwt.RegisteredClaims{// 一小时过期ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),//签发时间IssuedAt: jwt.NewNumericDate(time.Now()),// 生效时间NotBefore: jwt.NewNumericDate(time.Now()),},}sign, err : jwt_plugin.Sign(data)if err ! nil {c.JSON(http.StatusInternalServerError, gin.H{error: err.Error(),})}c.JSON(http.StatusOK, gin.H{access_token: sign,})
}
路由初始化
package routersimport (github.com/gin-gonic/gingoGin/middlewaregoGin/user
)func InitRouters(r *gin.Engine) {//使用路由分组// 添加跨域api : r.Group(/api)api.Use(middleware.Cors(), middleware.Auth())initUser(api)// 除了登录以外的 不需要认证notAuthApi : r.Group(/api)notAuthApi.Use(middleware.Cors())initLogin(notAuthApi)
}func initUser(group *gin.RouterGroup) {// 路由分组v1 : group.Group(/v1){// /api/v1/course// 路径携带参数v1.GET(/user/:id, user.Get)v1.POST(/user/:id, user.Add)}// v2版本v2 : group.Group(v2){v2.GET(/user/:id, user.GetV2)v2.POST(/user/:id, user.AddV2)}
}其他API
user.go
package userimport (github.com/gin-gonic/ginnet/http
)type user struct {Name string json:name binding:required, alphaunicodeAge int json:age binding:omitempty, number // 允许为空Phone string json:phone binding:omitempty, e164Email string json:email binding:omitempty, email
}func Add(c *gin.Context) {req : user{}err : c.ShouldBindJSON(req)if err ! nil {c.JSON(http.StatusInternalServerError, gin.H{error: err.Error(),})}// 都是gin.context作为入参c.JSON(http.StatusOK, gin.H{method: c.Request.Method,path: c.Request.URL.Path,req: req,})
}func Get(c *gin.Context) {id : c.Param(id)// 都是gin.context作为入参c.JSON(http.StatusOK, gin.H{method: c.Request.Method,path: c.Request.URL.Path,id: id,})
}func AddV2(c *gin.Context) {// 都是gin.context作为入参c.JSON(http.StatusOK, gin.H{method: c.Request.Method,path: c.Request.URL.Path,})
}func GetV2(c *gin.Context) {authInfo, _: c.Get(auth_info)// 都是gin.context作为入参c.JSON(http.StatusOK, gin.H{method: c.Request.Method,path: c.Request.URL.Path,auth_info: authInfo,})
}
测试
认证验签 不带token