为什么拦截器不会在从 servlet 过滤器调用的 EJB 方法上触发?
why does interceptor not get fired on an EJB method called from servlet filter?
我想使用 servlet 过滤器在 URLs 上实现高级资源过滤,并使用拦截器在方法上实现低级操作过滤,但我的拦截器不会在从 servlet 过滤器调用的 EJB 方法上触发.
拦截器注释接口:
@Inherited
@InterceptorBinding
@Retention (RUNTIME)
@Target({TYPE, METHOD})
public @interface Permit {
@Nonbinding
String[] actions() default "N/A";
}
拦截器:
@Permit
@Interceptor
public class PermitInterceptor {
@AroundInvoke
public Object verifyPermission(InvocationContext context) throws Exception {
Method method = context.getMethod();
if(method.isAnnotationPresent(Permit.class)) {
Permit permitAnnotation = method.getAnnotation(Permit.class);
List<String> permittedActions = Arrays.asList(permitAnnotation.actions());
List<String> userActions = SecurityContextHandler.getActiveUser().getActions();
if(!Collections.disjoint(permittedActions, userActions)){
return context.proceed();
}
}
throw new PermissionException("You do NOT have the required permissions to perform this action");
}
}
Servlet 过滤器:
@WebFilter(urlPatterns = {"/*"})
public class AccessFilter implements Filter {
@EJB
private RulesBean rules;
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
try{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String url = request.getRequestURI();
if(rules.isAllowed(url)){
chain.doFilter(request, response);
}else{
//handle as necessary
}
}catch(Exception ex){
//handle as necessary
}
}
}
最后是我想用来管理 routing/interception 我所有 servlet 的 EJB RulesBean
的样子;
规则:
@Stateless
@LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return canViewAllCustomers();
default: return true;
}
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
@Permit(actions={"ViewCustomers"})
private boolean canViewAllCustomers(){
return true;
}
...
//Other tests carried out here ...
}
不幸的是 PermitInterceptor
在进入 canViewAllCustomers()
方法之前没有被调用。
然而,令人惊讶的是,当 canViewAllCustomers()
被创建 public 并直接作为 rules.canViewAllCustomers()
而不是通过辅助方法 rules.isAllowed(String url)
调用时,PermitInterceptor
被触发。但这对我的情况没有帮助,因为它没有为我的 URL checks 提供单一入口点,这实际上意味着我必须在 Servlet 过滤器中进行所有检查。
问题:请问任何人都可以更清楚地说明事情以这种方式发生的原因吗?......非常欢迎提出有关实现这种情况的最佳方法的建议.谢谢。
注意: (提供更多视角)
您可能想知道为什么我要这样做,或者更具体地说,为什么 RuleBean
甚至存在...原因很简单,因为我的很多 Servlet 没有做太多事情,除了对 [=47 的路由响应=]触发服务器端 DataTables ajax 调用的视图,该调用填充表 ,因此我真的需要确保对视图的请求不会' 甚至达到获取视图的 if...else
条件,除非满足拦截器中的权限检查。
请参阅下面的示例 servlet;
@WebServlet ("/customers/*")
public class CustomerServlet extends VelocityViewServlet {
private static final String CUSTOMERS = "/customers"
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
if(uri.equals(CUSTOMERS)){
String view = Path.getTemplate("/customers/index.vm");
request.setAttribute("headerTitle", "Customers");
request.getRequestDispatcher(view).forward(request, response);
}else if(...){
...
}
}
}
您直接在 isAllowed()
中调用 canViewAllCustomers()
,这使应用程序服务器没有机会拦截该调用。
拦截与代理 classes 一起工作。当您将 EJB 注入您的 servlet 时,就像您所做的那样:
@EJB
private RulesBean rules;
实际注入的不是 EJB 实例,而是应用程序服务器在运行时创建的代理 class(您可以通过调试器看到这一点)。该代理 class 上的调用将被事务、自定义拦截器等拦截,然后委托给实际的 class 实例。
所以你需要做的是将 canViewAllCustomers()
放入一个新的 EJB,这样你就可以让应用程序服务器注入你的 RulesBean
class,
或者您可以从 isAllowed()
中检索 RulesBean
class 的引用,如下所示:
@Stateless
@LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
@Resource
SessionContext ctx;
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return self().canViewAllCustomers();
default: return true;
}
}
private RulesBean self() {
return ctx.getBusinessObject(RulesBean.class);
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
@Permit(actions={"ViewCustomers"})
public boolean canViewAllCustomers(){
return true;
}
}
我想使用 servlet 过滤器在 URLs 上实现高级资源过滤,并使用拦截器在方法上实现低级操作过滤,但我的拦截器不会在从 servlet 过滤器调用的 EJB 方法上触发.
拦截器注释接口:
@Inherited
@InterceptorBinding
@Retention (RUNTIME)
@Target({TYPE, METHOD})
public @interface Permit {
@Nonbinding
String[] actions() default "N/A";
}
拦截器:
@Permit
@Interceptor
public class PermitInterceptor {
@AroundInvoke
public Object verifyPermission(InvocationContext context) throws Exception {
Method method = context.getMethod();
if(method.isAnnotationPresent(Permit.class)) {
Permit permitAnnotation = method.getAnnotation(Permit.class);
List<String> permittedActions = Arrays.asList(permitAnnotation.actions());
List<String> userActions = SecurityContextHandler.getActiveUser().getActions();
if(!Collections.disjoint(permittedActions, userActions)){
return context.proceed();
}
}
throw new PermissionException("You do NOT have the required permissions to perform this action");
}
}
Servlet 过滤器:
@WebFilter(urlPatterns = {"/*"})
public class AccessFilter implements Filter {
@EJB
private RulesBean rules;
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
try{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String url = request.getRequestURI();
if(rules.isAllowed(url)){
chain.doFilter(request, response);
}else{
//handle as necessary
}
}catch(Exception ex){
//handle as necessary
}
}
}
最后是我想用来管理 routing/interception 我所有 servlet 的 EJB RulesBean
的样子;
规则:
@Stateless
@LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return canViewAllCustomers();
default: return true;
}
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
@Permit(actions={"ViewCustomers"})
private boolean canViewAllCustomers(){
return true;
}
...
//Other tests carried out here ...
}
不幸的是 PermitInterceptor
在进入 canViewAllCustomers()
方法之前没有被调用。
然而,令人惊讶的是,当 canViewAllCustomers()
被创建 public 并直接作为 rules.canViewAllCustomers()
而不是通过辅助方法 rules.isAllowed(String url)
调用时,PermitInterceptor
被触发。但这对我的情况没有帮助,因为它没有为我的 URL checks 提供单一入口点,这实际上意味着我必须在 Servlet 过滤器中进行所有检查。
问题:请问任何人都可以更清楚地说明事情以这种方式发生的原因吗?......非常欢迎提出有关实现这种情况的最佳方法的建议.谢谢。
注意: (提供更多视角) 您可能想知道为什么我要这样做,或者更具体地说,为什么
RuleBean
甚至存在...原因很简单,因为我的很多 Servlet 没有做太多事情,除了对 [=47 的路由响应=]触发服务器端 DataTables ajax 调用的视图,该调用填充表 ,因此我真的需要确保对视图的请求不会' 甚至达到获取视图的 if...else
条件,除非满足拦截器中的权限检查。
请参阅下面的示例 servlet;
@WebServlet ("/customers/*")
public class CustomerServlet extends VelocityViewServlet {
private static final String CUSTOMERS = "/customers"
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
if(uri.equals(CUSTOMERS)){
String view = Path.getTemplate("/customers/index.vm");
request.setAttribute("headerTitle", "Customers");
request.getRequestDispatcher(view).forward(request, response);
}else if(...){
...
}
}
}
您直接在 isAllowed()
中调用 canViewAllCustomers()
,这使应用程序服务器没有机会拦截该调用。
拦截与代理 classes 一起工作。当您将 EJB 注入您的 servlet 时,就像您所做的那样:
@EJB
private RulesBean rules;
实际注入的不是 EJB 实例,而是应用程序服务器在运行时创建的代理 class(您可以通过调试器看到这一点)。该代理 class 上的调用将被事务、自定义拦截器等拦截,然后委托给实际的 class 实例。
所以你需要做的是将 canViewAllCustomers()
放入一个新的 EJB,这样你就可以让应用程序服务器注入你的 RulesBean
class,
或者您可以从 isAllowed()
中检索 RulesBean
class 的引用,如下所示:
@Stateless
@LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
@Resource
SessionContext ctx;
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return self().canViewAllCustomers();
default: return true;
}
}
private RulesBean self() {
return ctx.getBusinessObject(RulesBean.class);
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
@Permit(actions={"ViewCustomers"})
public boolean canViewAllCustomers(){
return true;
}
}