在加载的servlet上调用安全的EJB方法(EAP 6.4)

时间:2017-08-22 10:26:44

标签: security ejb jaas jboss-eap-6

我试图在服务器负载上调用安全的EJB方法但我得到例外:

09:49:58,011 ERROR [org.jboss.as.ejb3.invocation] (ServerService Thread Pool -- 54) JBAS014134: EJB Invocation failed on component SecuredEJB for method public java.lang.String org.jboss.as.quickstarts.ejb_security.SecuredEJB.getSecurityInfo(): javax.ejb.EJBAccessException: JBAS014502: Invocation on method: public java.lang.String org.jboss.as.quickstarts.ejb_security.SecuredEJB.getSecurityInfo() of bean: SecuredEJB is not allowed
    at org.jboss.as.ejb3.security.AuthorizationInterceptor.processInvocation(AuthorizationInterceptor.java:114) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:86) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:59) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50) [jboss-as-ee-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:55) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ee.component.TCCLInterceptor.processInvocation(TCCLInterceptor.java:45) [jboss-as-ee-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:185) [jboss-as-ee-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:185) [jboss-as-ee-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:73) [jboss-as-ee-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.as.quickstarts.ejb_security.SecuredEJB$$$view1.getSecurityInfo(Unknown Source) [classes:]
    at org.jboss.as.quickstarts.ejb_security.SecuredEJBServlet.init(SecuredEJBServlet.java:55) [classes:]
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1194) [jbossweb-7.5.7.Final-redhat-1.jar:7.5.7.Final-redhat-1]
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1100) [jbossweb-7.5.7.Final-redhat-1.jar:7.5.7.Final-redhat-1]
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:3593) [jbossweb-7.5.7.Final-redhat-1.jar:7.5.7.Final-redhat-1]
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:3802) [jbossweb-7.5.7.Final-redhat-1.jar:7.5.7.Final-redhat-1]
    at org.jboss.as.web.deployment.WebDeploymentService.doStart(WebDeploymentService.java:163) [jboss-as-web-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.as.web.deployment.WebDeploymentService.access$000(WebDeploymentService.java:61) [jboss-as-web-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at org.jboss.as.web.deployment.WebDeploymentService$1.run(WebDeploymentService.java:96) [jboss-as-web-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) [rt.jar:1.7.0_55]
    at java.util.concurrent.FutureTask.run(FutureTask.java:262) [rt.jar:1.7.0_55]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_55]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_55]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_55]
    at org.jboss.threads.JBossThread.run(JBossThread.java:122) [jboss-threads-2.1.2.Final-redhat-1.jar:2.1.2.Final-redhat-1]

我的项目结构是:

enter image description here

SecuredEJBServlet.java

@SuppressWarnings("serial")
@WebServlet("/SecuredEJBServlet")
@ServletSecurity(@HttpConstraint(rolesAllowed = "quickstarts"))
public class SecuredEJBServlet extends HttpServlet {

    @EJB
    private SecuredEJB securedEJB;

    @Override
public void init(javax.servlet.ServletConfig arg0) throws javax.servlet.ServletException{
    Subject s = CMnJAASLogin.loginMethod();
    runAs(s);
    super.init(arg0);
    System.out.println("Inside init...");
    securedEJB.getName();
    //securedEJB.getSecurityInfo();
}

private void runAs(Subject s){
    Subject.doAs(s, new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            System.out.println("Inside privileged action");
            securedEJB.getSecurityInfo();
            return null;
        }
    });
}

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        securedEJB.getName();
        String principal = securedEJB.getSecurityInfo();
...
}

SecuredEJB.java

@Stateless
@DeclareRoles("java")
@SecurityDomain("custom")
public class SecuredEJB {
    @Resource
    private SessionContext ctx;

    @RolesAllowed({ "java" })
    public String getSecurityInfo() {
        Principal principal = ctx.getCallerPrincipal();
        return principal.toString();
    }

    @PermitAll
    public void getName(){
        System.out.println(principal.getName());
    }
}

CMnAuthenticator.java

public class CMnAuthenticator extends UsernamePasswordLoginModule{
    @Override
    protected String getUsersPassword() throws LoginException {
        return "java";
    }

    @Override
    protected boolean validatePassword(String passwordWant, String passwordHave){
        return true;
    }

    @Override
    protected Group[] getRoleSets() throws LoginException {
        HashMap setsMap = new HashMap();
        String groupName = "Roles";
        Group group = (Group) setsMap.get(groupName);
        if (group == null) {
            group = new SimpleGroup(groupName);
            setsMap.put(groupName, group);
        }
        try {
            Principal p = super.createIdentity("quickstarts");
            group.addMember(p);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        Group[] roleSets = new Group[setsMap.size()];
        setsMap.values().toArray(roleSets);
        return roleSets;
    }
}

CMnEJBAuthenticator.java

public class CMnEJBAuthenticator extends UsernamePasswordLoginModule {
    @Override
    protected String getUsersPassword() throws LoginException {
        return "java";
    }

    @Override
    protected boolean validatePassword(String passwordWant, String passwordHave){
        return true;
    }

    @Override
    protected Group[] getRoleSets() throws LoginException {
        System.out.println("Inside CMnEJBAuthenticator:getRoleSets...");
        HashMap setsMap = new HashMap();
        String groupName = "Roles";
        Group group = (Group) setsMap.get(groupName);
        if (group == null) {
            group = new SimpleGroup(groupName);
            setsMap.put(groupName, group);
        }
        try {
            Principal p = super.createIdentity("java");
            group.addMember(p);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        Group[] roleSets = new Group[setsMap.size()];
        setsMap.values().toArray(roleSets);
        return roleSets;
    }
}

的JBoss-web.xml中

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web>
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.jboss.org/schema/jbossas
    http://www.jboss.org/schema/jbossas/jboss-web_7_2.xsd">
    <!-- Configure usage of the security domain "other" -->
    <security-domain>servlet-security-quickstart</security-domain>
    <disable-audit>true</disable-audit>
</jboss-web>

的web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   version="3.0">

   <!-- Configure login to be HTTP Basic -->
   <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>RealmUsersRoles</realm-name>
   </login-config>

    <servlet>
        <servlet-name>bootstrap</servlet-name>
        <servlet-class>org.jboss.as.quickstarts.ejb_security.SecuredEJBServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>bootstrap</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

standalone.xml

<security-domain name="custom" cache-type="default">
                    <authentication>
                        <login-module code="org.jboss.as.quickstarts.ejb_security.others.CMnEJBAuthenticator" flag="required">
                            <module-option name="unauthenticatedIdentity" value="Super"/>
                        </login-module>
                    </authentication>
                </security-domain>
<security-domain name="servlet-security-quickstart" cache-type="default">
                    <authentication>
                        <login-module code="org.jboss.as.quickstarts.ejb_security.others.CMnAuthenticator" flag="required">
                            <module-option name="unauthenticatedIdentity" value="Super"/>
                        </login-module>
                    </authentication>
                </security-domain>

Jaas身份验证:

package org.jboss.as.quickstarts.ejb_security.others;

import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.LoginContext;
import java.io.IOException;

public class CMnJAASLogin {

    public static Subject loginMethod(){
        LoginContext lc = null;

        CallbackHandler cabHndlr = new CallbackHandler() {
            @Override
            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                for (int i = 0; i < callbacks.length; i++) {
                    if (callbacks[i] instanceof NameCallback) {
                        NameCallback nc = (NameCallback) callbacks[i];
                        nc.setName("java");
                    } else if (callbacks[i] instanceof PasswordCallback) {
                        PasswordCallback pc = (PasswordCallback) callbacks[i];
                        pc.setPassword("java".toCharArray());
                    } else {
                        throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
                    }
                }
            }
        };
        try {
            lc = new LoginContext("custom", cabHndlr);
            lc.login();
            return lc.getSubject();
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return null;
    }
}

知道我缺少什么吗?

注意:我正在研究JBoss EAP 6.4和java 1.7

将@RunAs(“java”)添加到Servlet类可以解决这个直接问题。

但是,如果我需要拒绝其他用户访问,RunAs注释将无法帮助那里。

所以,我需要以这种方式解决它(如果没有用户调用方法/ ejb / servlet,请使用“java”,否则使用该用户的角色。

2 个答案:

答案 0 :(得分:0)

我没有尝试过这个,但是servlet规范的§15.3.1说你只需要在web.xml中为你的servlet定义添加一个run-as元素:

<servlet>
    <servlet-name>bootstrap</servlet-name>
    <servlet-class>org.jboss.as.quickstarts.ejb_security.SecuredEJBServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <run-as>java</run-as>
</servlet>

但是,对于正常认证的servlet调用,是否将实际经过身份验证的主体传播到EJB并不完全清楚。你将不得不尝试看看。

答案 1 :(得分:0)

我在AppConfigurationProperty中使用org.jboss.security.ClientLoginModule后解决了这个问题。

最后,JAAS类看起来像这样:

import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class CMnJAASLogin {

    public static LoginContext loginMethod() {
        LoginContext lc = null;

        CallbackHandler cabHndlr = new CallbackHandler() {
            @Override
            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                for (int i = 0; i < callbacks.length; i++) {
                    if (callbacks[i] instanceof NameCallback) {
                        NameCallback nc = (NameCallback) callbacks[i];
                        nc.setName("java");
                    } else if (callbacks[i] instanceof PasswordCallback) {
                        PasswordCallback pc = (PasswordCallback) callbacks[i];
                        pc.setPassword("java".toCharArray());
                    } else {
                        throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
                    }
                }
            }
        };
        try {
            String configurationName = "JBoss Test";
            Configuration config = new JBossJaasConfiguration(configurationName);

            lc = new LoginContext(configurationName, new Subject(), cabHndlr, config);
            return lc;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }


    static class JBossJaasConfiguration extends Configuration {
        private final String configurationName;

        JBossJaasConfiguration(String configurationName) {
            this.configurationName = configurationName;
        }

        @Override
        public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
            if (!configurationName.equals(name)) {
                throw new IllegalArgumentException("Unexpected configuration name '" + name + "'");
            }

            return new AppConfigurationEntry[]{

                    createClientLoginModuleConfigEntry(),

            };
        }

        private AppConfigurationEntry createClientLoginModuleConfigEntry() {
            Map<String, String> options = new HashMap<String, String>();
            options.put("multi-threaded", "true");
            options.put("restore-login-identity", "true");

            return new AppConfigurationEntry("org.jboss.security.ClientLoginModule",
                    AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
        }
    }
}

整个项目上传到:https://github.com/shekharswaraj/EJBSecurity

注意:它只是一个包含许多硬编码值的测试项目。整个项目已在EAP6.4快速入门的ejb-security项目之上进行了修改。