MVC 拦截器与 Spring 安全过滤器与其他东西……?

Posted

技术标签:

【中文标题】MVC 拦截器与 Spring 安全过滤器与其他东西……?【英文标题】:MVC interceptor vs Spring security filter vs something else...? 【发布时间】:2013-10-17 23:33:27 【问题描述】:

我正在为我的 Web 应用程序使用带有 Spring Security 的 Spring-MVC。它包括用户注册页面和私人用户面板。我目前使用以下 URL 模式设置它:

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 - 与帐户屏蔽视图有关

目前,我有一个设置为/account/** 的 MVC 拦截器,它检查用户状态,并重定向到适当的页面,但不知何故,我觉得这不是真正理想或适当的解决方案,因为我面临奇怪的行为,比如多个控制器调用......而且我不太确定何时在preHandle() 方法中返回true / false。这是拦截器的代码 sn-p:

@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;

这是一种有效的方法吗?有什么替代建议吗?

【问题讨论】:

【参考方案1】:

所有选项都有效,这取决于您想要的抽象级别。

Filter 中,您只能访问HttpServletRequestHttpServletResponse 对象,因此您与Servlet API 非常耦合。您也无法(直接)访问所有出色的 Spring 功能,例如返回要呈现的视图或 ResponseEntity

HandlerInterceptor 中,又是相同的。您可以直接在您无权访问ModelAndViewpreHandle() 中进行重定向或请求处理,或者设置您在postHandle() 中签入的标志。您可以访问ModelAndView,但不能访问其他一些 Spring MVC 功能。

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

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

【讨论】:

以上是关于MVC 拦截器与 Spring 安全过滤器与其他东西……?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Web源码之核心组件(拦截器与异常处理)

Spring Web源码之核心组件(拦截器与异常处理)

Spring Web源码之核心组件(拦截器与异常处理)

Spring MVC 拦截器

Spring MVC 拦截器

干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结