身份验证和授权插件企业版
Neo4j 提供身份验证和授权插件接口,以支持原生用户或内置基于配置的 LDAP 连接器未覆盖的真实部署场景。
SPI(服务提供者接口)位于 com.neo4j.server.security.enterprise.auth.plugin.spi 包中。
定制插件可以访问 <neo4j-home> 目录,以便从该目录下的文件加载自定义设置。插件还可以写入安全事件日志。
身份验证插件
身份验证插件实现了 AuthenticationPlugin 接口的 authenticate 方法。
下面的示例展示了一个最小的身份验证插件,它检查 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;
}
授权插件
授权插件实现了 AuthorizationPlugin 接口的 authorize 方法。
下面的示例展示了一个最小的授权插件,它为名为 neo4j 的用户分配 reader 角色。
@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 凭证并返回 reader 角色的授权。
@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。 |