java 从纯客户端调用远程 EJB(基于 IIOP 的 RMI)时如何传播 JAAS 主题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12213734/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 08:04:06  来源:igfitidea点击:

How to propagate JAAS Subject when calling a remote EJB (RMI over IIOP) from a pure client

javajakarta-eejbosswebspherejaas

提问by Yves Martin

I am testing the propagation of JAAS Subjectwith a custom Principalfrom a standalone EJB client running on a raw Java runtime to a JavaEE server. I am targeting both JBoss and WebSphere implementations.

我正在测试带有自定义主体JAAS 主题从在原始 Java 运行时上运行的独立 EJB 客户端到 JavaEE 服务器的传播。我的目标是 JBoss 和 WebSphere 实现。

According to this forum threadI have expected it would work with JBoss easily.

根据这个论坛帖子,我预计它可以轻松地与 JBoss 一起使用。

Here is my EJB client code code snippet:

这是我的 EJB 客户端代码片段:

Subject subject = new Subject();
Principal myPrincipal = new MyPrincipal("me I myself");
subject.getPrincipals().add(myPrincipal);

PrivilegedExceptionAction<String> action = new PrivilegedExceptionAction<String>() {
    public String run() throws Exception {
            String result;
            System.out.println("Current Subject: " + Subject.getSubject(AccessController.getContext()));
            InitialContext ic = new InitialContext();
            Business1 b = (Business1) ic.lookup("StatelessBusiness1");
            result = b.getNewMessage("Hello World");
            return result;
        }
    };

result = subject.doAs(subject, action);
System.out.println("result "+result);

Server-side code is:

服务端代码为:

public String getNewMessage(String msg) {
    System.out.println("getNewMessage principal: " + sessionContext.getCallerPrincipal());
    System.out.println("Current Subject: " + Subject.getSubject(AccessController.getContext()));
    return "getNewMessage: " + msg;
}

To be sure, even if it is the default behaviour, I have added this section to my ejb-jar.xmlsession bean:

可以肯定的是,即使这是默认行为,我也已将此部分添加到我的ejb-jar.xml会话 bean:

<security-identity>
   <use-caller-identity/>
</security-identity>

My session bean is not protected by any role.

我的会话 bean 不受任何角色的保护。

According to this IBM WebSphere infocenter section, I have also enabled the system property com.ibm.CSI.rmiOutboundPropagationEnabled=true.

根据这个 IBM WebSphere infocenter 部分,我还启用了系统属性com.ibm.CSI.rmiOutboundPropagationEnabled=true

Technically speaking the service call works properly either on JBoss or WebSphere. But the JAAS Subject including my custom principal created on the client is not propagated to the server. Or course, the Subjectdumped just before JNDI context creation and EJB call is OK.

从技术上讲,服务调用在 JBoss 或 WebSphere 上都能正常工作。但是包含我在客户端上创建的自定义主体的 JAAS 主题不会传播到服务器。或者当然,Subject在 JNDI 上下文创建和 EJB 调用之前转储是可以的。

I run the same Java runtime version for server and client (IBM Java6 SR9 FP2...), MyPrincipalserializable class is available in server ClassPath (AppServer/lib/extfor WebSphere, server/default/libfor JBoss)

我为服务器和客户端运行相同的 Java 运行时版本(IBM Java6 SR9 FP2 ...),可MyPrincipal序列化类在服务器 ClassPath 中可用(AppServer/lib/ext对于 WebSphere,server/default/lib对于 JBoss)

WebSphere dumps:

WebSphere 转储:

[8/31/12 11:56:26:514 CEST] 00000024 SystemOut     O getNewMessage principal: UNAUTHENTICATED
[8/31/12 11:56:26:515 CEST] 00000024 SystemOut     O Current Subject: null

JBoss dumps:

JBoss 转储:

 12:30:20,540 INFO  [STDOUT] getNewMessage principal: anonymous
 12:30:20,540 INFO  [STDOUT] Current Subject: null

For sure, I have missed some kind of magic spell. Do you know which one ?

当然,我错过了某种魔法咒语。你知道是哪一个吗?

采纳答案by pglezen

I suspect you don't have security enabled on the WAS server. Because security is not enabled and you didn't authenticate to WAS, there is no credential. Thus your call to getCallerPrincipalis returning UNAUTHENTICATED.

我怀疑您没有在 WAS 服务器上启用安全性。由于未启用安全性且您未向 WAS 进行身份验证,因此没有凭据。因此,您的呼叫getCallerPrincipal返回 UNAUTHENTICATED。

If you turn on application security in WAS, you'll have to authenticate via the CSIv2 protocol. Creating your own JAAS subject in a standalone client will not do it. If it could, then anyone could create a "hey, it's me" credential and login to any remote EJB they wanted.

如果您在 WAS 中打开应用程序安全性,则必须通过CSIv2 协议进行身份验证。在独立客户端中创建您自己的 JAAS 主题不会这样做。如果可以,那么任何人都可以创建“嘿,是我”凭证并登录到他们想要的任何远程 EJB。

Your code will work on the server by attaching your subject to the running thread of execution. Flowing subjects/credentials across the wire requires a protocol to effect the serialization of the subject info and ensure trust of the party asserting the identity in the credential. From a standalone client, WAS accepts user info in the form of basic authorization, LTPA, and kerberos. This can be configured on an inbound CSIv2 configuration within the admin console. It's documented in the Info Center link I referenced earlier.

通过将您的主题附加到正在运行的执行线程,您的代码将在服务器上运行。跨线路流动主题/凭证需要一个协议来影响主题信息的序列化并确保在凭证中声明身份的一方的信任。从独立客户端,WAS 以基本授权、LTPA 和 kerberos 的形式接受用户信息。这可以在管理控制台内的入站 CSIv2 配置上进行配置。它记录在我之前引用的信息中心链接中。

It's fun stuff. Good luck.

这是有趣的东西。祝你好运。

回答by Murlo

probably this will help you with the price to use proprietary websphere-classes. as I remember , websphere does NOT propagate the jaas caller-subject, this is typical to ibm

可能这将帮助您支付使用专有 websphere 类的价格。我记得,websphere 不会传播 jaas 调用者主题,这是 ibm 的典型特征

    package foo.bar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.login.CredentialExpiredException;
import org.apache.log4j.Logger;
import com.ibm.websphere.security.WSSecurityException;
import com.ibm.websphere.security.auth.CredentialDestroyedException;
import com.ibm.websphere.security.auth.WSSubject;
import com.ibm.websphere.security.cred.WSCredential;

public class IdentityHelper
{
    private static final Logger log = Logger.getLogger(IdentityHelper.class);
    private static final String CLASS_OBJECT = "java.util.HashMap";
    private static final String KEY_OBJECT = "java.lang.String";
    private static final String VALUE_OBJECT = "java.util.HashSet"; 

    private Subject subject=null;
    private WSCredential creds;
    private Set publicCredentials=null;
    public IdentityHelper(Subject _subject) throws WSSecurityException
    {
        if(_subject==null)
        {
            IdentityHelper.log.warn("given subject was null, using Caller-Subject or the RunAs-Subject!");
            this.subject = WSSubject.getCallerSubject();
            if(this.subject==null)this.subject=WSSubject.getRunAsSubject();
        }
        else
        {           
            this.subject=_subject;
        }
        init();
    }
    public IdentityHelper() throws WSSecurityException
    {
        this.subject=WSSubject.getRunAsSubject();
        if(this.subject==null)
        {
            IdentityHelper.log.warn("using Caller-Subject NOT the RunAs-Subject!");
            this.subject = WSSubject.getCallerSubject();
        }
        init();
    }

    private void init() throws WSSecurityException
    {
        Set<WSCredential> credSet= this.subject.getPublicCredentials(WSCredential.class);
        //set should contain exactly one WSCredential
        if(credSet.size() > 1) throw new WSSecurityException("Expected one WSCredential, found " + credSet.size());
        if(credSet.isEmpty())
        {
            throw new WSSecurityException("Found no credentials");
        }
        Iterator<WSCredential> iter= credSet.iterator();
        this.creds=(WSCredential) iter.next();
        this.publicCredentials=this.subject.getPublicCredentials();
    }
    public WSCredential getWSCredential() throws WSSecurityException
    {
        return this.creds;
    }
    public List<String> getGroups() throws WSSecurityException,CredentialDestroyedException,CredentialExpiredException
    {
        WSCredential c = this.getWSCredential();
        return c.getGroupIds();
    }
    /**
     * helper method for obtaining user attributes from Subject objects.
     * @param subject
     * @return
     */
    @SuppressWarnings("unchecked")
    public Map<String, Set<String>> getAttributes()
    {
        Map<String, Set<String>> attributes = null;
        Iterator<?> i = this.subject.getPublicCredentials().iterator();
        while (attributes == null && i.hasNext())
        {
            Map<String, Set<String>> tmp = null;
            Object o = i.next();
            if(IdentityHelper.log.isDebugEnabled())
            {
                IdentityHelper.log.debug("checking for attributes (class name): " + o.getClass().getName());
            }
            if(!o.getClass().getName().equals(CLASS_OBJECT))
                continue;//loop through
            tmp = (Map) o;
            Object tObject = null;
            Iterator<?> t = null;
            t = tmp.keySet().iterator();
            tObject = t.next();
            if(IdentityHelper.log.isDebugEnabled())
            {
                IdentityHelper.log.debug("checking for attributes (key object name): " + tObject.getClass().getName()); 
            }
            if(!tObject.getClass().getName().equals(KEY_OBJECT))
                continue;//loop through
            t = tmp.values().iterator();
            tObject = t.next();
            if(IdentityHelper.log.isDebugEnabled())
            {
                IdentityHelper.log.debug("checking for attributes (value object name): " + tObject.getClass().getName()); 
            }
            if(!tObject.getClass().getName().equals(VALUE_OBJECT))
                continue;//loop through
            attributes = (Map) o;
        }
        if (attributes == null)
        {
            attributes = new HashMap<String, Set<String>>(); 
        }
        return attributes;
    }
    public Subject getSubject()
    {
        return this.subject;
    }
    protected Set getPublicCredentials() {
        return publicCredentials;
    }


}

see also: Getting the caller subject from the thread for JAASand Getting the RunAs subject from the thread

还看到:充分利用线程JAAS主叫主题,并充分利用线程的RunAs主题