问题描述
我使用Spring Security的Spring-MVC为我的Web应用程序。它包括用户注册页面和私人用户面板。我已经使用以下URL模式设置:- whatever / myapp / login 用户登录
- 不管/ myapp / register?step = 1 开始注册
- whatever / myapp / account / ** private area views(pages)
- whatever / myapp / pending
- 无论/ myapp /已屏蔽帐户被阻止的视图 >
- whatever / myapp / register / retry 如果注册失败,允许重试
基本上,下面这些网址应该要求用户身份验证,即需要登录:
- any / myapp / account / ** (私人区域页面)
- whatever / myapp / pending (这个页面的定时器设置为重定向到/ account / home)
- whatever / myapp / register / retry
对于一个非常简单的例子使用Spring安全性。但是,不管通过Spring安全性进行用户身份验证,根据用户的当前帐户状态(存储在我的数据库中),私人区域页面应该可访问或不可访问。更具体地说:如果用户试图访问私有区域( / account / ** )中的任何内容,他应该显示适当的观点(重定向到适当的页面),根据状态。我定义了这些状态:
- 已挂起 - 与待定视图相关
- 已启用 - 允许完全访问
- 已停用 - 与此无关
- retry_allowed - 与重试视图相关
- 阻止 - 与帐户阻止的视图相关
目前,我有一个MVC拦截器设置 / account / ** ,检查用户状态,并重定向到适当的页面,但不知何故,我觉得这不是真正的理想或适当的解决方案, m面临奇怪的行为,像多个控制器调用...还我不太确定何时返回 true / false 在 preHandle()方法中。这里是拦截器的代码片段:
@Override
public boolean preHandle(
HttpServletRequest请求,
HttpServletResponse响应
Object arg2)
抛出异常{
IPanelUser pUser =(IPanelUser)SecurityContextHolder.getContext()
.getAuthentication()。getPrincipal );
//首先检查委托人,然后从DB
加载//暂停后的初始状态
if(pUser.getCustomer()。getStatus()== CustomerStatus .Suspended.getCode()){
//如果挂起,从数据库加载并更新状态
Customer customer = this.customerService.getUserByUsername(pUser.getUsername());
if(customer!= null)
pUser.getCustomer()。setStatus(customer.getStatus());
//仍然被暂停?重定向到挂起
if(pUser.getCustomer()。getStatus()== CustomerStatus.Suspended.getCode()){
response.sendRedirect(../ pending);
返回false;
$ b $ if(pUser.getCustomer()。getStatus()== CustomerStatus.Blocked.getCode()){
//重定向到阻止页面
response.sendRedirect(../ blocked);
SecurityContextHolder.clearContext();
返回false;
if(pUser.getCustomer()。getStatus()== CustomerStatus.AllowRetry.getCode()){
//重定向到CC提交页面
response.sendRedirect(../ register / retry);
返回false; $()
if(pUser.getCustomer()。getStatus()== CustomerStatus.Enabled.getCode()||
pUser.getCustomer()。getStatus()== CustomerStatus.Disabled.getCode()){
//什么也不做
}
return true;
}
。
这是一个有效的方法吗?任何替代建议?
所有选项都是有效的,这取决于你想要的抽象级别。
$ Filter ,您只能访问 HttpServletRequest 和 HttpServletResponse 对象,所以你非常配合 Servlet API。你也不能(直接)访问所有伟大的Spring功能,比如返回要呈现的视图或 ResponseEntity 。
在一个 HandlerInterceptor 中,它又是相同的。您可以直接在 preHandle()中执行重定向或请求处理,而您无权访问 ModelAndView 或设置一个标志,你检查 postHandle()。您可以访问 ModelAndView ,但不能访问其他Spring MVC功能。
Spring Security是一个很好的选择,但是我发现它有很多配置,我不太喜欢。
我最喜欢的另一个选择是使用AOP(您可以使用Spring Security或Shiro来完成这项工作)。您可以创建一个注释,如 @Private ,并且注释您的 @Controller 处理程序方法。您使用AOP来建议这些方法。建议基本上检查某个会话或请求属性的标志(授权与否)。如果你被允许,你继续执行处理程序的方法,如果没有,你会抛出 UnauthorizedException (或类似的)。然后,您还可以声明一个,您可以完全控制响应的生成方式: ModelAndView 相关的),一个 ResponseEntity ,用 @ResponseBody 注解处理程序,直接写回应,等等。有更多的控制权,如果你想要它。
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:
- whatever/myapp/login user log in
- whatever/myapp/register?step=1 start registration
- whatever/myapp/account/** private area views (pages)
- whatever/myapp/pending view shown while post-registration processes complete
- whatever/myapp/blocked account blocked view
- whatever/myapp/register/retry if registration failed, allow retry
Essentially, these URLs below should require user authentication, i.e. require log-in:
- whatever/myapp/account/** (private area pages)
- whatever/myapp/pending (this page has a timer set to redirect to /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).
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:
- 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
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 / false within preHandle() method. Here's the code snippet from the interceptor:
@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 ?
All options are valid, it depends on the level of abstraction you want.
In a Filter, you only have access to HttpServletRequest and HttpServletResponse objects, so you are very much coupled with the Servlet API. You also don't (directly) have access to all the great Spring functionality like returning a view to be rendered or a 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 ModelAndView or set a flag which you check in postHandle(). You would have access to the ModelAndView but not to some other Spring MVC functionality.
Spring Security is a good alternative, but I find it has a lot of configuration that I don't like too much.
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 @Private and you annotate your @Controller handler 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 @ExceptionHandler for 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.
这篇关于MVC拦截器与Spring安全筛选器vs其他...?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!