2015-08-03 192 views
3

我正在编写Spring REST类型接口到数据库的过程中,该数据库将检索各种资源的用户特定结果。Spring Rest Web请求范围

为了容纳用户,我有一个名为CurrentUser的弹簧@Component注释bean作为临时度量。

@Component 
public class CurrentUser { 

    @Autowired 
    private UserDAO userDAO;  

    private String userId; 
    private String email; 
    private String notes; 

    public String getUserId() { 
     return userId; 
    } 

    public void setUserId(String userId) throws ApiException { 
     User user = userDAO.getUser(userId) // Database call to 
     if (!user.isValid()) { 
      throw ApiException(...)  // The exception would crash back to the user as a error in the response 
     }  

     // Valud user so set these aspects. 
     this.userId = user.userId;  
     this.email = user.email; 
    } 
} 

在使用以下拦截器对API中的任何方法进行每次调用时,都会在Spring Interceptor中初始化此对象。

public class AuthenticationInterceptor extends HandlerInterceptorAdapter { 
    @Autowired 
    private CurrentUser user; 

    @Autowired 
    private RequestParameters requestParameters; 

    @Override 
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ApiException { 
     user.setUserId(StringUtils.defaultString(request.getParameter("userId"), "defaultUser")); 
     return true; 
    } 
} 

这只是一个识别用户的位置持有者,直到可以添加正确的身份验证。

我是比较新的春天,而这个职位 的原因是为了增加我的春天的线程安全方面的了解,情况是这样

我最近发现,春天不会自动线程安全,我可能需要更多地考虑范围。

我想了解的是以下几点:

  1. 对于上面的设置,有没有危险,同时提出请求的1000,可能会干扰和相互覆盖? 例如对于一个用户的请求可能会被不同的用户从单独的http请求覆盖,导致请求者接收到错误的数据。

  2. 什么是解决这个问题的最佳方法。 (即使它将被替换,我也有类似的方式实例化其他对象) 我正在看的选项(如果这是一个问题),设置原型范围或直接附加到请求/会话而不是允许他们拥有自动装配的对象。

任何人都可以给我的任何信息都会非常感谢,因为我一开始就想让它变得正确(呃),而不是稍后处理不好的假设。

+0

我们甚至有这种怀疑并有严格的批量测试,发现春天默认情况下不处理基于请求的数据。因此,在项目和服务层中启用基于事务的配置。 您也可以在请求范围中配置bean,理想情况下,它们具有将同步块放入方法中的类似效果。 – Jango

回答

2

答案1:是的,你不需要1000请求惹麻烦。 2个请求并行就足够了。

答2: 的主要问题是这里的划定范围之一:

春天的默认范围管理豆是单身。这意味着每个应用程序只有一个CurrentUser实例存在。

这显然不是你想要的。 (由于这里存在严重的安全问题,每个应用程序只有一个CurrentUser实例)。

简单的答案:

我可能会使用Spring Security ;-)

更简单的答案:

如果这不是一个选项:

  • 使用过滤代替HandlerInterceptor(更直接控制清理)

  • 创建一个线程本地来存储用户(并使用终端在过滤器中进行清理)并将其设置在过滤器中

  • 创建请求作用域服务(使用@ScopedProxy,以便能够将其连接到单身),访问ThreadLocal的作为UserService(您将需要一个接口,使其轻松地工作)

  • 自动装配这个UserService在你需要它

由于通过规范在Servlet环境中的每个请求绑定到一个线程,并且线程本地人本质上是线程安全的,你完全是线程安全的,并且它会很好地扩展。范围代理的开销很小。 (这只是一种选择,其他选项可以明确地使用请求范围,或者以稍微优雅的方式使用方面,但它是一种相当简单的方法,可以完成工作。会认真的建议看看Spring Security)。

+0

正是我在找的东西。实际的安全实施仍然在不断变化,因为它需要连接到SSO提供商。 (我仍然对此有所了解)。这个例子是项目中的几个spring beans之一,可能会遇到这样的问题,因此超出了安全本身,这个答案很合适。谢谢。 – JamesA

0

您可以使用spring mvc的参数解析器功能,而无需将其作为bean。 这样做,实现接口HandlerMethodArgumentResolver并将其注册到容器中。然后,你的处理方法可以有型当前用户

import org.springframework.core.MethodParameter; 
import org.springframework.web.bind.support.WebDataBinderFactory; 
import org.springframework.web.context.request.NativeWebRequest; 
import org.springframework.web.method.support.HandlerMethodArgumentResolver; 
import org.springframework.web.method.support.ModelAndViewContainer; 

public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver{ 

@Override 
public boolean supportsParameter(MethodParameter parameter) { 
    if (parameter.getParameterType().equals(CurrentUser.class)) { 
     return true; 
    } 
    return false; 
} 

@Override 
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, 
     WebDataBinderFactory binderFactory) throws Exception { 
    if (supportsParameter(parameter)) { 
     String userId = (String) webRequest.getAttribute("userId", NativeWebRequest.SCOPE_REQUEST); 
     return new CurrentUser(userId); 
    } 
    return null; 
} 

public class CurrentUser{ 

    public CurrentUser(String userId) { 
     // TODO Auto-generated constructor stub 
    } 

} 
} 

的论点之后,你可以拥有S型的处理方法

@RequestMapping 
public String handler(CurrentUser user){ 
.... 
}