3.4. 拦截器实现及cookie解密并验证
上一节课我们讲过了为什么使用cookie代替session,很多SX说cookie不安全,不建议用cookie,一定要用session,那是骗鬼的哈!看你怎么用!大流量高并发情况下你服务器读取session就会浪费很多资源,cookie反而是分布存储到了用户自己的电脑浏览器,这样对你服务器没有任何压力,不香吗?
既然将了如何加密cookie保证绝对安全防止cookie被篡改那么就有招数解密cookie实现登录验证,当然不能单纯的去讲如何实现解密cookie实现登录验证,这里我们捎带着把iris当中实现拦截器的功能带出来!
因为我们一直在强调分布式,所以呢,我们这里的权限验证单独做成一个服务,这样耦合度大大降低,不是很好吗?这里只讲如何实现架构上的调整以及解密cookie实现登录验证,至于分布式权限验证,看好了哈,是分布式权限验证,这个我们今天不讲,比较费劲,还没想好要不要以博客的形式发出来!
单独新建一个validate.go文件 随便你放在哪里 我是放在了项目跟目录下了
你爱放哪放哪哈,这个无所谓
上代码:
validate.go
//分布式权限验证 需要独立运行的一个模块
package main
import (
"errors"
"fmt" "net/http" "product/common" "product/encrypt")
//执行正常的业务逻辑 在拦截验证完成之后走的正常逻辑
func Check(w http.ResponseWriter,r *http.Request){
//执行正常业务逻辑
fmt.Println("执行check!")
}
//统一验证拦截器 每个接口都需要提前验证
func Auth(w http.ResponseWriter,r *http.Request) error {
fmt.Println("进行了验证!")
//添加基于cookie的权限验证
//在拦截验证器里面我们需要验证每个用户的cookie
err:=CheckUserInfo(r)
if err != nil {
return err
}
return nil
}
//验证cookie用到的身份校验函数
func CheckUserInfo(r *http.Request) error {
//获取uid cookie
uidCookie,err := r.Cookie("uid")
if err != nil {
return errors.New("用户uid cookie 获取失败!")
}
//获取用户加密串
signCookie,err := r.Cookie("sign")
if err != nil {
return errors.New("用户 sign 加密串获取失败!")
}
//对sign加密串进行解密
signBytes, err := encrypt.DePwdCode(signCookie.Value)
if err != nil {
return errors.New("加密串被串改")
}
fmt.Println("结果比对")
fmt.Println("用户ID:"+uidCookie.Value)
fmt.Println("解密后的用户ID"+string(signBytes))
//拿到解密后的值跟cookie里面的uid比对 因为我们使用uid进行的aes加密 比对正确则通过 否则失败
if checkInfo(uidCookie.Value,string(signBytes)) {
return nil
}
return errors.New("身份校验失败!")
}
//自定义逻辑判断
func checkInfo(checkStr string,signStr string) bool {
if checkStr == signStr{
return true
}else{
return false
}
}
func main(){
//1.过滤器
filter := common.NewFilter()
//注册拦截器 将我们访问的uri和FilterHandle类型绑定起来 FilterHandle类型是一个函数类型 对应关系放到了结构体里面的参数filterMap map字典当中去了
filter.RegisterFilterUri("/check",Auth)
filter.RegisterFilterUri("/cc",Auth)
//2.启动服务
//当访问/check的时候会自动去执行自定义方法 Handle(Check)并把要正常执行的函数放进去
http.HandleFunc("/check",filter.Handle(Check))
http.HandleFunc("/cc",filter.Handle(Check))
//启动服务 这没啥好讲的啦
http.ListenAndServe(":8083",nil)
}
filter.go文件
我是放在了common包里面:
放哪里你自己看着办就行 只要在你那跑的时候都调对了就中!!!
//过滤
package common
import "net/http"
//声明一个新的数据类型(函数类型) 可以把函数当做是一种类型 这也是golang的特点
type FilterHandle func(rw http.ResponseWriter,req *http.Request) error
//拦截器结构体
type Filter struct {
//用来存储需要拦截的URI
filterMap map[string]FilterHandle
}
//Filter初始化函数 你可以把它理解为构造函数 有了这个构造函数那么就可以调用结构体Filter相关的方法了
func NewFilter() *Filter {
return &Filter{filterMap:make(map[string]FilterHandle)}
}
//注册拦截器 把我们的uri和handler绑定起来
func (f *Filter) RegisterFilterUri(uri string,handler FilterHandle) {
f.filterMap[uri] = handler
}
//根据uri获取对应的handle
func (f *Filter) GetFilterHandle(uri string) FilterHandle {
return f.filterMap[uri]
}
//声明新的函数类型
type WebHandle func(rw http.ResponseWriter,req *http.Request)
//执行拦截器并返回函数类型
//要去返回的是一个函数类型 返回函数其实就是在执行这个匿名函数
func (f *Filter) Handle(webHandle WebHandle) func(rw http.ResponseWriter,r *http.Request) {
return func(rw http.ResponseWriter, r *http.Request) {
//在函数体当中我们遍历了Filter结构体里面的filterMap map字典 因为我们在此之前执行了注册拦截器操作就是把uri和FilterHandle类型绑定起来了
//path就是FilterHandle类型的函数 可以用来直接执行 比如这里path就是 /check handle其实就是Auth() 那么Auth()就要和 FilterHandle类型保持一致
for path,handle := range f.filterMap{
//判断当前浏览器访问的路径是否和map字典里面注册进来的一致
if path == r.RequestURI {
//执行拦截业务逻辑 如果一直 执行拦截业务 也就是您在注册之后绑定的handle类型的函数 这里是 Auth() err := handle(rw, r)
//判断是否报错
if err!= nil {
rw.Write([]byte(err.Error()))
return
}
//会不断的循环遍历map字典里面的值 一旦找到一个一样的 拦截并验证完成之后 跳出循环
break
}
}
//拦截请求并验证完成之后 执行正常注册的函数 这里正常的注册函数就是Check() 里面可以写你正常的业务逻辑
webHandle(rw,r)
}
}
我真的不知道该用什么语言来描述,所以就把注释写到代码里面去了,如果看注释你还不明白的话,请加qq:495681586咨询 我会耐心指导你 不过是收费的!天下没有免费的午餐,这些东西也是我自己费劲吧啦熬夜写出来的!