Karaf开发手册(2)

 由  snoopy7713 发布

安全框架

Karaf通过允许JAAS工作在OSGi环境中增强支持JAAS。该框架通过在运行时智能的部署新的密钥库或信任库来增加一个OSGi密钥库的管理功能。

简介

该功能允许部署在JAAS运行时,JAAS基本配置在应用程序的各个部分发挥作用。包括通过默认的虚拟登录模型的配置应用Karaf域远程登录控制。这些域页可用于NMR,JBI组件或JMX服务器认证用户登录或发送消息给总线。

在JAAS域中,你也可部署密钥库和可信任库保证远程shell控制台的安全,设置HTTPS连接器或为WS-Security使用证书。

下面是定义的一个简单的spring 的XML schema ,允许部署一个新的域或一个新的密钥库

Schema

覆盖或部署一个新的域,你可使用下面的被spring 命名空间句柄所支持的XSD,我们可以这样定义一个spring xml配置文件。

当定义Karaf 域是使用下面的XML Schema

<?xml version="1.0" encoding="UTF-8"?>
<!--

    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    this work for additional information regarding copyright ownership.
    The ASF licenses this file to You under the Apache License, Version 2.0
    (the "License"); you may not use this file except in compliance with
    the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<xs:schema elementFormDefault='qualified'
           targetNamespace='http://karaf.apache.org/xmlns/jaas/v1.1.0'
           xmlns:xs='http://www.w3.org/2001/XMLSchema'
           xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:tns='http://karaf.apache.org/xmlns/jaas/v1.1.0'>

    <xs:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0"/>

    <xs:element name="config">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="module" minOccurs="0" maxOccurs="unbounded">
                    <xs:complexType mixed="true">
                        <xs:attribute name="name" use="optional" type="xs:string"/>
                        <xs:attribute name="className" use="required" type="xs:string"/>
                        <xs:attribute name="flags" default="required">
                            <xs:simpleType>
                                <xs:restriction base="xs:NMTOKEN">
                                    <xs:enumeration value="required"/>
                                    <xs:enumeration value="requisite"/>
                                    <xs:enumeration value="sufficient"/>
                                    <xs:enumeration value="optional"/>
                                </xs:restriction>
                            </xs:simpleType>
                        </xs:attribute>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
            <xs:attribute name="name" use="required" type="xs:string"/>
            <xs:attribute name="rank" use="optional" default="0" type="xs:int"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="keystore">
        <xs:complexType>
            <xs:attribute name="name" use="required" type="xs:string"/>
            <xs:attribute name="rank" use="optional" default="0" type="xs:int"/>
            <xs:attribute name="path" use="required" type="xs:string"/>
            <xs:attribute name="keystorePassword" use="optional" type="xs:string"/>
            <xs:attribute name="keyPasswords" use="optional" type="xs:string"/>
        </xs:complexType>
    </xs:element>

</xs:schema>

你可在下面的位置找到该schema 这是两个使用schema的例子:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:jaas="http://karaf.apache.org/xmlns/jaas/v1.0.0"
           xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">

    <!-- Bean to allow the $[karaf.base] property to be correctly resolved -->
    <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]"/>

    <jaas:config name="myrealm">
        <jaas:module className="org.apache.karaf.jaas.modules.properties.PropertiesLoginModule" 
                     flags="required">
            users = $[karaf.base]/etc/users.properties
        </jaas:module>
    </jaas:config>

</blueprint>


<jaas:keystore xmlns:jaas="http://karaf.apache.org/xmlns/jaas/v1.1.0"
               name="ks"
               rank="1"
               path="classpath:privatestore.jks"
               keystorePassword="keyStorePassword"
               keyPasswords="myalias=myAliasPassword">
</jaas:keystore>

这里的id属性是该bean的blueprint id,当属性名称未指定时作为域的默认名称。另外属性在配置文件中的元素是一个等级即:是一个整数。当LoginContext找到一个给定用户的验证域时,该验证域在OSGi注册表中匹配为required Name,若发现不止一个验证域,则使用等级最高的。这样可以使用新的值覆盖一些域。最后一个发布的设置成false的属性不被发布在OSGi注册表的域中,该域被禁用。

每一个域都可包括一个或更多的定义模型,每个一个模型标识一个LoginModule并且 类名属性必须被设置成用户的登录模型类名。注意:这个bundle 的类加载器的登录模型必须可用。因此要么在bundle中定义了自身,要么所需的包中进行了正确的导入。块属性必须取四个中的一个,在JAAS document中有详细解释。

模型中的内容被解析成一个properties 文件并且将被用于login 模型的进一步配置。

部署这样的代码将在OSGi注册表中产生JaasRealm对象,该对象将在使用JAAS登录模型时调用。

配置覆盖和使用属性等级

属性等级配置元素被绑定在了底层OSGi 服务层。当JAAS framework执行一个验证时,它将使用域名去寻找匹配的JAAS 配置。如果使用了多个配置,则使用最高等级的属性。

所以,如果你在Karaf中覆盖默认的安全配置,你需要部署一个name=”karaf” rank=”1” 的JAAS配置。

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:jaas="http://karaf.apache.org/xmlns/jaas/v1.1.0"
           xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">

    <!-- Bean to allow the $[karaf.base] property to be correctly resolved -->
    <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]"/>

    <jaas:config name="karaf" rank="1">
        <jaas:module className="org.apache.karaf.jaas.modules.properties.PropertiesLoginModule"
                     flags="required">
            users = $[karaf.base]/etc/users.properties
            ...
        </jaas:module>
    </jaas:config>

</blueprint>

架构

由于在JAAS规范中的约束,一个类必须对于所有的bundle是可用的。该类叫做ProxyLoginModle,作为OSGi定义的LoginModule中的一个代理是一个LoginModule。如果你想整合这个功能到另一个OSGi运行时,这个类对于系统类加载器和引导委托类路径部分的相关包必须是可用的。

Xml schemoa定义在允许使用的一个简单xml配置(利用spring xml的可扩展性)之上,为给定的域注册一个JAAS配置。这个配置作为一个JaasRealm将用于OSGi注册表,OSGi指定的配置将查找这样的服务。然后代理login module 将能使用域提供的信息去实际加载bundle 包含的真正的login module类。

可用的域

来自多个login modules的Karaf可被整合到你的环境中。

PropertiesLoginModule

这个login module 是默认的一个配置。他使用一个properties 文本文件去加载users,passwords,roles.

Name Description
users location of the properties file

该文件使用的properties文件格式. Properties文件格式允许这样,每行定义一个user,password和相关roles

<jaas:config name="karaf">
    <jaas:module className="org.apache.karaf.jaas.modules.properties.PropertiesLoginModule" 
                 flags="required">
        users = $[karaf.base]/etc/users.properties
    </jaas:module>
</jaas:config>

OsgiConfigLoginModule

OsgiConfigLoginModule使用OSGi的ConfigurationAdmin 服务提供users,passwords 和roles。

Name Description
pid the PID of the configuration containing user definitions

和PropertiesLoginModule的配置格式一样。

JDBCLoginModule

JDBCLoginModule利用一个数据库加载users ,passwords和roles,JDBCLoginModule提供了一个数据源(normal 或 XA),该数据源和查询password和role检索是可配置的,使用下面的参数

Name Description
datasource The datasource as on OSGi ldap filter or as JDNI name
query.password The SQL query that retries the password of the user
query.role The SQL query that retries the roles of the user
传递一个数据源作为一个OSGi LDAP 过滤器

为使用OSGi LDAP 过滤器,必须提供osgi前缀,如下面的例子:

<jaas:config name="karaf">
    <jaas:module className="org.apache.karaf.jaas.modules.jdbc.JDBCLoginModule" 
                 flags="required">
        datasource = osgi:javax.sql.DataSource/(osgi.jndi.service.name=jdbc/karafdb)
        query.password = SELECT PASSWORD FROM USERS WHERE USERNAME=?
        query.role = SELECT ROLE FROM ROLES WHERE USERNAME=?
    </jaas:module>
</jaas:config>
传递一个数据源作为一个JNDI name

为使用JNDI name 必须提供jndi前缀。如下面的例子,假设利用aries jndi通过JNDI暴露服务

<jaas:config name="karaf">
    <jaas:module className="org.apache.karaf.jaas.modules.jdbc.JDBCLoginModule" 
                 flags="required">
        datasource = jndi:aries:services/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/karafdb)
        query.password = SELECT PASSWORD FROM USERS WHERE USERNAME=?
        query.role = SELECT ROLE FROM ROLES WHERE USERNAME=?
    </jaas:module>
</jaas:config>

LDAPLoginModule

LDAPLoginModule 利用LDAP加载users和roles,绑定users到LDAP检查passwords。

LDAPLoginModule支持一下参数:

Name Description
connection.url The LDAP connection URL, e.g. ldap://hostname
connection.username Admin username to connect to the LDAP. This parameter is optional, if it's not provided, the LDAP connection will be anonymous.
connection.password Admin password to connect to the LDAP. Only used if the connection.username is specified.
user.base.dn The LDAP base DN used to looking for user, e.g. ou=user,dc=apache,dc=org
user.filter The LDAP filter used to looking for user, e.g. (uid=%u) where %u will be replaced by the username.
user.search.subtree If "true", the user lookup will be recursive (SUBTREE). If "false", the user lookup will be performed only at the first level (ONELEVEL).
role.base.dn The LDAP base DN used to looking for roles, e.g. ou=role,dc=apache,dc=org
role.filterThe LDAP filter used to looking for user's role, e.g. (member:=uid=%u)
role.name.attribute The LDAP role attribute containing the role string used by Karaf, e.g. cn
role.search.subtree If "true", the role lookup will be recursive (SUBTREE). If "false", the role lookup will be performed only at the first level (ONELEVEL).
authentication Define the authentication backend used on the LDAP server. The default is simple.
initial.context.factory Define the initial context factory used to connect to the LDAP server. The default is com.sun.jndi.ldap.LdapCtxFactory
ssl If "true" or if the protocol on the connection.url is ldaps, an SSL connection will be used
ssl.provider The provider name to use for SSL
ssl.protocol The protocol name to use for SSL (SSL for example)
ssl.algorithm The algorithm to use for the KeyManagerFactory and TrustManagerFactory (PKIX for example)
ssl.keystoreThe key store name to use for SSL. The key store must be deployed using a jaas:keystore configuration.
ssl.keyalias The key alias to use for SSL
ssl.truststore The trust store name to use for SSL. The trust store must be deployed using a jaas:keystore configuration.

LDAPLoginModule的使用例如:

<jaas:config name="karaf">
  <jaas:module className="org.apache.karaf.jaas.modules.ldap.LDAPLoginModule" flags="required">
        connection.url = ldap://localhost:389
        user.base.dn = ou=user,dc=apache,dc=org
        user.filter = (cn=%u)
        user.search.subtree = true
        role.base.dn = ou=group,dc=apache,dc=org
        role.filter = (member:=uid=%u)
        role.name.attribute = cn
        role.search.subtree = true
        authentication = simple
  </jaas:module>
</jaas:config>

如果你希望使用一个SSL connection,可使用下面的配置实例:

<ext:property-placeholder />

<jaas:config name="karaf" rank="1">
    <jaas:module className="org.apache.karaf.jaas.modules.ldap.LDAPLoginModule" flags="required">
        connection.url = ldaps://localhost:10636
        user.base.dn = ou=users,ou=system
        user.filter = (uid=%u)
        user.search.subtree = true
        role.base.dn = ou=groups,ou=system
        role.filter = (uniqueMember=uid=%u)
        role.name.attribute = cn
        role.search.subtree = true
        authentication = simple
        ssl.protocol=SSL
        ssl.truststore=ks
        ssl.algorithm=PKIX
    </jaas:module>
</jaas:config>

<jaas:keystore name="ks"
               path="file:///${karaf.home}/etc/trusted.ks"
               keystorePassword="secret" />

Encryption service(加密服务)

EcryptionService是注册在OSGi注册表中的服务,提供加密和检查加密的passwords。该服务以工厂形式为加密对象提供加密服务。

该服务在所有的Karaf 的登录模型中用于支持对passwords的加密

配置属性

每个登录模型支持下面的额外的属性设置

Name Description
encryption.name Name of the encryption service registered in OSGi (cf. paragraph below)
encryption.enabled Boolean used to turn on encryption
encryption.prefix Prefix for encrypted passwords
encryption.suffix Suffix for encrypted passwords
encryption.algorithm Name of an algorithm to be used for hashing, like "MD5" or "SHA-1"
encryption.encoding Encrypted passwords encoding (can be hexadecimal or base64)
role.policy A policy for identifying roles (can be prefix or group) below)
role.discriminator A discriminator value to be used by the role policy

下面是一个例子:

<jaas:config name="karaf">
    <jaas:module className="org.apache.karaf.jaas.modules.properties.PropertiesLoginModule" 
                 flags="required">
        users = $[karaf.base]/etc/users.properties
        encryption.enabled = true
        encryption.algorithm = MD5
        encryption.encoding = hexadecimal
    </jaas:module>
</jaas:config>

前缀和后缀

登录模型同时支持加密和纯密码。有时候一些登录模型可以加密运行中的passwords并返回一个加密形式

Jasypt

Karaf默认提供一个简单的加密服务,可满足一般的简单需要。然而,有时候,你可能想安装Jasypt库获得更强的加密算法和更多的控制。

安装Jasypt是最简单的获取更多功能的方法。

karaf@root> features:install jasypt-encryption

这样将下载并安装所需的bundles,页会在OSGi注册表中为Jasypt注册一个EncryptionService。

当为使用Jasypt而注册了一个login模型,你需要指定一个加密名称属性并设置一个jasypt值以便能够使用Jasypt加密服务。

另外,对于以上标准的属性,Jasypt服务还提供了如下的参数:

Name Description
providerName Name of the java.security.Provider name to use for obtaining the digest algorithm
providerClassName Class name for the security provider to be used for obtaining the digest algorithm
iterations Number of times the hash function will be applied recursively
saltSizeBytes Size of the salt to be used to compute the digest
saltGeneratorClassName Class name of the salt generator

使用Jasypt服务典型的域配置就像下面:

<jaas:config name="karaf">
    <jaas:module className="org.apache.karaf.jaas.modules.properties.PropertiesLoginModule" 
                 flags="required">
        users = $[karaf.base]/etc/users.properties
        encryption.enabled = true
        encryption.name = jasypt
        encryption.algorithm = SHA-256
        encryption.encoding = base64
        encryption.iterations = 100000
        encryption.saltSizeBytes = 16
    </jaas:module>
</jaas:config>

角色发现策略

JAAS 规范没有提供区分主要用户(User)和角色(Role),规范中没有指定类。为了提供这种区分,意味着应用开发把应用和已经创建的JAAS实现角色策略剥离

角色策略是一个通过应用区分角色,不依赖实现的公约 ,每一个角色策略可在login模型配置中通过设置一个“role.policy”和“role.discriminator”属性。当前,Karaf提供了两个策略用于所有Karaf的Login模型。

  • Prefixed Roles(前缀角色)
  • Grouped Roles(分组角色)

前缀角色

当在login 模型运用了一个角色的前缀配置,应用可通过前缀识别主要的角色,如:

<jaas:config name="karaf">
    <jaas:module className="org.apache.karaf.jaas.modules.properties.PropertiesLoginModule" 
                 flags="required">
        users = $[karaf.base]/etc/users.properties
        role.policy = prefix
        role.discriminator = ROLE_
    </jaas:module>
</jaas:config>

应用可使用下面的这种片段识别主要的角色。

LoginContext ctx = new LoginContext("karaf", handler);
ctx.login();
authenticated = true;
subject = ctx.getSubject();
for (Principal p : subject.getPrincipals()) {
    if (p.getName().startsWith("ROLE_")) {
        roles.add((p.getName().substring("ROLE_".length())));
    }
}

分组角色

当使用了分组策略,通过配置名(属性role.discriminator),login模型提供的所有角色将成为组成员。如:

<jaas:config name="karaf">
    <jaas:module className="org.apache.karaf.jaas.modules.properties.PropertiesLoginModule" 
                 flags="required">
        users = $[karaf.base]/etc/users.properties
        role.policy = group
        role.discriminator = ROLES
    </jaas:module>
</jaas:config>

LoginContext ctx = new LoginContext("karaf", handler);
ctx.login();
authenticated = true;
subject = ctx.getSubject();
for (Principal p : subject.getPrincipals()) {
    if ((p instanceof Group) && ("ROLES".equalsIgnoreCase(p.getName()))) {
        Group g = (Group) p;
        Enumeration<? extends Principal> members = g.members();
        while (members.hasMoreElements()) {
            Principal member = members.nextElement();
            roles.add(member.getName());
        }
    }
}

使用features-maven-plugin

Features-maven-plugin 提供了帮助你建立和验证XML描述符提供的功能的多种目标,就像利用你自己的功能创建一个定制Karaf描述符一样。

Goal Description
features:add-features-to-repo Copies all the bundles required for a given set of features into a directory (e.g. for creating your own Karaf-based distribution)
features:generate-features-file Deprecated - use features:generate-features-xml instead
features:generate-features-xml Generates a features XML descriptor for a set of bundles
features:validate Validate a features XML descriptor by checking if all the required imports can be matched to exports

配置 feature-maven-plugin

为了使用feature-maven-plugin,你必须在工程文件pom.xml中定义插件

<project>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.karaf.tooling</groupId>
        <artifactId>features-maven-plugin</artifactId>
        <version>2.2.2</version>

        <executions>
          <!-- add execution definitions here -->
        </executions>
      </plugin>
    </plugins>
  </build>  
</project>
查看评论