Grails Spring Security LDAP登录:如何按子组限制用户访问?
按LDAP子组限制Grails应用用户登录的实现方案
Got it,既然你已经搞定了Grails 2.4.4 + spring-security-ldap的基础LDAP认证,现在要实现按子组限制登录,我给你梳理两种最实用的方案,都是完全不需要碰数据库的,符合你的硬性要求:
方案一:直接通过LDAP搜索过滤筛选合法用户(最推荐)
这种方式最直接高效——让LDAP服务器直接返回属于目标子组的用户,不需要在应用层额外处理。核心是修改用户搜索的过滤条件,加入memberOf(或递归组匹配规则)。
步骤1:修改Config.groovy的LDAP配置
在你已有的LDAP配置基础上,添加/调整以下内容:
// 基础LDAP连接配置(你应该已经有了) grails.plugin.springsecurity.ldap.context.managerDn = 'cn=admin,dc=yourdomain,dc=com' grails.plugin.springsecurity.ldap.context.managerPassword = 'your-admin-pass' grails.plugin.springsecurity.ldap.context.server = 'ldap://localhost:389' // 用户搜索配置:添加子组过滤条件 grails.plugin.springsecurity.ldap.search.base = 'dc=yourdomain,dc=com' // 关键:用&组合UID和子组成员条件,递归匹配嵌套组用1.2.840.113556.1.4.1941规则 grails.plugin.springsecurity.ldap.search.filter = '(& (uid={0}) (memberOf:1.2.840.113556.1.4.1941:=cn=allowed-login-users,ou=subgroups,ou=groups,dc=yourdomain,dc=com))' grails.plugin.springsecurity.ldap.search.searchSubtree = true // 禁用JDBC用户服务(因为不需要数据库) grails.plugin.springsecurity.userLookup.userDomainClassName = '' grails.plugin.springsecurity.userLookup.authorityJoinClassName = '' grails.plugin.springsecurity.authority.className = ''
关键说明:
memberOf:1.2.840.113556.1.4.1941:是LDAP的递归组匹配规则,如果你的子组是直接关联用户(没有嵌套子组),可以简化为memberOf=cn=allowed-login-users,...- 把
cn=allowed-login-users,ou=subgroups,ou=groups,dc=yourdomain,dc=com替换成你实际的子组DN
这样配置后,只有属于目标子组的用户才能通过LDAP搜索被找到,自然也就只有他们能完成认证登录。
方案二:自定义权限检查(更灵活的场景)
如果需要更复杂的逻辑(比如同时检查多个子组、动态调整允许的组),可以在认证后通过自定义逻辑过滤用户。
步骤1:自定义LDAP权限填充器
创建一个类继承DefaultLdapAuthoritiesPopulator,在里面检查用户是否属于目标子组:
import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator import org.springframework.ldap.core.DirContextOperations import org.springframework.security.core.userdetails.UsernameNotFoundException class CustomLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopulator { CustomLdapAuthoritiesPopulator(DirContextOperations userData) { super(userData) } @Override protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations userData, String username) { // 获取用户所属的所有组DN列表 def userGroups = userData.attributes.get('memberOf')?.getAll() as List ?: [] // 目标允许登录的子组DN def allowedGroupDn = 'cn=allowed-login-users,ou=subgroups,ou=groups,dc=yourdomain,dc=com' // 如果用户不在目标组,抛出异常阻止登录 if (!userGroups.contains(allowedGroupDn)) { throw new UsernameNotFoundException("User $username is not authorized to access this application") } // 返回用户的角色(可以根据组映射,这里默认给ROLE_USER) return [new SimpleGrantedAuthority('ROLE_USER')] as Set } }
步骤2:配置使用自定义填充器
在Config.groovy里指定这个自定义类:
grails.plugin.springsecurity.ldap.userDetails.contextMapper = 'com.yourpackage.CustomLdapAuthoritiesPopulator'
可选:通过认证成功处理器检查
如果你更喜欢在认证完成后再做检查,可以自定义AuthenticationSuccessHandler:
import org.springframework.security.core.Authentication import org.springframework.security.web.authentication.AuthenticationSuccessHandler import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse class CustomAuthSuccessHandler implements AuthenticationSuccessHandler { @Override void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { // 检查用户是否持有允许登录的角色(前提是你把组映射成了角色) def hasAllowedRole = authentication.authorities.any { it.authority == 'ROLE_ALLOWED_LOGIN_USERS' } if (!hasAllowedRole) { // 重定向到拒绝访问页面 response.sendRedirect(request.contextPath + '/accessDenied') return } // 正常跳转首页 response.sendRedirect(request.contextPath + '/') } }
然后在resources.groovy注册这个bean:
beans = { customAuthSuccessHandler(com.yourpackage.CustomAuthSuccessHandler) }
最后在Config.groovy配置使用:
grails.plugin.springsecurity.successHandler.ref = 'customAuthSuccessHandler' grails.plugin.springsecurity.successHandler.defaultTargetUrl = '/'
注意事项
- 确保你的OpenLDAP服务器开启了
memberOfoverlay,否则无法使用memberOf属性查询用户所属组 - 如果用递归组匹配,要确认LDAP服务器支持那个扩展规则(大部分主流LDAP服务器都支持)
- 所有配置中的DN和域名都要替换成你实际的LDAP结构
内容的提问来源于stack exchange,提问作者alchn
相关产品推荐
相关产品推荐

