MVC拦截器vs春季安全过滤器vs其他……?


问题内容

我将带有Spring Security的Spring-MVC用于我的Web应用程序。它包括用户注册页面和私人用户面板。我目前使用以下网址格式进行了设置:

  • whatever/myapp/login 用户登录
  • whatever/myapp/register?step=1 开始注册
  • whatever/myapp/account/** 私有区视图(页面)
  • whatever/myapp/pending 注册后流程完成后显示的视图
  • whatever/myapp/blocked 帐户被阻止的视图
  • whatever/myapp/register/retry 如果注册失败,则允许重试

本质上,以下这些URL应该要求用户身份验证,即要求登录:

  • whatever/myapp/account/** (专用区域页面)
  • whatever/myapp/pending (此页面设置了计时器以重定向到/ account / home)
  • whatever/myapp/register/retry

使用Spring安全性很容易实现。但是,无论通过Spring安全性进行用户身份验证,都应根据用户当前的帐户状态(存储在我的数据库中)来访问或不访问私有区域页面。

更具体地说:如果用户尝试访问私有区域(/account/**)中的任何内容,则应根据状态向他显示适当的视图(重定向到适当的页面)。我定义了以下状态:

  • suspended -与待定视图有关
  • enabled -允许完全访问
  • disabled -这里不相关
  • retry_allowed-与重试视图有关
  • blocked -与帐户被阻止的视图有关

目前,我将MVC拦截器设置为/account/**,用于检查用户状态并重定向到相应的页面,但是由于某种原因,我面临着奇怪的行为,例如多控制器,因此我感觉这并不是理想的解决方案。调用…而且我也不确定何时在方法中返回true/
。这是拦截器的代码片段: false``preHandle()

@Override
public boolean preHandle(
    HttpServletRequest request, 
    HttpServletResponse response,
    Object arg2) 
    throws Exception {

IPanelUser pUser =  (IPanelUser) SecurityContextHolder.getContext()
        .getAuthentication().getPrincipal();

// check principal first and then load from DB
// "suspended" is initial status upon registration
if(pUser.getCustomer().getStatus() == CustomerStatus.Suspended.getCode()) {

    // if suspended, load from DB and update status
    Customer customer = this.customerService.getUserByUsername(pUser.getUsername());
    if(customer != null)
        pUser.getCustomer().setStatus(customer.getStatus());

    // still suspended? redirect to pending
    if(pUser.getCustomer().getStatus() == CustomerStatus.Suspended.getCode()) {
        response.sendRedirect("../pending");
        return false;
    }
}

if(pUser.getCustomer().getStatus() == CustomerStatus.Blocked.getCode()) {

    // redirect to blocked page
    response.sendRedirect("../blocked");
    SecurityContextHolder.clearContext();
    return false;
}

if(pUser.getCustomer().getStatus() == CustomerStatus.AllowRetry.getCode()) {

    // redirect to CC submission page
    response.sendRedirect("../register/retry");
    return false;
}

if(pUser.getCustomer().getStatus() == CustomerStatus.Enabled.getCode() ||
   pUser.getCustomer().getStatus() == CustomerStatus.Disabled.getCode()) {

    // do nothing
}

return true;
}

这是有效的方法吗?还有其他建议吗?


问题答案:

所有选项均有效,这取决于所需的抽象级别。

在中Filter,您只能访问HttpServletRequestHttpServletResponse对象,因此与ServletAPI紧密结合。您也无法(直接)访问所有出色的Spring功能,例如返回要渲染的视图或ResponseEntity

在中HandlerInterceptor,它又一次相同。您可以在preHandle()没有访问权限的地方直接进行重定向或请求处理,也可以ModelAndView设置签入的标志postHandle()。您将有权使用ModelAndViewSpring
MVC的其他功能,但不能使用。

Spring Security是一个不错的选择,但是我发现它有很多我不喜欢的配置。

我最喜欢的最后一种选择是使用AOP(您也可以使用Spring
Security或Shiro做到这一点)。创建一个类似的注释,@Private然后注释@Controller处理程序方法。您使用AOP来建议这些方法。该建议基本上检查某些会话或请求属性中的标志(已授权或未授权)。如果允许,则继续执行处理程序方法,否则,将抛出UnauthorizedException(或类似的)结果。然后,您还要@ExceptionHandler为该异常声明一个,在那里您可以完全控制响应的生成方式:(一个ModelAndView和相关的),a
ResponseEntity,使用注释处理程序@ResponseBody,直接编写响应等。我觉得您拥有更多的控制权, 如果你想要的话。