golang使用grpc+go-kit模拟oauth认证

84 篇文章 3 订阅
31 篇文章 0 订阅

我们使用grpc对外的接口,进行服务,模拟对外认证的接口

首先我们要了解oauth的基本认证过程
在这里插入图片描述
第三方的服务端,在oauth2.0中作为一个客户端的身份,进行请求数据。

  • 用户进行选择第三方的登陆,比如选择到某一个第三方的平台进行登陆,则会跳转到第三方登陆平台
  • 用户输入用户名密码,在第三方平台进行登陆,,如果登陆成功,则返回code。
  • 客户端,也就是我们想要登陆的网站,将会读取code,并且将会携带这个code,和第三方网站所颁发的密码,进行请求token,如果code和注册时所得到的密码,都验证成功,此时,第三方客户端会返回一个token。
  • 我们登陆的网站会携带这个token去请求用户身份资源的服务器,如果token比对成功,则返回用户的信息
    所以我们需要一些服务
  • codeserver,作用,分发code,验证code的准确性
  • tokenserver,作用分发token,验证token的准确性
  • loginserver,作用,登陆成功后,调用codeserver得到code
  • userdetailserver,作用调用tokenserver的token验证,验证token是否合法,如果合法,进行返回用户的基本信息
    继续,我们大概看一下这些功能具体怎样实现。

实现

codeserver

type Codeserver struc (
	GetCode ()
	ValidCode ()
)
//函数的具体传参今不写了

其实我们的code和token,主要是使用redis数据库进行实现,并且给申请的code和token设置过期时间, 也就是说,在数据库中实现一个定时的作用,如果,申请完code,长时间不申请token则这个code会过期,就会让用户重新进行登陆,重新获取code

func (s ServicesA) GetCode(c context.Context, req *codeserver.GetCodeReuqest) (*codeserver.RCodeResponse, error) {
	con , err := UseRedis()//加载redis,用于操作redis
	if err != nil {
		return nil , errors.New("the redis databases is not work")
	}
	randstr :=  GetRandomString(10)//随机生成一个字符串作为code
	_ , err = con.Do("hset" , req.UserId , "code" , randstr)//插入数据库,用于获取token时进行验证
	con.Do("set" , randstr , req.UserId , "EX" , 120)
	con.Do("EXPIRE" , req.UserId , 20)//设置code的过期时间
	if err != nil {
		return nil , errors.New("data is not insert")
	}
	return &codeserver.RCodeResponse{Code: randstr} , nil
}
//检查code是否合法
func (s ServicesA) Isvalid(c context.Context, req *codeserver.ValidRequest) (*codeserver.ValidResponse, error) {
	con , err := UseRedis()//加载redis
	if err != nil {
		return nil , errors.New("the databses is not work")
	}
	r , err := con.Do("get" , req.Code)//找到code,如果能找到code,则合法,找不到则不合法
	if err != nil {
		return nil , err
	}
	if r == nil {
		return &codeserver.ValidResponse{IsValid: false} , nil
	} else {
		return &codeserver.ValidResponse{IsValid: true} , nil
	}
}

至于其他的endpoint层和transport层等等,就先不写了,我们就这篇文章主要是看怎样模拟实现oauth
tokenserver

func Isvalid (request *codeserver.ValidRequest) bool {
	lis , err := grpc.Dial("127.0.0.1:8081" , grpc.WithInsecure())
	if err != nil {
		log.Println(err)
		return false
	}
	client := codeserver.NewCodeServerClient(lis)
	rep , err := client.Isvalid(context.Background() , request)
	if err != nil {
		log.Println(err)
		return false
	}
	if rep.IsValid {
		return true
	} else {
		return false
	}
}
func (s ServiceAI) GetToken(ctx context.Context, req *tokenservice.ReqGetToken) (*tokenservice.RepGetToken, error) {
//判断code是否合法
	if !Isvalid(&codeserver.ValidRequest{UserId: req.UserId , Code: req.Code}) {
		return nil , errors.New("code is not valid ")
	}
	con , err := UseRedis()
	if err != nil {
		return nil , errors.New("connet database default")
	}
	//通过code获取clientid
	User := GetUserId(req.Code)
	mysql , err := UseMysql()
	if err != nil {
		log.Println("get secrete default")
	}
	var c Client
	mysql.Table("client").Where("id = ?",req.ClientId).Find(&c)
//在mysql数据库中进行查找,请求所携带的密码,是否与第三方注册时给的密码是否相同,如果不相同,则不返回token。
	if c.Secret !=req.Secret {
		fmt.Println(c.Secret , " " , req.Secret)
		return nil , errors.New("not pi pei")
	}
	str := GetRandomString(11)
	_ , err = con.Do("hset" , User , "token" ,  str)
	con.Do("EXPIRE" , User , 120)
	//将生成的token进行插入数据库,并设置过期时间,如果避免token被多次利用
	con.Do("set" , str , User , "EX" , 120)
	//设置userid和token的对应关系,避免没有对应上,客户端拿到token之后随便拿取其他人的用户滤数据
	if err != nil {
		return nil , err
	}
	return &tokenservice.RepGetToken{Toen: str} , nil
}
//判断token是都合法,给userdetailserver用,当服务器接到token后,需要调用这个接口,查看token是否合法,如果合法返回用户数据
func (s ServiceAI) IsValidToken(ctx context.Context, req *tokenservice.IsValidTokenReq) (*tokenservice.IsValidToeknRep, error) {
	con , err := UseRedis()
	if err != nil {
		log.Println(err)
		return nil , err
	}
	r , err := con.Do("get" ,req.Token)
	if err != nil {
		return nil , err
	}
	if r == nil {
		return &tokenservice.IsValidToeknRep{IsValid: false} , nil
	}
	rep := string(r.([]uint8))
	return &tokenservice.IsValidToeknRep{IsValid: true , Userid: rep} , nil
}

useroauthserver

type User struct {
	Id int
	Name string
	Password string
	Al string
	UId string
}

func usemysql () (*gorm.DB , error) {
	return gorm.Open("mysql" , "root:123456@/oauth?charset=utf8&parseTime=True&loc=Local")
}
//调用codeserver接口,进行拿取code
func getcode (userid string) string {
	con , err := grpc.Dial(":8081" , grpc.WithInsecure())
	if err != nil {
		log.Println(err , errors.New("get code default"))
	}
	client := codeserver.NewCodeServerClient(con)
	rep , err := client.GetCode(context.Background() , &codeserver.GetCodeReuqest{UserId: userid})
	if err != nil || rep == nil{
		log.Println(err)
		return ""
	}
	return rep.Code
}
//认证用户,将上传的用户名和密码进行比对。
func (a AuthServicesA) AuthT(ctx context.Context, req *userauth.AuthRequest) (*userauth.AuthResponse, error) {
	con , err := usemysql()
	if err != nil {
		log.Println(err)
		return nil , errors.New("the database is connect default")
	}
	var u User
	con.Table("user").Where("uid =?" , req.Id).Find(&u)
	//在数据库中进行查找,如果没找到该用户,说明该用户不存在,或者用户输入错误
	if &u == nil {
		return nil , errors.New("the id is wrong ")
	}
	if req.Password != u.Password {
		return nil , errors.New("the user password is wrong")
	}
//如果认证成功,则进行调用codeserver接口,返回code
	code :=getcode(req.Id)
	if code == "" {
		return &userauth.AuthResponse{IsTrue: false} , nil
	}
	return &userauth.AuthResponse{Code: code , IsTrue: true} , nil
}

基本原理就是这样,但是我们还是差一个userdetail的服务端
这个服务端,主要作用就是拿到请求的token,并进行检验,如果检验成功,返回用户数据,至于怎样检验,就是调用tokenserver中的检验接口。
这里就不写了,留给读者完成。
我写的这三个接口在gitee上有源码,是基于golang写的,使用的框架有grpc,go-kit的服务框架。
具体源码地址gitee.com/silves-xiang

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值