1. 程式人生 > >SpringMVC如何獲取登陸使用者資訊

SpringMVC如何獲取登陸使用者資訊

說到Java web,那麼在web端獲取登陸使用者資訊幾乎是所有系統都需要實現的功能,下面我們就來講一下幾種獲取登陸使用者資訊的方法。

從session中獲取

這是我們剛剛接觸Java web時就瞭解的一種解決方案,配置一個登陸過濾器或者登陸攔截器,在使用者登陸的時候將使用者資訊放到session中,然後控制層在session中取出使用者資訊,這是最簡單也是最常見的一種解決方案。

隨著使用者量的增大,單部署的服務往往不能滿足我們的需求,這時候往往我們會引入叢集來分攤伺服器的壓力。但是引入集群后,從session中存取使用者資訊就會變得不那麼靠譜了。

大家都知道session是存在服務端的,那麼如果使用者在叢集機器A中登陸,使用者資訊就儲存在了機器A上,此時如果使用者新的請求被分發到叢集機器B上,就無法獲取使用者資訊了。

這是叢集情況下使用者資訊被儲存在叢集中的單個伺服器上導致的,解決方案主要有兩種: - 讓使用者後續的所有請求都分發到使用者登陸的那臺機器 - 把使用者資訊儲存在快取中介軟體中,而不是叢集中的每個伺服器上

第一種方案可以在代理伺服器(apache,nginx等)上配置session黏著,這樣就能保證使用者後續請求都會被路由到同一臺機器上,這就解決了叢集情況下拿不到使用者資訊的問題了。

當然,對ip進行hash演算法使某一個使用者的請求永遠隨機到某一臺機器上也是一種思路。

從快取中介軟體獲取

從快取中介軟體獲取,就是把使用者資訊儲存在快取中介軟體中,控制層直接從快取中介軟體中獲取使用者資訊。

使用者在登陸成功後,會生成一個token,然後以token為鍵,使用者資訊為值儲存在快取中介軟體中,並且把token傳送到頁面端。

頁面端向服務端傳送請求的時候帶上這個token,服務端控制層可以根據這個token從快取中介軟體中獲取使用者資訊。

在叢集情況下,使用者資訊還是儲存在一個獨立出來的快取伺服器上,這樣就可以避免獲取不到使用者資訊的問題了。

以上兩種方法就是獲取登陸使用者資訊的兩種主要解決方案了,這裡再從程式碼層面上給出一個小分享。

不論是從session中獲取還是從快取中介軟體中獲取使用者資訊,不可避免的是在控制層需要顯式的去獲取使用者

User user = (User)session.get("login_user");

或者

User user = (User)redisService.get(token);

幾乎每個控制層方法都需要執行上面的語句獲取一下使用者資訊,著就會顯得很羅嗦,所以下面給出一個小分享來使用註解和HandlerMethodArgumentResolver結合優化控制層程式碼。

我們先來了解一下HandlerMethodArgumentResolver, SpringMvc中的HandlerAdapter會檢測所有的 HandlerMethodArgumentResolver,對引數進行處理和加工。

這樣,我們就可以自定義一個HandlerMethodArgumentResolver,讀取請求中的token,根據token獲取到使用者資訊,將使用者資訊通過自定義註解的方式傳遞到控制層。

沒錯,這個小分享是基於SpringMVC的,下面給出詳細程式碼。

  • 首先自定義一個註解,用來傳遞使用者資訊
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Logined {

}
  • 自定義一個HandlerMethodArgumentResolver,用來獲取使用者資訊
public class LoginedArgumentResolver implements HandlerMethodArgumentResolver {

    private RedisService<String, Object> redisService;

    public LoginedArgumentResolver(RedisService<String, Object> redisService) {
        this.redisService = redisService;
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        if (parameter.getParameterAnnotation(Logined.class) != null
                && parameter.getParameterType() == LoginUser.class) {
            // 如果該引數註解有@Logined才會執行下面的resolveArgument方法
            return true;
        }
        return false;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        String accessToken = request.getHeader(LocalConstant.ACCESS_TOKEN_KEY);
        LoginUser loginUser = (LoginUser)redisService.get(LocalConstant.REDIS_LOGIN_USER_KEY + accessToken);
        return loginUser;
    }
}
  • 控制層獲取使用者資訊
@GetMapping("/getLoginUser")
public LoginUser addCouple(@Logined LoginUser loginUser){
    return loginUser;
}

通過上面的Logined註解和LoginedArgumentResolver結合,在控制層中需要獲取使用者資訊,若需要在引數中新增@Logined就可以了,是不是簡單了很多呢,希望今天的分享可以給大家帶來幫助。