Ldap:GoEasyAdmin用户统一登陆详细解析与实践

2024-02-03 15:25:51 浏览数 (1)

背景

本文讲解了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
}

验证完毕后我们还需要将其插入到数据库中,从而获得用户的相关信息,例如用户的角色IDuserID等。

中间件优化

代码语言: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)查询该用户的信息、存入数据库。有问题的小伙伴可以留言交流哦。

0 人点赞