2015-08-03 341 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){ 
.... 
}