我没有发现很多很好的一般安全解决方案(建议是值得欢迎的),但是我发现最好的一点是Apache Shiro.
不过这似乎有一些缺点.其中有些可以在Balus C’s网站阅读:
http://balusc.blogspot.com/2013/01/apache-shiro-is-it-ready-for-java-ee-6.html
但是我已经偶然发现另一个大问题,已经在here中提到了关于依赖注入和代理的问题.
基本上我有一个很好的基于JPA的UserDAO,它提供了身份验证所需的一切.我的数据库在persistence.xml和mydatabase-ds.xml(用于JBoss)中整齐配置.
再次复制所有这个配置信息似乎很愚蠢,并将用户表查询添加到shiro.ini中.所以这就是为什么我选择编写我自己的Realm而不是使用JdbcRealm.
我的第一个尝试就是授权AuthorizationRealm …类似于:
@Stateless public MyAppRealm extends AuthorizingRealm { @Inject private UserAccess userAccess; @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken userPassToken = (UsernamePasswordToken) token; User user = userAccess.getUserByEmail(userPassToken.getUsername()); if (user == null) { return null; } AuthenticationInfo info = new SimpleAuthenticationInfo(); // set data in AuthenticationInfo based on data from the user object return info; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // TODO return null; } }
所以这样做不好,因为MyAppRealm不能被代理,因为父类中有一个最终的init()方法.
我的第二个尝试是让MyAppRealm实现所有需要的接口,并将它们委托给AuthorizingRealm的实例.我不喜欢这个,但也可以尝试一下.
这让我进一步,webapp启动,但仍然不足.原因是在配置文件shiro.ini中,我指定了我的领域的类:
myAppRealm = com.myapp.MyAppRealm
这几乎告诉我,Shiro将负责创建MyAppRealm实例.因此,它不会被CDI管理,因此不会被注入,这正是我所看到的.
我已经看到这个SO answer,但是我看不到它可能如何工作,因为AuthorizingRealm的一个子类将继承一个最终的init()方法,这意味着该子类不能被代理.
有什么想法可以解决这个问题吗?
解决方法
解决这个问题的方法是在两个框架之间创建一个明确的桥梁.如果真的很幸运,非CDI框架将有钩子来让您自定义对象创建,您可以在其中插入使用CDI的内容(例如,在Stripes Web框架中,您可以编写一个使用CDI的ActionResolver).
如果没有,则桥必须采取代理的形式.在该代理中,您可以执行显式的CDI查找.您可以通过抓住BeanManager
来引导进入CDI,这可以让您设置上下文,然后在其中创建bean.这样的事情
BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager"); Bean<UserDAO> userDAObean = (Bean<UserDAO>) beanManager.resolve(beanManager.getBeans(UserDAO.class)); CreationalContext<?> creationalContext = beanManager.createCreationalContext(null); UserDAO userDAO = userDAObean.create(creationalContext);
userDAO是一个注入的CDI托管的bean,绑定到您现在拥有的上下文作为creationalContext.
当你完成了这个bean(由你自己做这个查询每次请求一次或每个应用程序生命周期一次)就可以释放这个bean:
creationalContext.release();