身份验证和授权插件
Neo4j 提供身份验证和授权插件接口,以支持原生用户或内置的基于配置的 LDAP 连接器无法涵盖的实际部署场景。
SPI(服务提供者接口)位于 com.neo4j.server.security.enterprise.auth.plugin.spi
包中。
自定义构建的插件可以访问 <neo4j-home> 目录,以便您从那里加载的任何自定义设置。插件还可以写入安全事件日志。
身份验证插件
身份验证插件使用 authenticate
方法实现 AuthenticationPlugin
接口。
以下示例显示了一个最小的身份验证插件,该插件检查 Neo4j 用户是否具有 Neo4j 密码。
@Override
public AuthenticationInfo authenticate( AuthToken authToken )
{
String principal = authToken.principal();
char[] credentials = authToken.credentials();
if ( principal.equals( "neo4j" ) && Arrays.equals( credentials, "neo4j".toCharArray() ) )
{
return (AuthenticationInfo) () -> "neo4j";
}
return null;
}
授权插件
授权插件使用 authorize
方法实现 AuthorizationPlugin
接口。
以下示例显示了一个最小的授权插件,它将读者角色分配给名为 neo4j
的用户。
@Override
public AuthorizationInfo authorize( Collection<PrincipalAndProvider> principals )
{
if ( principals.stream().anyMatch( p -> "neo4j".equals( p.principal() ) ) )
{
return (AuthorizationInfo) () -> Collections.singleton( PredefinedRoles.READER );
}
return null;
}
请注意 PredefinedRole
辅助类的用法。
简化的组合插件
还有一个简化的组合插件接口 AuthPlugin
,它在名为 authenticateAndAuthorize
的单个方法中提供身份验证和授权。
以下示例显示了一个组合插件,它验证 neo4j
/neo4j
凭据并返回读者角色授权。
@Override
public AuthInfo authenticateAndAuthorize( AuthToken authToken )
{
String principal = authToken.principal();
char[] credentials = authToken.credentials();
if ( principal.equals( "neo4j" ) && Arrays.equals( credentials, "neo4j".toCharArray() ) )
{
return AuthInfo.of( "neo4j", Collections.singleton( PredefinedRoles.READER ) );
}
return null;
}
可扩展平台
Neo4j 提供了一个可扩展平台,因为某些用户部署场景可能无法通过标准 LDAP 连接器轻松配置。一个已知的复杂性是与 LDAP 用户目录集成,其中组将用户作为成员,而不是相反。
以下示例首先搜索用户所属的组,然后通过调用自定义构建的 getNeo4jRoleForGroupId
方法将该组映射到 Neo4j 角色。
@Override
public AuthInfo authenticateAndAuthorize( AuthToken authToken ) throws AuthenticationException
{
try
{
String username = authToken.principal();
char[] password = authToken.credentials();
LdapContext ctx = authenticate( username, password );
Set<String> roles = authorize( ctx, username );
return AuthInfo.of( username, roles );
}
catch ( NamingException e )
{
throw new AuthenticationException( e.getMessage() );
}
}
private LdapContext authenticate( String username, char[] password ) throws NamingException
{
Hashtable<String,Object> env = new Hashtable<>();
env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
env.put( Context.PROVIDER_URL, "ldap://0.0.0.0:10389" );
env.put( Context.SECURITY_PRINCIPAL, String.format( "cn=%s,ou=users,dc=example,dc=com", username ) );
env.put( Context.SECURITY_CREDENTIALS, password );
return new InitialLdapContext( env, null );
}
private Set<String> authorize( LdapContext ctx, String username ) throws NamingException
{
Set<String> roleNames = new LinkedHashSet<>();
// Set up our search controls
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope( SearchControls.SUBTREE_SCOPE );
searchCtls.setReturningAttributes( new String[]{GROUP_ID} );
// Use a search argument to prevent potential code injection
Object[] searchArguments = new Object[]{username};
// Search for groups that has the user as a member
NamingEnumeration result = ctx.search( GROUP_SEARCH_BASE, GROUP_SEARCH_FILTER, searchArguments, searchCtls );
if ( result.hasMoreElements() )
{
SearchResult searchResult = (SearchResult) result.next();
Attributes attributes = searchResult.getAttributes();
if ( attributes != null )
{
NamingEnumeration attributeEnumeration = attributes.getAll();
while ( attributeEnumeration.hasMore() )
{
Attribute attribute = (Attribute) attributeEnumeration.next();
String attributeId = attribute.getID();
if ( attributeId.equalsIgnoreCase( GROUP_ID ) )
{
// Found a group that the user is a member of. See if it has a role mapped to it
String groupId = (String) attribute.get();
String neo4jGroup = getNeo4jRoleForGroupId( groupId );
if ( neo4jGroup != null )
{
// Yay! Add it to your set of roles
roleNames.add( neo4jGroup );
}
}
}
}
}
return roleNames;
}
有关更多信息和其他插件示例,请参见 https://github.com/neo4j/neo4j-example-auth-plugins。 |