本文讲解了Genbu
涉及到的中间件、多集群管理实现等功能,但是并没有介绍用户相关的内容。统一登录也是我们所需的、很实用的一个功能了。接下来我们以案例的方式来讲解一下吧!
案例演示
下载依赖包
代码语言:javascript复制go get github.com/casbin/gorm-adapter/v3@v3.10.0
配置文件
代码语言:javascript复制ldap:
# ldap用户登录
address: 127.0.0.1:389
adminUser: cn=admin,dc=kubesre,dc=com
baseDN: dc=kubesre,dc=com
password: 123456
逻辑处理
代码语言:javascript复制func (u *userInfo) LdapLogin(userName, password string) (*mod.User, error) {
// 连接ldap
l, err := ldap.Dial("tcp", viper.GetString("ldap.address"))
if err != nil {
global.TPLogger.Error("连接ldap失败:", err)
return nil, errors.New("连接ldap失败")
}
_, err = l.SimpleBind(&ldap.SimpleBindRequest{
Username: fmt.Sprintf("cn=%s,ou=user,%s", userName, viper.GetString("ldap.baseDN")),
Password: password,
})
defer l.Close()
if err != nil {
global.TPLogger.Error("登录失败:", err)
return nil, errors.New("登录失败")
}
var ldapUser *mod.User
ldapUser, err = dao.NewUserInterface().ExitUser(userName, password)
if err == nil && ldapUser != nil {
global.TPLogger.Info("ldap用户已经存在数据库中,无需同步")
return ldapUser, nil
}
_ = l.Bind(viper.GetString("ldap.adminUser"), viper.GetString("ldap.password"))
searchRequest := ldap.NewSearchRequest(
viper.GetString("ldap.baseDN"),
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0,
0,
false,
fmt.Sprintf("(cn=%s)", userName),
[]string{"uid", "cn", "mail", "sn", "telephoneNumber"},
nil,
)
sr, err := l.Search(searchRequest)
if err != nil || len(sr.Entries) != 1 {
global.TPLogger.Error("用户不存在/用户名称重复:", err)
return nil, errors.New("用户不存在/用户名称重复")
}
userEntry := sr.Entries[0]
data := mod.User{
UID: userEntry.GetAttributeValue("uid"),
UserName: userEntry.GetAttributeValue("cn"),
Password: password,
Phone: userEntry.GetAttributeValue("telephoneNumber"),
Email: userEntry.GetAttributeValue("mail"),
NickName: userEntry.GetAttributeValue("sn"),
CreateBy: "ldap",
}
if err = dao.NewUserInterface().UserAdd(&data); err != nil {
global.TPLogger.Error("用户写入数据库失败:", err)
return nil, errors.New("登录失败")
}
ldapUser, err = dao.NewUserInterface().GetUserFromUserName(userName)
if err != nil {
global.TPLogger.Error("校验用户失败:", err)
return nil, errors.New("登录失败")
}
return ldapUser, nil
}
验证完毕后我们还需要将其插入到数据库中,从而获得用户的相关信息,例如用户的角色ID、userID等。
中间件优化
代码语言:javascript复制func loginFunc(c *gin.Context) (interface{}, error) {
var loginUser mod.User
if err := c.ShouldBind(&loginUser); err != nil {
return "", jwt.ErrMissingLoginValues
}
userName := loginUser.UserName
password := loginUser.Password
// 获取请求路径
path := strings.Split(c.Request.RequestURI, "?")[0]
if !strings.Contains(path, "ldap") {
data, err := system2.NewUserInfo().ExitUser(userName, password)
if err == nil {
loginUser.ID = data.ID
loginUser.RoleId = data.RoleId
return &loginUser, nil
}
} else {
data, err := system2.NewUserInfo().LdapLogin(userName, password)
if err == nil {
loginUser.ID = data.ID
loginUser.RoleId = data.RoleId
return &loginUser, nil
}
}
return nil, jwt.ErrFailedAuthentication
}
以上仅截取了核心代码,用来判断是否为ldap用户登录。
路由设置
代码语言:javascript复制func InitBaseRouters(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) gin.IRoutes {
{
r.POST("/login", authMiddleware.LoginHandler) // 登录
r.POST("/ldap/login", authMiddleware.LoginHandler) // ldap登录
r.GET("/login/info", system.LoginUserInfo, authMiddleware.LoginHandler) // 登录用户详情
r.POST("/logout", authMiddleware.LogoutHandler) // 退出
r.POST("/refresh", authMiddleware.RefreshHandler) // 刷新令牌
}
return r
}
这里我们添加了一个
ldap
用户登录的api
,主要是为了区分普通用户还是ldap用户的功能。
效果展示
普通用户登录(非LDAP用户)
image
LDAP用户
总结
到此我们LDAP也就结束了,大致流程就是连接ldap、用户账号密码认证、使用管理员(ldap)查询该用户的信息、存入数据库。有问题的小伙伴可以留言交流哦。