Java MVC 拦截器 vs Spring 安全过滤器 vs 其他东西......?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19291843/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 15:45:04  来源:igfitidea点击:

MVC interceptor vs Spring security filter vs something else...?

javaspring-mvcfilterspring-securityinterceptor

提问by Less

I'm using Spring-MVC with Spring Security for my web application. It includes user registration pages and private user panel. I have it set up currently with the following URL patterns:

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

  • whatever/myapp/loginuser log in
  • whatever/myapp/register?step=1start registration
  • whatever/myapp/account/**private area views (pages)
  • whatever/myapp/pendingview shown while post-registration processes complete
  • whatever/myapp/blockedaccount blocked view
  • whatever/myapp/register/retryif registration failed, allow retry
  • whatever/myapp/login用户登录
  • whatever/myapp/register?step=1开始注册
  • whatever/myapp/account/**私人区域浏览量(页面)
  • whatever/myapp/pending注册后流程完成时显示的视图
  • whatever/myapp/blocked帐户被阻止查看
  • whatever/myapp/register/retry如果注册失败,允许重试

Essentially, these URLs below should require user authentication, i.e. require log-in:

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

  • whatever/myapp/account/**(private area pages)
  • whatever/myapp/pending(this page has a timer set to redirect to /account/home)
  • whatever/myapp/register/retry
  • whatever/myapp/account/**(私人区域页面)
  • whatever/myapp/pending(此页面有一个计时器设置为重定向到 /account/home)
  • whatever/myapp/register/retry

This is quite straightforward to achieve using Spring security. However, regardless of user authentication through Spring security, private area pages should be accessible or not, depending on user's current account status (stored in my DB).

使用 Spring 安全性实现这一点非常简单。但是,无论用户是否通过 Spring security 进行身份验证,私人区域页面都应该可以访问或不可访问,这取决于用户当前的帐户状态(存储在我的数据库中)。

More specifically: if a user tries to access anything in the private area (/account/**), he should be shown the appropriate view (redirected to appropriate page), according to the status. I have these statuses defined:

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

  • suspended- relates to pending view
  • enabled- allow full access
  • disabled- not relevant here
  • retry_allowed- relates to retry view
  • blocked- relates to account-blocked view
  • suspended- 与待定视图有关
  • enabled- 允许完全访问
  • disabled- 这里不相关
  • retry_allowed- 与重试视图有关
  • blocked- 与帐户被阻止的视图有关

Currently, I have a MVC interceptor setup to /account/**, that checks user status, and redirects to appropriate pages, but somehow I get the sense that this is not really the ideal or appropriate solution here, since I'm facing strange behavior, like multiple controller invocation... and also I'm not quite certain when to return true/ falsewithin preHandle()method. Here's the code snippet from the interceptor:

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

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

.

.

Is this a valid approach ? Any alternative suggestions ?

这是一种有效的方法吗?任何替代建议?

采纳答案by Sotirios Delimanolis

All options are valid, it depends on the level of abstraction you want.

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

In a Filter, you only have access to HttpServletRequestand HttpServletResponseobjects, so you are very much coupled with the ServletAPI. You also don't (directly) have access to all the great Spring functionality like returning a view to be rendered or a ResponseEntity.

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

In a HandlerInterceptor, it's again more of the same. You can do your redirection or request handling directly in the preHandle()where you don't have access to the ModelAndViewor set a flag which you check in postHandle(). You would have access to the ModelAndViewbut not to some other Spring MVC functionality.

在 a 中HandlerInterceptor,它再次变得更加相同。您可以直接在preHandle()您无权访问ModelAndView或设置您签入的标志的地方进行重定向或请求处理postHandle()。您可以访问ModelAndView但不能访问其他一些 Spring MVC 功能。

Spring Security is a good alternative, but I find it has a lot of configuration that I don't like too much.

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

One final alternative, that I like the most, is to use AOP (you can do this with Spring Security or Shiro as well). You create an annotation like @Privateand you annotate your @Controllerhandler methods. You use AOP to advise these methods. The advice basically checks some session or request attribute for a flag (authorized or not). If you are allowed, you continue executing the handler method, if not, you throw an UnauthorizedException(or similar). You then also declare an @ExceptionHandlerfor that exception where you have pretty much complete control over how the response is generated: a ModelAndView(and related), a ResponseEntity, annotate the handler with @ResponseBody, write the response directly, etc. I feel like you have much more control, if you want it.

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