WebLogic 的 JAAS 配置

JAAS configuration for WebLogic

我已启动 Web 应用程序并 运行正在 tomcat 中。现在,我必须 运行 我的应用程序(war 文件)在 WebLogic 12c 中。

我找不到足够的信息:

1) 在 war 文件中 weblogic.xml 和 web.xml 是强制性的吗?教程在 weblogic.xml 中提到了有关设置上下文和更多配置的内容,但我没有在哪里可以弄清楚它是强制性的。

2) 我需要在 WebLogic 中配置 JAAS 领域。我尝试配置领域,但不知道配置有问题。有人可以指点我正确的教程或提供 JAAS 设置所需的步骤。

我在 startWebLogic.cmd 文件中添加了 -Djava.security.auth.login.config=%DOMAIN_HOME%\jaas.config

下面是我的登录模块代码:

public class AuthLoginModule implements LoginModule {
    private static Logger logger = Logger.getLogger(AuthLoginModule.class);
    // initial state
    private Subject subject;
    private CallbackHandler callbackHandler;
    private Map<String, ?> sharedState;
    private Map<String, ?> options;
    // the authentication status
    private boolean succeeded = false;
    private boolean commitSucceeded = false;

    // username and password
    private String username;
    private String password;
    Map<String,String> userData = new HashMap<String,String>();

    private AuthPrincipal userPrincipal;

    public AuthLoginModule() throws WebAuthServiceException {
        super();
    }

    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
        this.subject = subject;
        this.callbackHandler = callbackHandler;
        this.setSharedState(sharedState);
        this.setOptions(options);
        String appName = options.get(WebAuthConstants.APP_UNIQUE_NAME).toString();
        logger.info("AppName in AuthLoginModule: " + appName);
    }

    public boolean login() throws LoginException {
        if (callbackHandler == null)
            throw new LoginException("Error: no CallbackHandler available " + "to garner authentication information from the user");

        Callback[] callbacks = new Callback[2];
        callbacks[0] = new NameCallback("user name: ");
        callbacks[1] = new PasswordCallback("password: ", false);
        try {
            callbackHandler.handle(callbacks);
            username = ((NameCallback) callbacks[0]).getName();
            char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
            if (tmpPassword == null) {
                // treat a NULL password as an empty password
                tmpPassword = new char[0];
            }
            password = new String(tmpPassword);
            if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
                throw new LoginException("User name or password is empty");
            }

        } catch (java.io.IOException ioe) {
            throw new LoginException(ioe.toString());
        } catch (UnsupportedCallbackException uce) {
            throw new LoginException("Error: " + uce.getCallback().toString() + " not available to garner authentication information " + "from the user");
        }
        String validateUserCredData = validateUserCred();
        if (validateUserCredData!=null) {
            if(JsonUtil.jsonFromString(validateUserCredData).get("statusCode").getAsInt()== HttpStatus.SC_UNAUTHORIZED) {
                userData.put(DataConstants._USER_PWD_STATUS, DataConstants._RESET_USER_PWD);
            }
            succeeded = true;
        } else {
            succeeded = false;
        }
        return succeeded;
    }


    private String validateUserCred() {
        try {
            logger.info("Started validating user credentials for: " + username);
            // If there is no error then user allowed to access all
            UserClientService UserClientService = ClientServiceFactory.getInstance().getUserService();
            return UserClientService.validateUserCredentials(username, password);
        } catch (Throwable e) {
            logger.error("Exception while authentication user against Service API, Error Code: ", e);
        }
        return null;
    }

    public boolean commit() throws LoginException {
        if (succeeded == false) {
            return false;
        } else {
            // add a Principal (authenticated identity) to the Subject
            // assume the user we authenticated is the SamplePrincipal
            userPrincipal = new AuthPrincipal(username, password, userData);
            if (!subject.getPrincipals().contains(userPrincipal))
                subject.getPrincipals().add(userPrincipal);
            logger.info("Login Module successfully added user principal");
            // in any case, clean out state
            username = null;
            password = null;
            commitSucceeded = true;
            return true;
        }
    }

    public boolean abort() throws LoginException {
        if (succeeded == false) {
            return false;
        } else if (succeeded == true && commitSucceeded == false) {
            // login succeeded but overall authentication failed
            succeeded = false;
            username = null;
            password = null;
            userPrincipal = null;
        } else {
            // overall authentication succeeded and commit succeeded,
            // but someone else's commit failed
            logout();
        }
        return true;
    }

    public boolean logout() throws LoginException {
        subject.getPrincipals().remove(userPrincipal);
        succeeded = false;
        succeeded = commitSucceeded;
        username = null;
        password = null;
        userPrincipal = null;
        logger.info("Login Module successfully removed user principal after successful logout");
        return true;
    }

    public Map<String, ?> getSharedState() {
        return sharedState;
    }

    public void setSharedState(Map<String, ?> sharedState) {
        this.sharedState = sharedState;
    }

    public Map<String, ?> getOptions() {
        return options;
    }

    public void setOptions(Map<String, ?> options) {
        this.options = options;
    }
}

在一些教程中,我看到了专门针对 WebLogic 编写的 LoginModule,但我觉得 LoginModule 不应该针对任何服务器进行更改,因为它遵循 J2EE。

如有任何帮助,我们将不胜感激。

最后,我根据需要进行了基于表单的 JAAS 配置。 我在评论部分和一些博客中关注了 KC Wong 提供的 oracle 文档 link。

以下是我遵循的步骤。

1) 首先我需要创建一个 自定义身份验证提供程序 。我为此创建了一个 Java 项目,其中包括我的 LoginModule、AuthenticationProviderImpl(实现 AuthenticationProviderV2)和 MBean XML。 此 MBean XML 包含有关 MBeanType 和 MBeanAttribute 的信息。 我在下面发布了一些重要文件以获取更多信息。

WLAuthenticationProviderImpl .java

package com.abc.wls.security.providers.authentication;

import java.util.HashMap;

import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;

import weblogic.management.security.ProviderMBean;
import weblogic.security.provider.PrincipalValidatorImpl;
import weblogic.security.spi.AuthenticationProviderV2;
import weblogic.security.spi.IdentityAsserterV2;
import weblogic.security.spi.PrincipalValidator;
import weblogic.security.spi.SecurityServices;

public class WLAuthenticationProviderImpl implements AuthenticationProviderV2 {
    private String description = "MyOwn WLAuthentication Provider";
    // private SimpleSampleAuthenticatorDatabase database;
    private LoginModuleControlFlag controlFlag;

    public void initialize(ProviderMBean mbean, SecurityServices services) {
        System.out.println("WLAuthenticationProviderImpl.initialize");
        // WLAuthenticationProviderMBean mbean =
        // (WLAuthenticationProviderMBean) mbean;

        // SimpleSampleAuthenticatorMBean myMBean =
        // (SimpleSampleAuthenticatorMBean) mbean;
        // description = myMBean.getDescription() + "\n" + myMBean.getVersion();
        // database = new SimpleSampleAuthenticatorDatabase(myMBean);
        // String flag = myMBean.getControlFlag();
        /*
         * if (flag.equalsIgnoreCase("REQUIRED")) { controlFlag =
         * LoginModuleControlFlag.REQUIRED; } else if
         * (flag.equalsIgnoreCase("OPTIONAL")) { controlFlag =
         * LoginModuleControlFlag.OPTIONAL; } else if
         * (flag.equalsIgnoreCase("REQUISITE")) { controlFlag =
         * LoginModuleControlFlag.REQUISITE; } else if
         * (flag.equalsIgnoreCase("SUFFICIENT")) { controlFlag =
         * LoginModuleControlFlag.SUFFICIENT; } else { throw new
         * IllegalArgumentException("invalid flag value" + flag); }
         */
    }

    public String getDescription() {
        System.out.println("WLAuthenticationProviderImpl.getDescription");
        return description;
    }

    public void shutdown() {
        System.out.println("WLSecurityProviderImpl.shutdown");
    }

    private AppConfigurationEntry getConfiguration(HashMap options) {
        System.out.println("WLAuthenticationProviderImpl.getConfiguration");
        if (options == null)
            options = new HashMap<>();
        options.put("app-unique-name", "xyz-ui");
        // return new
        // AppConfigurationEntry("examples.security.providers.authentication.Simple.Simple.SampleLoginModuleImpl",
        // controlFlag, options);
        return new AppConfigurationEntry("com.abc.wls.security.providers.authentication.WLServerLoginModule", LoginModuleControlFlag.REQUIRED, options);
    }

    public AppConfigurationEntry getLoginModuleConfiguration() {
        System.out.println("WLAuthenticationProviderImpl.getLoginModuleConfiguration");
        HashMap options = new HashMap();
        return getConfiguration(options);
    }

    public AppConfigurationEntry getAssertionModuleConfiguration() {
        System.out.println("WLAuthenticationProviderImpl.getAssertionModuleConfiguration");
        HashMap options = new HashMap();
        options.put("IdentityAssertion", "true");
        return getConfiguration(options);
    }

    public PrincipalValidator getPrincipalValidator() {
        return new PrincipalValidatorImpl();
    }

    public IdentityAsserterV2 getIdentityAsserter() {
        return null;
    }

}

WLServerLoginModule.java

package com.abc.wls.security.providers.authentication;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import weblogic.logging.NonCatalogLogger;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class WLServerLoginModule implements LoginModule {
    private static NonCatalogLogger logger = new NonCatalogLogger("WLServerLoginModule");
    // initial state
    private Subject subject;
    private CallbackHandler callbackHandler;
    private Map<String, ?> sharedState;
    private Map<String, ?> options;
    // the authentication status
    private boolean succeeded = false;
    private boolean commitSucceeded = false;

    // username and password
    private String username;
    private String password;
    Map<String, String> userData = new HashMap<String, String>();
    private final JsonParser jsonParser = new JsonParser();

    private WLSAuthPrincipal userPrincipal;

    public WLServerLoginModule() throws LoginException {
        super();
    }

    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
        logger.info("WLServerLoginModule.initialize");
        this.subject = subject;
        this.callbackHandler = callbackHandler;
        this.setSharedState(sharedState);
        this.setOptions(options);
        String appName = options.get("app-unique-name").toString();
        logger.info("AppName in WLServerLoginModule: " + appName);
    }

    public boolean login() throws LoginException {
        logger.info("WLServerLoginModule.login");
        if (callbackHandler == null)
            throw new LoginException("Error: no CallbackHandler available " + "to garner authentication information from the user");

        Callback[] callbacks = new Callback[2];
        callbacks[0] = new NameCallback("user name: ");
        callbacks[1] = new PasswordCallback("password: ", false);
        try {
            callbackHandler.handle(callbacks);
            username = ((NameCallback) callbacks[0]).getName();
            char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
            if (tmpPassword == null) {
                // treat a NULL password as an empty password
                tmpPassword = new char[0];
            }
            password = new String(tmpPassword);
            if (isEmpty(username) || isEmpty(password)) {
                throw new LoginException("User name or password is empty");
            }
        } catch (java.io.IOException ioe) {
            throw new LoginException(ioe.toString());
        } catch (UnsupportedCallbackException uce) {
            throw new LoginException("Error: " + uce.getCallback().toString() + " not available to garner authentication information " + "from the user");
        }

        try {
            if (isValidUser(username, password)) {
                succeeded = true;
            } else {
                succeeded = false;
            }
        } catch (Exception e) {
            logger.error("Post validation exception e: ", e);
            succeeded = false;
        }
        return succeeded;
    }

    private boolean isValidUser(String username, String password) {
        // Your custom validation logic
        return true;
    }


    public boolean commit() throws LoginException {
        logger.info("WLServerLoginModule.commit");
        if (succeeded == false) {
            return false;
        } else {
            // add a Principal (authenticated identity) to the Subject
            // assume the user we authenticated is the SamplePrincipal
            userPrincipal = new WLSAuthPrincipal(username, password, userData);
            if (!subject.getPrincipals().contains(userPrincipal)) {
                // subject.getPrincipals().add(new WLSUserImpl(username));
                subject.getPrincipals().add(userPrincipal);
                logger.info("Custom User principal Added");
            }
            subject.getPrincipals().add(new WLSUserImpl(username));
            subject.getPrincipals().add(new WLSGroupImpl("ABC_USERS"));
            logger.info("Login Module successfully added user principal");
            if (subject != null && subject.getPrincipals() != null) {
                logger.info("All user principals added: " + subject.getPrincipals());
                logger.info("All user principals count: " + subject.getPrincipals().size());
            }
            // in any case, clean out state
            username = null;
            password = null;
            commitSucceeded = true;
            return true;
        }
    }

    public boolean abort() throws LoginException {
        logger.info("WLServerLoginModule.abort");
        if (succeeded == false) {
            return false;
        } else if (succeeded == true && commitSucceeded == false) {
            // login succeeded but overall authentication failed
            succeeded = false;
            username = null;
            password = null;
            userPrincipal = null;
        } else {
            // overall authentication succeeded and commit succeeded, but
            // someone else's commit failed
            logout();
        }
        return true;
    }

    public boolean logout() throws LoginException {
        logger.info("WLServerLoginModule.logout");
        subject.getPrincipals().remove(userPrincipal);
        succeeded = false;
        succeeded = commitSucceeded;
        username = null;
        password = null;
        userPrincipal = null;
        logger.info("Login Module successfully removed user principal after successful logout");
        return true;
    }

    public Map<String, ?> getSharedState() {
        return sharedState;
    }

    public void setSharedState(Map<String, ?> sharedState) {
        this.sharedState = sharedState;
    }

    public Map<String, ?> getOptions() {
        return options;
    }

    public void setOptions(Map<String, ?> options) {
        this.options = options;
    }
}

2) 我想拥有自己的自定义 AuthPrincipal,它扩展到 WLSAbstractPrincipal 并实现 WLUser。我的情况略有不同,我想在 AuthPrincipal 中存储用户名、密码和一些关于用户的更重要的信息。所以,我创建了这个 Custom AuthPrincipal。

3) 现在,创建一个 build.xml,它将发出此自定义身份验证主体。

4) 从中生成 jar 后,我将其与 WebLogic 支持的默认身份验证提供程序一起复制到 {WL_HOME}/server/lib/mbeantypes 中。

5) 现在,我们需要更改默认领域 myrealm。使用任何名称和 select 类型创建新的身份验证提供程序作为您创建的自定义身份验证提供程序的名称。并将此 Auth Provider 设为必需的。