[z/OS]

配置联合存储库的定制系统授权工具 (SAF) 映射模块

可以配置系统授权工具 (SAF) 映射模块,以将带有 SAF 用户注册表适配器的联合存储库用于 SAF 授权。SAF 映射模块将联合存储库中的标识映射到 SAF 标识。

开始之前

由于联合存储库可以具有用户注册表链,例如,SAF、轻量级目录访问协议 (LDAP) 或基于文件的存储库,因此映射模块可清楚地识别必须与 SAF 授权配合使用的 SAF 标识。为了通过联合存储库对操作系统注册表或其他注册表(例如,LDAP)启用 SAF 授权,SAF 标识映射是必需的。此标识映射会设置 SAF 所需的其他凭证信息。

可以通过编写定制的登录映射模块来定制 Java™ 认证和授权 (JAAS) 登录配置。WebSphere® Application Server ltpaLoginModule 模块和 AuthenLoginModule 模块使用映射模块中提供的共享状态变量,为 SAF 授权设置相应的标识。

避免故障 避免故障:
  • 如果需要使用此映射模块,那么必须编译 SampleVMMSAFMappingModule。
  • 如果要使用 SAF 分布式标识映射功能,那么不必配置映射模块。
gotcha

关于此任务

在以下代码示例中,依赖 Java Platform, Enterprise Edition (Java EE) 标识的可用性来控制到 SAF 标识的映射。此代码使用共享状态中的 Constants.WSPRINCIPAL_KEY 值。此值由 WebSphere Application Server 登录模块放入代码中。可以将定制登录模块插入到 ltpaLoginModule 模块之后,且刚好位于 MapPlatformSubject 模块之前。如果添加定制登录模块,请使用回调数组或其他共享状态值来获取用于控制到 z/OS® 用户标识的映射的值。
//This program may be used, run, copied, modified and
//distributed without royalty for the purpose of developing,
//using, marketing, or distributing.

package com.ibm.websphere.wim;

import com.ibm.websphere.security.auth.WSPrincipal;
import com.ibm.websphere.security.cred.WSCredential;
import com.ibm.websphere.wim.util.PrincipalUtil;
import com.ibm.websphere.wim.exception.WIMException;
import com.ibm.websphere.wim.ras.WIMLogger;
import com.ibm.wsspi.security.auth.callback.Constants;
import com.ibm.wsspi.security.token.AttributeNameConstants;

import java.lang.reflect.Array;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;

import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

/**
 *
 * SampleVMMSAFMappingModule demonstrates a custom login module
 * that maps the existing WSPrincipal from the shared state to a z/OS
 * user id for a Federated Repository.
 *
 *
 *
 * The following values will be set into the shared state if authentication
 * succeeds.  If authentication fails, this login module will still indicate
 * success, but no values are set into the shared state.
 *
 *   AttributeNameConstants.ZOS_USERID
 *   AttributeNameConstants.ZOS_AUDIT_STRING
 *   AttributeNameConstants.CALLER_PRINCIPAL_CLASS
 *
 * This login module does not use any callbacks, nor does it modify the Subject
 * in any way.
 *
 * @author IBM Corporation
 * @version 1.0
 * @since 1.0
 */
public class SampleVMMSAFMappingModule implements LoginModule
{
    public final static String CLASSNAME = SampleVMMSAFMappingModule.class.getName();
    private static final Logger trcLogger = WIMLogger.getTraceLogger(CLASSNAME);
    boolean logEnabled = trcLogger.isLoggable(Level.FINER);

    /*
     * Constant that represents the name of this mapping module.  Whenever this sample
     * code is used to create a class with a different name, this value should be changed.
     *
     * By default, this value is used as part of the sample audit token, and for debugging
     * purposes.
     */
    private final static String MAPPING_MODULE_NAME = "com.ibm.websphere.wim.SampleVMMSAFMappingModule";

    /*
     * Constant that represents the maximum length of the ZOS_USERID.  Current MVS naming
     * restrictions limit this to eight characters.
     *
     * When the option to useWSPrincipalName is chosen, the name from the WSPrincipal is
     * shortened to this many characters.
     */
    private final static int MAXIMUM_NAME_LENGTH = 7;

    /*
     * Specifies whether or not to use this module's default mapping behavior, which is
     * to use the WSPrincipal name to generate the ZOS_USERID.  This depends on the
     * value of the "useWSPrincipalName" option passed in from the LoginContext.
     */
    private boolean useWSPrincipalName = true;

    /*
     * Specifies whether debugging is enabled for this Login Module.  This depends on the
     * value of the "debug" option passed in from the LoginContext.
     */
    private boolean debugEnabled = false;

    /*
     * Stores the Subject passed from the LoginContext.
     */
    private Subject subject;

    /*
     * Stores the CallbackHandler passed from the LoginContext.
     */
    private CallbackHandler callbackHandler;

    /*
     * Stores the shared state Map passed from the LoginContext.
     */
    private Map sharedState;

    javax.security.auth.Subject runas_subject, caller_subject;
    /*
     * Stores the options Map passed from the LoginContext.
     */
    private Map options;

    /*
     * This value is used to store the success or failure of the login() method so
     * that commit() and abort() can act differently in the two cases if so desired.
     */
    private boolean succeeded = false;


    /**
     * Construct an uninitialized mapping module object.
     */
    public SampleVMMSAFMappingModule()
    {
    }

    /**
     * Initialize this login module.
     *
     * This is called by the LoginContext after this login module is
     * instantiated. The relevant information is passed from the LoginContext
     * to this login module. If the login module does not understand any of the data
     * stored in the sharedState and options parameters,
     * they can be ignored.
     *
     * @param subject
     *                The subject that this LoginContext is authenticating
     * @param callbackHandler
     *                A CallbackHandler for communicating with the end user
     *                to gather login information (e.g., username and password).
     * @param sharedState
     *                The state shared with other configured login modules.
     * @param options
     *                The options specified in the login configuration for this particular login module.
     */
    public void initialize(Subject newSubject, CallbackHandler newCallbackHandler,
            Map newSharedState, Map newOptions)
    {
        // obtain the value for debug before anything else so that tracing can be used within
        // this method
        if (newOptions.containsKey("debug"))
        {
            String debugEnabledString = (String) newOptions.get("debug");
            if (debugEnabledString != null && debugEnabledString.toLowerCase().equals("true"))
            {
                debugEnabled = true;
            }
        }

        if (debugEnabled)
        {
            debug("initialize() entry");
        }

        // this login module is not going to use any of these objects except for the sharedState,
        // but for consistency with most login modules, we will save a reference to all of them
        this.subject = newSubject;
        this.callbackHandler = newCallbackHandler;
        this.sharedState = newSharedState;
        this.options = newOptions;

        if (options.containsKey("useWSPrincipalName"))
        {
            String useWSPrincipalNameString = (String) options.get("useWSPrincipalName");
            if (useWSPrincipalNameString != null && useWSPrincipalNameString.toLowerCase().equals("false"))
            {
                useWSPrincipalName = false;
            }
        }

        if (debugEnabled)
        {
            debug(new Object[] { "initialize() exit", subject, callbackHandler, sharedState, options });
        }
    }

    /**
     * Method to map the WSPrincipal to a ZOS_USERID
     *
     * This method derives a ZOS_USERID and stores it into the Shared State for use by a later
     * Login Module.
     *
     * @return true if the authentication succeeded, or false
     *         if this Login Module should be ignored
     * @exception LoginException
     *         if the authentication fails, which is impossible for this Login Module
     */
    public boolean login() throws LoginException
    {
        if (debugEnabled)
        {
            debug("login() entry");
        }

        if (sharedState.containsKey(AttributeNameConstants.ZOS_USERID))
        {
            // we don't want to override this value if, for whatever reason, another Login Module
            // has already placed it into the shared state, but we still consider this a success
            // because the exit criteria for this module has been met
            if (debugEnabled)
            {
                debug("ZOS_USERID already exists:  so no additional work is needed");
            }
            succeeded = true;
        }
        else if (!sharedState.containsKey(Constants.WSPRINCIPAL_KEY) ||
                !sharedState.containsKey(Constants.WSCREDENTIAL_KEY))
        {
            // if there isn't a Principal or Credential in the shared state, we can't do
            // anything so we'll return false to inform the LoginContext to ignore this module
            if (debugEnabled)
            {
                debug("Principal or Credential is unavailable:  skipping this Login Module");
            }
            succeeded = false;
        }
        else
        {
            if (debugEnabled)
            {
                debug("Principal and Credential are available:  continue with login");
            }

            String name = null;
            String audit = null;
            String principalClass = null;

            // extract the WSPrincipal and WSCredential from the shared state
            WSPrincipal principal = (WSPrincipal) sharedState.get(Constants.WSPRINCIPAL_KEY);
            WSCredential credential = (WSCredential) sharedState.get(Constants.WSCREDENTIAL_KEY);

            if (useWSPrincipalName)
            {
                // this sample mapping module provides a method to obtain the ZOS_USERID from the uniqueID obtained
                // from the WSPrincipal name if the property "useWSPrincipalName" is set to true
                if (debugEnabled)
                {
                    debug("Using name from WSPrincipal to obtain ZOS_USERID");
                }

                final String principalName=stripRealm(principal.getName());
                String realm = getRealm(credential);

                // for this example, a sample audit token will be created that combines the ZOS_USERID,
                // the realm, and the name of this mapping module
                //
                // a custom audit token can be created using any available data rather than using
                // this sample token

                // A Subject may contain more than one Principal.  This value specifies the
                // class of the Principal to be returned when the Subject is asked for the
                // Caller Principal.  If a custom Principal class has been placed into the
                // Subject, that class name can be specified here.
                //
                // Two predefined values for the Caller Principal Class are provided:
                //
                // - AttributeNameConstants.ZOS_CALLER_PRINCIPAL_CLASS
                //   the z/OS Principal class
                //
                // - AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS
                //   the default Principal class
                principalClass = AttributeNameConstants.DEFAULT_CALLER_PRINCIPAL_CLASS;

                //name=doMapUser(principalName);
                name=doCustomMapUser(principalName);

                succeeded = true;
                audit = realm + "/" + name + " MappingModule:" + MAPPING_MODULE_NAME;
                }

                else
                {
                    if (debugEnabled)
                    {
                        debug("Using Custom logic to obtain ZOS_USERID");
                    }
                    // if the behavior provided by this mapping module to obtain the ZOS_USERID from the
                    // WSPrincipal name is not enough, custom mapping logic can be provided here
                    //
                    // to use this custom mapping logic, the property "useWSPrincipalName" must be set
                    // to false

                    // name = ...custom logic
                    // audit = ...custom logic
                    // principalClass = ...custom logic

                    // by default, no custom mapping is provided, so the success of this code path
                    // is false; if custom mapping is provided, the following variable should be
                    // modified to represent the success or failure of the custom mapping
                    succeeded = false;
                }

                if (succeeded)
                {
                    // now that we have values for name, audit, and principalClass, we just need to set
                    // them into the shared state
                    sharedState.put(AttributeNameConstants.ZOS_USERID, name);
                    sharedState.put(AttributeNameConstants.ZOS_AUDIT_STRING, audit);
                    sharedState.put(AttributeNameConstants.CALLER_PRINCIPAL_CLASS, principalClass);
                    if (debugEnabled)
                    {
                        debug(new Object[] { "Values have been stored into the shared state", name, audit, principalClass });
                    }
                }
            }

            if (debugEnabled)
            {
                debug("login() exit");
            }
            return succeeded;
        }

        /**
         * Method to commit the authentication result.
         *
         * This Login Module does not need to commit any data, so we will simply return.
         *
         * @return true if the original login succeeded, or false
         *         if the original login failed
         * @exception LoginException
         *         if the commit fails, which cannot happen in this Login Module
         */
        public boolean commit() throws LoginException
        {
            if (debugEnabled)
            {
                debug("commit() entry");
            }

            // the return value of commit() is the same as the success of the original login
            boolean returnVal = succeeded;

            cleanup();

            if (debugEnabled)
            {
                debug("commit() exit");
            }
            return returnVal;
        }

        /**
         * Method to abort the authentication process (Phase 2).
         *
         * No matter whether our original login succeeded or failed, this method cleans up
         * our state and returns.
         *
         * @return true if the original login succeeded, or false
         *         if the original login failed
         * @exception LoginException
         *         if the abort fails, which cannot happen in this Login Module
         */
        public boolean abort() throws LoginException
        {
            if (debugEnabled)
            {
                debug("abort() entry");
            }

            // the return value of abort() is the same as the success of the original login
            boolean returnVal = succeeded;

            cleanup();

            if (debugEnabled)
            {
                debug("abort() exit");
            }
            return returnVal;
        }

        /**
         * Method which logs out a Subject.
         *
         * Since our commit method did not modify the Subject, we don't have anything to
         * logout or clean up and can just return true.
         *
         * @return true if the logout succeeded
         * @exception LoginException
         *         if the logout fails, which cannot happen in the Login Module
         */
        public boolean logout() throws LoginException
        {
            if (debugEnabled)
            {
                debug("logout() entry");
            }

            // our local variables were cleanup up during the commit, so no further cleanup is needed

            if (debugEnabled)
            {
                debug("logout() exit");
            }

            // since there is nothing to logout, we always succeed
            return true;
        }

        /*
         * Cleans up our local variables; the only cleanup required for
         * this Login Module is to set our success variable back to false.
         */
        private void cleanup()
        {
            if (debugEnabled)
            {
                debug("cleanup() entry");
            }

            // there's nothing to cleanup, really, so just reset our success variable
            succeeded = false;

            if (debugEnabled)
            {
                debug("cleanup() exit");
            }
        }

        /*
         * Private method to print trace information.  This implementation uses System.out
         * to print trace information to standard output, but a custom tracing system can
         * be implemented here as well.
         */
        private void debug(Object o)
        {
            System.out.println("Debug: " + MAPPING_MODULE_NAME);
            if (o != null)
            {
                if (o.getClass().isArray())
                {
                    int length = Array.getLength(o);
                    for (int i = 0; i < length; i++)
                    {
                        System.out.println("\t" + Array.get(o, i));
                    }
                }
                else
                {
                    System.out.println("\t" + o);
                }
            }
        }

        /*
         * Private method to obtain the realm name from the Credential and return it.  This
         * keeps the exception handling involved with obtaining the realm name out of the main
         * login() logic.
         */
        private String getRealm(WSCredential credential)
        {
            if (debugEnabled)
            {
                debug("getRealm() entry");
            }

            String realm = null;

            try
            {
                realm = credential.getRealmName();

                if (debugEnabled)
                {
                    debug("Got realm='" + realm + "' from credential");
                }
            }
            catch (Exception e)
            {
                // getRealmName throws CredentialExpiredException and CredentialDestroyedException
                if (debugEnabled)
                {
                    //debug(new Object[] { "Caught exception in getRealm: ", e });
                }
                realm = "UNKNOWN_REALM";
            }

            if (debugEnabled)
            {
                debug("getRealm() exit");
            }
            return realm;
        }

        /*
         * Private method to generate the ZOS_USERID from the WSPrincipal name/uniqueName.
         */
        private String createSAFIdentityName(String name)
        {
            if (debugEnabled)
            {
                debug("createSAFIdentityName() entry "+ name);
            }

            name=stripRealm(name).toUpperCase();
            //Get the CN.if uniqueName of format "uid=test,o=com" strip to "test".
            if (name.indexOf("=") >= 0)
                name = name.substring((name.indexOf("=")+1),name.length());

            if (name.indexOf(",") > 0)
                name = name.substring(0,name.indexOf(","));

            //handle case where realm appended :<realmname>\\userid
            if(name.indexOf("\\")>0){
            name = name.substring((name.indexOf("\\")+1),name.length());
            }

            // shorten the name if its length exceeds the defined maximum
            if (name.length() > MAXIMUM_NAME_LENGTH)
            {
                name = name.substring(0, MAXIMUM_NAME_LENGTH);

                if (debugEnabled)
                {
                    debug("WSPrincipal/uniqueName shortened to " + name);
                }
            }

            if (debugEnabled)
            {
                debug("createSAFIdentityName() exit");
            }
            return name; //return a SAF Idenity Name
        }
        /*
         * Private Helper method to Strip realm information if any.
         */
        private String stripRealm(String name){
            int index = name.indexOf("/") + 1; // index of the first character after the first /
            if (index >= name.length())
            {
                // this block handles the case where the first / is the last character in the String,
                // it really shouldn't happen, but if it does we can just strip it off
                name = name.substring(0, index - 1);

                if (debugEnabled)
                {
                    debug("Stripping trailing / from WSPrincipal name");
                }
            }
            else
            {
                name = name.substring(index);
            }

            return name;

        }


        /**
         * Private helper method to deduce the z/OS ID to be mapped.
         * You can use this when you want to map RACF users & other repository users in specific ways.
         * This uses a Federated Repository util method, isRACFUser to identify if the user is from RACF registry.
         */
        private String  doCustomMapUser(String principalName){
            boolean isRACFUser=true;
            String  mapID=null;
            try{
            isRACFUser=PrincipalUtil.isRACFUser(principalName);
            }
            catch(WIMException we){
               debug("Exception happened while checking isRACFuser"+we);
               //Handle it as per need.
            }
            mapID=isRACFUser==true?createSAFIdentityName(principalName):doMapUser(principalName);

            return mapID;
        }

        /**
         * Private helper method to deduce the z/OS ID to be mapped.
         * You can use this directly if you want to map z/OS ID irrespective of RACF/Non-RACF users.
         *
         */
        private String  doMapUser(String principalName){
            //If loginUser belongs to other registry like LDAP make sure an MVS user exists to match the derived name.
            //Or just map to any existing RACF user as in the commented line below.
            // name="USER237";
            //if user exists in RACF then Map that user.
            String uniqueName=null;
            String mapID=null;
            try {
                javax.naming.InitialContext ctx = new javax.naming.InitialContext();
                // Retrieves the local UserRegistry object.
                final com.ibm.websphere.security.UserRegistry reg = (com.ibm.websphere.security.UserRegistry) ctx.lookup("UserRegistry");
                // Retrieves the registry uniqueName based on the principalName
                uniqueName = reg.getUniqueUserId(principalName);
            } catch ( Exception e ) {
                debug("Exception thrown while getting uniqueID: "+e);
            }
            mapID=createSAFIdentityName(uniqueName);

            return mapID;
        }
    }
样本映射模块在联合存储库的用户注册表标识和 z/OS 标识之间创建映射。此用户注册表标识用作 z/OS 用户标识。如果需要不同的映射,那么可以定制此模块(如作为先前代码样本一部分的 else 子句中所示)以执行映射。
注: 联合存储库实用程序方法(称为 PrincipalUtil.isRACFUser)用于标识用户是否来自 RACF® 注册表。如果用户是 RACF 用户,那么此方法返回 true。仅当在联合存储库下配置当前用户注册表时,才使用此方法。

过程

  1. 编译 Java 代码。
    1. 确保代码是可信的,且以处理授权程序工具 (APF) 授权的模块的相同认真态度进行处理。 从 z/OS 控制器访问缺省 Java 授权和认证服务 (JAAS) 系统登录配置。
    2. 确保您的类路径设置包含正确的 Java 归档 (JAR) 文件: 可以使用下列其中一组 JAR 文件:
      • WebSphere_Application_Client/plugins/org.eclipse.emf.commonj.sdo.jarWebSphere_Application_Client/plugins/com.ibm.ws.emf.jar 以及 WebSphere_Application_Client/plugins/com.ibm.ws.runtime.client.jar
      • WAS_HOME/plugins/org.eclipse.emf.commonj.sdo.jarWAS_HOME/plugins/com.ibm.ws.runtime.wim.base.jar 以及 WAS_HOME/plugins/com.ibm.ws.runtime.jar
  2. 将此类安装到 WebSphere Application Server 和 Deployment Manager 的 classes 目录(如果您指定的映射类不是 IBM® 所提供的缺省类)。
  3. 将 JAR 文件放入单元中每个节点(其中包括 WebSphere Application Server Network Deployment 单元中的 Deployment Manager 节点)的 WAS_HOME/classes 目录中。
  4. 重新启动 WebSphere Application Server 以使更改生效。
  5. 通过对此模块指定定制属性来对 SampleSAFMappingModule 启用日志记录:
    1. 在 WebSphere Application Server 管理控制台中,单击安全性 > 全局安全性
    2. 在“认证”下,展开“Java 认证和授权服务”。
    3. 单击系统登录
    4. 单击 login_module_name
    5. 在“JAAS 登录模块”下,单击 com.ibm.websphere.wim.SampleVMMSAFMappingModule
    6. 在“定制属性”下,单击新建
    7. 在“名称”字段中,输入 debug,在“值”字段中,输入 true
    8. 单击应用,然后单击保存

指示主题类型的图标 任务主题



时间戳记图标 最近一次更新时间: last_date
http://www14.software.ibm.com/webapp/wsbroker/redirect?version=cord&product=was-nd-mp&topic=tsec_customsaffedrepos
文件名:tsec_customsaffedrepos.html