@EJB注入自定义UserdetailsS​​ervice(实现Spring Security UserDetailsS​​ervice)

时间:2013-05-17 16:12:13

标签: java java-ee jsf-2 spring-security ejb-3.1

我在我的应用程序中使用了JPA / EJB 3 / JSF 2 / Spring Security 3中的glassfish 3.1.2。 我想写一个像这样的自定义UserdetailsS​​ervice:

import org.apache.log4j.Logger;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
    public class MyUserDetailsService implements UserDetailsService {

        private static final Logger logger = Logger.getLogger(MyUserDetailsService.class);
        @EJB
        private CollaborateurFacadeLocal collaborateurFacade;

        @Override
        public UserDetails loadUserByUsername(String userName) {
            Collaborateur collab = getUser(userName);
            if (collab == null) {
                throw new UsernameNotFoundException(userName + " not found");
            }
            User user = new User(collab);
            if (user == null) {
                throw new UsernameNotFoundException(userName + " not found");
            }
            return user;
        }
    }

所以我试图在我的MyUserDetailsS​​ervice中注入一个EJB,以便能够在我的Spring Security上下文文件中将它用作身份验证提供程序:“applicationContext-security.xml”,如下所示:

<authentication-manager>
        <authentication-provider user-service-ref="myUserDetailsService">
        </authentication-provider>
    </authentication-manager>

问题是我陷入了由null collaborateurFacade引起的NullPointerException。

我尝试了几件事,其中包括:

  • 使用上下文(如选址here)来释放EJB:解决方案有效,但这不是我想要的:因为EJB的名称可能会被修改。

  • 将MyUserDetailsS​​ervice类作为JSF 2托管bean,将collaborateurFacade设置为null。

  • 将MyUserDetailsS​​ervice类作为JSF 2托管bean并使用ApplicationScoped范围,将collaborateurFacade设置为null。

问题: 是否有任何干净的方法可以在MyUserDetailsS​​ervice类中注入EJB?

我知道我可以用@Service注释来注释我的类(参见this link),然后我会混合使用EJB和Spring,我不想要

2 个答案:

答案 0 :(得分:2)

这是一个简单的例子,理论上应该起作用

  1. 将其添加到您的上下文配置
  2. <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">  
        <property name=”alwaysUseJndiLookup” value=”true” />  
    </bean>  
    

    2。我猜你有一个本地EJB,因为你尝试使用@EJB注释。您必须为EJB提供映射名称。例如,对于无国籍用户

    @Stateless(name = "ejb/CollaborateurFacade", mappedName = "ejb/CollaborateurFacade")
    class CollaboratorFacade {}
    

    3。在spring bean中使用mappedName

    @EJB(mappedName = "ejb/CollaborateurFacade")
    private CollaborateurFacadeLocal collaborateurFacade;
    

    我知道你写道,EJB的名称是一个需要修改的主题,但我不知道你怎么能避免这样做。

    还有另一种可能的解决方案(基于xml!)http://czetsuya-tech.blogspot.ca/2012/05/how-to-call-stateless-ejb-from-spring.html

答案 1 :(得分:1)

我想通过以下方式解决我的问题:不要重命名我的外观(EJB),并且必须使用我在类注释@EJB中定义的名称查找我的EJB。然后我的UserDetailsS​​ervice变成这样:

@EJB(name = "collaborateurFacadeLocal", beanInterface = CollaborateurFacadeLocal.class)
public class SiUserDetailsService implements UserDetailsService {

    private static final Logger logger = Logger.getLogger(SiUserDetailsService.class);
    private CollaborateurFacadeLocal collaborateurFacade;

    private static final String COLLABORATEUR_EJB_LOOKUP_PATH = "java:comp/env/collaborateurFacadeLocal";

    @Override
    public UserDetails loadUserByUsername(String userName) {
        User user;
        Collaborateur collab = getUser(userName);
        if (collab == null) {
            throw new UsernameNotFoundException(userName + " not found");
        }
        user = new User(collab);
        if (user == null) {
            throw new UsernameNotFoundException(userName + " not found");
        }
        return user;
    }

    private Collaborateur getUser(String userName) {
        try {
            InitialContext initialContext = new InitialContext();
            collaborateurFacade = (CollaborateurFacadeLocal) initialContext.lookup(COLLABORATEUR_EJB_LOOKUP_PATH);
            return collaborateurFacade.findUserByUserName(userName);
        } catch (NamingException ex) {
            logger.error("Could not lookup for EJB CollaborateurFacadeLocal with lookup path " + COLLABORATEUR_EJB_LOOKUP_PATH);
        }
        return null;
    }
}

java:comp / env / collaborateurFacadeLocal中的collaborateurFacadeLocal是@EJB中的一个(name = annotation,将其与@EJB属性相关联(name =

这样:

  • 如果更改了外观名称,我的SiUserDetailsS​​ervice中将出现编译错误,并且仅在我的siUserDetailsS​​ervice类中更改其名称,查找仍然有效。
  • 如果耳朵名称发生变化,我的SiUserDetailsS​​ervice仍然有效,因为我没有使用耳朵名称查找。

我的Spring Security上下文文件仍然是:

<beans:bean id = "siUserDetailsService" class = "com.xxx.xxx.beans.SiUserDetailsService" />  

    <authentication-manager>
        <authentication-provider user-service-ref="siUserDetailsService">
        </authentication-provider>
    </authentication-manager>