我使用dropwizard 0.7.0,我想创建一个自定义过滤器。
自定义过滤器应检查数据库中是否存在令牌。 在Application类中创建过滤器并注册此过滤器的正确方法是什么?
我使用this question来实现过滤器,这是有效的,但是当我将代码更改为:
final AuthenticationDAO authenticationDAO = new AuthenticationDAO(hibernateBundle.getSessionFactory());
environment.servlets().addFilter("authenticationFilter", new AuthenticationFilter(authenticationDAO)).addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/transaction/*");
这是我的过滤器:
public class AuthenticationFilter implements Filter {
private final AuthenticationDAO authenticationDAO;
public AuthenticationFilter(AuthenticationDAO authenticationDAO) {
this.authenticationDAO = authenticationDAO;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String authenticationToken = ((Request) servletRequest).getHeader(Constants.HEADER_TOKEN_PARAM_NAME);
HttpServletResponse response = (HttpServletResponse)servletResponse;
if(Strings.isNullOrEmpty(authenticationToken)){
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
} else if(!authenticationDAO.findByAuthenticationToken(authenticationToken).isPresent()){
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Override
public void destroy() {
}
}
当访问过滤器时,我收到以下错误,因为没有会话活动:
WARN [2014-04-22 14:37:42,733] org.eclipse.jetty.servlet.ServletHandler:/ test / show ! org.hibernate.HibernateException:当前没有会话绑定到执行上下文 !在org.hibernate.context.internal.ManagedSessionContext.currentSession(ManagedSessionContext.java:75)〜[hibernate-core-4.3.1.Final.jar:4.3.1.Final] !在org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1013)〜[hibernate-core-4.3.1.Final.jar:4.3.1.Final]
答案 0 :(得分:10)
启用DropWizard" magic" (Hibernate)在代码的非托管部分,你必须注入一个SessionFactory。您可以在DropWizard配置中看到如何创建一个:
https://dropwizard.github.io/dropwizard/manual/hibernate.html
然后,您可以将该sessionFacotry注入构造函数中的AuthenticationFilter。
在过滤器中,您必须手动绑定hibernate会话,例如:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String authenticationToken = ((Request) servletRequest).getHeader(Constants.HEADER_TOKEN_PARAM_NAME);
Session session = sessionFactory.openSession();
session.setDefaultReadOnly(true);
session.setCacheMode(CacheMode.NORMAL);
session.setFlushMode(FlushMode.MANUAL);
ManagedSessionContext.bind(session);
// DropWizard magic enabled from this point.
HttpServletResponse response = (HttpServletResponse)servletResponse;
if(Strings.isNullOrEmpty(authenticationToken)){
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
} else if(!authenticationDAO.findByAuthenticationToken(authenticationToken).isPresent()){
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
session.close();
// DropWizard magic disabled from this point.
}
这是本质。 bind()和close()之间发生的任何事情都会启用DropWizard魔法 - 也可以在托管Dao中使用。你可以通过使错误处理等更加健壮来改进它。
即使您正在点击示例中的托管资源,这也会有效。发生的事情是DropWizards RequestDispatcher激活并执行相同的操作:它打开一个会话并将其绑定到该线程。结果是托管资源使用的是另一个会话,而不是手动打开的会话,并且删除了最初绑定的会话而不进行清理。在你的例子中,THIS的效果是在调用doFilter(...)之后魔术已经结束。如果您以后要访问数据库,则必须重新绑定之前创建的会话。请致电:
ManagedSessionContext.bind(session);
我还没有遇到过这项技术的任何问题,但是如果您需要在另一个会话中进行的一个会话中访问数据,我可以想象一个问题。这可能需要对隔离级别进行一些调整。
答案 1 :(得分:1)
我自己就是这样做了,我认为我的问题正如Dropwizard主题中所讨论的那样: https://groups.google.com/forum/#!msg/dropwizard-user/LE6FYIpSDQ0/X5smCEZWltcJ
简而言之,您可能需要打开并绑定自己的会话 - 我的Dropwizard管理的类正在使用会话,但是不受dropwizard管理的类会遇到此错误。
我正在尝试在打开和绑定我自己的会话之间做出决定,然后将该类移动到由Dropwizard管理的类,以便我可以使用@UnitOfWork注释。
除此之外 - 如果你不在@UnitOfWork(似乎只对其余的调用有效),那么你可以使用会话就好 - 但是你不能使用DAO层。如果您使用DAO,您将收到该错误。可能还有更多,但为了我的目的,我只使用了SQLQuery:
SQLQuery deleteQuery = session.createSQLQuery("DELETE FROM MYTABLE WHERE REPORTTIME < ?");
deleteQuery.setTimestamp(0, cutoffDate.toDate());
deleteQuery.executeUpdate();
这是否是最好的解决方案是值得商榷的,但就我的目的来说,这已经足够了。
答案 2 :(得分:0)
也许你可以试试:
import com.google.common.base.Strings;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
public class AuthenticationFilter implements ContainerRequestFilter {
private final AuthenticationDAO authenticationDAO;
public AuthenticationFilter(AuthenticationDAO authenticationDAO) {
this.authenticationDAO = authenticationDAO;
}
@Override
public ContainerRequest filter(final ContainerRequest containerRequest) {
String authenticationToken = containerRequest.getHeaderValue(Constants.HEADER_TOKEN_PARAM_NAME);
if (Strings.isNullOrEmpty(authenticationToken)) {
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build());
} else if (!authenticationDAO.findByAuthenticationToken(authenticationToken).isPresent()) {
throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN).build());
}
return containerRequest;
}
}
在run-method中你可以添加:
environment.jersey().getResourceConfig().getContainerRequestFilters().add(new AuthenticationFilter(...));
此拉取请求https://github.com/dropwizard/dropwizard/pull/541可能会满足您的需求。
答案 3 :(得分:0)
在事务外使用AuthenticationDAO时会出现问题。如DropWizard manual中所述,注释@UnitOfWork可用于创建事务边界。
然而,为了使这项工作适用于泽西资源以外的课程,您需要使用UnitOfWorkAwareProxyFactory。
首先创建一个工厂。
UnitOfWorkAwareProxyFactory proxyFactory = new UnitOfWorkAwareProxyFactory("session factory", hibernateBundle.getSessionFactory());
然后用它来创建AuthenticationFilter。
AuthenticationFilter filter = proxyFactory.create(AuthenticationFilter.class, AuthenticationDAO.class, authenticationDAO);
environment.servlets().addFilter("authenticationFilter", filter).addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/transaction/*");
最后使用@UnitOfWork注释doFilter。
public class AuthenticationFilter implements Filter {
@UnitOfWork
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
...