基于Go语言实现的单点登录系统

2023-06-13,,

参考:https://jwt.io/introduction/

单点登录是基于Go语言实现的单点登录系统,下面先简单介绍JWT

JSON Web Token简述

JWT是一个开放的标准(RFC 7519),它定义了一个紧凑且自包含的方式,用于在各方之间以JSON对象安全地传输信息.这些信息可以通过数字签名进行验证和信任。可以使用秘密(使用HMAC算法)或使用RSA的公钥/私钥对来对JWT进行签名。

紧凑:由于它们尺寸较小,JWT可以通过URL,POST参数或HTTP头内发送。另外,尺寸越小意味着传输速度越快。

自包含:有效载荷包含有关用户的所有必需信息,避免了多次查询数据库的需要。

JSON Web Token 应用场景

身份验证(Authentication):这是使用JWT最常见的情况。一旦用户登录,每个后续请求将包括JWT,允许用户访问该令牌允许的路由,服务和资源。单点登录是目前广泛使用JWT的一项功能,因为它的开销很小,而且能够轻松地在不同的域中使用。

信息交换:JSON Web Tokens是在各方之间安全传输信息的好方法。因为JWT可以被签名,例如使用公钥/私钥对,所以你可以确定发件人是他们说的那个人。此外,由于使用标头、有效载荷、计算签名,因此您还可以验证内容是否未被篡改。

JWT 结构

JSON Web Tokens包含三个由点(.)分隔的部分,它们是:头部、有效载荷、签名
JWT通常看起来如下所示。
xxxxx.yyyyy.zzzzz

Header头部

头部通常由两部分组成:令牌的类型(即JWT)和正在使用的散列算法(the hashing algorithm 如HMAC SHA256或RSA)。
例如:
{
"alg": "HS256",
"typ": "JWT"
}
然后,这个JSON被Base64Url编码,形成JWT的第一部分。

有效载荷

令牌的第二部分是包含声明的有效载荷。 声明是关于实体(通常是用户)和附加元数据的声明。 有三种类型的声明:保留,公开和私有声明。

保留的声明(Reserved claims):这是一组预先定义的声明,不是强制性的,但推荐使用,以提供一组有用的,可互操作的声明。 其中一些是:iss(发行人),exp(到期时间),sub(主题),aud(听众)等等。

    请注意,声明名称只有三个字符长,因为JWT是紧凑的。

公开声明(Public claims):这些可以由使用JWT的人员任意定义。 但为避免冲突,应在IANA JSON Web令牌注册表中定义它们,或者将其定义为包含防冲突命名空间的URI。

私有声明(Private claims):这是为了同意使用它们的各方之间共享信息而创建的自定义claims。

有效载荷的一个例子可以是:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后将有效载荷Base64Url进行编码,以形成JSON Web令牌的第二部分。

签名

要创建签名部分,您必须采用编码头部,编码有效载荷,密钥,头部中指定的算法并签名。

例如,如果您想使用HMAC SHA256算法,签名将按以下方式创建:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
签名用于验证JWT的发件人是谁,并确保邮件在一路上没有改变。

拼接在一起

输出是三个由点分隔的Base64字符串,可以轻松地在HTML和HTTP环境中传递,而与基于XML的标准(如SAML)相比,它更加紧凑。
下面显示了一个JWT,它具有先前的头部和有效载荷编码,并且用秘密签名。

JSON Web Token如何工作

在身份验证中(authentication),当用户使用他们的凭证(credentials)成功登录时,将返回一个JSON Web Token,并且必须保存在本地(通常在本地存储中,但也可以使用Cookie),而不是在传统方法中创建会话 服务器并返回一个cookie。

无论何时用户想要访问受保护的路由或资源,用户代理都应发送JWT,通常在Authorization头部的Bearer模式中。 头部的内容应该如下所示:
Authorization: Bearer <token>

这是一种无状态身份验证机制,因为用户状态永远不会保存在服务器内存中。 服务器的受保护路由将在授权头中检查有效的JWT,如果存在,则允许用户访问受保护的资源。 由于JWT是独立的,所有必要的信息都在那里,减少了多次查询数据库的需求。

这使您可以完全依赖无状态的数据API,甚至向下游服务发出请求。 无论哪个域正在为您的API提供服务,跨源资源共享(CORS)将不会成为问题,因为它不使用cookie。

下图显示了这个过程:

为什么我们应该使用JWT

让我们来谈谈与简单Web令牌(SWT)和安全声明标记语言令牌(SAML)相比,JSON Web令牌(JWT)的好处。

由于JSON不如XML冗长,所以在进行编码时,它的大小也会变小,从而使JWT比SAML更紧凑。这使得JWT成为在HTML和HTTP环境中传递的一个很好的选择。

安全方面,SWT只能通过使用HMAC算法的共享秘密进行对称签名。但是,JWT和SAML令牌可以使用X.509证书形式的公钥/私钥对进行签名。与签署JSON的简单性相比,使用XML数字签名签署XML而不引入模糊的安全漏洞是非常困难的。

JSON解析器在大多数编程语言中都很常见,因为它们直接映射到对象。相反,XML没有自然的文档到对象映射。这使得使用JWT比SAML断言更容易。

关于使用情况,JWT在互联网上使用。这突出显示了在多个平台(尤其是移动平台)上JSON Web令牌的客户端处理的简易性。

Go语言实战项目代码

支持手机号码+验证码、邮箱+验证码、微信第三方授权三种方式注册
支持手机号码、用户名、邮箱号码、微信登录
支持手机和邮箱找回密码
支持阿里云通信和互亿无线的短信验证码服务

代码路径:https://github.com/KenmyZhang/single-sign-on
package api

import (
    "net/http"
    "regexp"        
    "strconv"
    "fmt"
    l4g "github.com/alecthomas/log4go"

    "github.com/KenmyZhang/single-sign-on/app"
    "github.com/KenmyZhang/single-sign-on/model"
    "github.com/KenmyZhang/single-sign-on/utils"
    "github.com/KenmyZhang/single-sign-on/sqlStore"
)

func InitUser() {
    l4g.Debug(utils.T("api.user.init.debug"))
    BaseRoutes.User.Handle("", ApiCustomClaimsRequired(getUser)).Methods("GET")
    BaseRoutes.User.Handle("/image", ApiHandler(getProfileImage)).Methods("GET")
    BaseRoutes.User.Handle("/image", ApiCustomClaimsRequired(setProfileImage)).Methods("POST")      
    BaseRoutes.Users.Handle("/login", ApiHandler(login)).Methods("POST")
    BaseRoutes.Users.Handle("/logout", ApiHandler(logout)).Methods("POST")
    BaseRoutes.Users.Handle("/sendsms", ApiHandler(sendSmsCode)).Methods("POST")
    BaseRoutes.Users.Handle("/phone/signup", ApiHandler(signupByMobile)).Methods("POST")
    BaseRoutes.Users.Handle("/phone/login", ApiHandler(loginByMobile)).Methods("POST")
    BaseRoutes.Users.Handle("/phone/exist", ApiHandler(isMobileExist)).Methods("POST")
    BaseRoutes.Users.Handle("/phone/reset", ApiHandler(resetPasswordByMobile)).Methods("POST")
    BaseRoutes.Users.Handle("/email/verify/code/send", ApiHandler(sendVerificationCodeEmail)).Methods("POST")
    BaseRoutes.Users.Handle("/email/signup", ApiHandler(signupByEmail)).Methods("POST")
    BaseRoutes.Users.Handle("/email/exist", ApiHandler(isEmailExist)).Methods("POST")
    BaseRoutes.Users.Handle("/email/reset", ApiHandler(resetPasswordByEmail)).Methods("POST")
    BaseRoutes.Users.Handle("/search", ApiCustomClaimsRequired(searchUsers)).Methods("POST")
}

    ......

推荐一篇文章
https://www.cnblogs.com/lyzg/p/6132801.html
https://studygolang.com/articles/11793

《基于Go语言实现的单点登录系统.doc》

下载本文的Word格式文档,以方便收藏与打印。