基於session和token的身份認證方案


一、基於session的身份認證方案

1.方案圖示

2.比較通用的鑒權流程實現如下:

在整個流程中有兩個攔截器。
第一個攔截器 AuthInteceptor是為了每一次的請求的時候都先去session中取user對象,如果session中有,就放user對象到threadlocal中。這是為了業務處理的時候能直接獲取用戶對象。
第二個攔截器 AuthActionInteceptor是先看頁面是否需要登錄,如果不需要登錄,則直接進行業務邏輯處理;如果需要登錄,則判斷是否已經登錄(UserContext.getUser()是否為空),沒有登錄則重定向到登錄頁面,登錄后是會把用戶信息放到session當中的 。 
@Component
public class AuthInterceptor implements HandlerInterceptor{

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        
        Map<String, String[]> map = request.getParameterMap();
        map.forEach((k,v) -> {
            if (k.equals("errorMsg") || k.equals("successMsg") || k.equals("target")) {
                request.setAttribute(k, Joiner.on(",").join(v));
            }
        });
        String reqUri =    request.getRequestURI();
        if (reqUri.startsWith("/static") || reqUri.startsWith("/error") ) {
            return true;
        }
        HttpSession session = request.getSession(true);//參數為true,沒有session則創建新的
        User user =  (User)session.getAttribute(CommonConstants.USER_ATTRIBUTE);
        if (user != null) {
            UserContext.setUser(user);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        UserContext.remove();
    }
    

}
View Code
@Component
public class AuthActionInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        User user = UserContext.getUser();
        if (user == null) {
            String msg = URLEncoder.encode("請先登錄","utf-8");
            String target = URLEncoder.encode(request.getRequestURL().toString(),"utf-8");
            if ("GET".equalsIgnoreCase(request.getMethod())) {
                response.sendRedirect("/accounts/signin?errorMsg=" + msg + "&target="+target);
                return false;//修復bug,未登錄要返回false
            }else {
                response.sendRedirect("/accounts/signin?errorMsg="+msg);
                return false;//修復bug,未登錄要返回false
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

    }

}
View Code
@Configuration
public class WebMvcConf extends WebMvcConfigurerAdapter {

    @Autowired
    private AuthActionInterceptor authActionInterceptor;
    
    @Autowired
    private AuthInterceptor authInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry){
         registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns("/static");
            registry
                .addInterceptor(authActionInterceptor).addPathPatterns("/house/toAdd")
                .addPathPatterns("/accounts/profile").addPathPatterns("/accounts/profileSubmit")
                .addPathPatterns("/house/bookmarked").addPathPatterns("/house/del")
                .addPathPatterns("/house/ownlist").addPathPatterns("/house/add")
                .addPathPatterns("/house/toAdd").addPathPatterns("/agency/agentMsg")
                .addPathPatterns("/comment/leaveComment").addPathPatterns("/comment/leaveBlogComment");
            super.addInterceptors(registry);
    }

    
}
View Code
為什么要分成兩個攔截器呢?
第一個攔截器是為了攔截除靜態資源以外的任何資源,並且從session中取user放到ThreadLocal中,至於為什么要這么做,是可以在后面的業務處理邏輯中都可以方便使用到它,具體請參考 ThreadLocal管理session
第二個攔截器才是對於需要進行訪問控制的資源進行判斷處理。
最后業務邏輯處理完后,都需要將user從threadlocal中移除,以免影響以后的請求,因為我們的請求線程是處在線程池中的,每個線程在下一個請求中都可以復用。
 
3.缺點
  1).sessio需要存在服務器內存中,這樣就不能跨實例共享。當下一次請求被分發到另一個實例的時候,就會造成重新登錄。
  2).在高並發情況下,session放在內存中受限於內存的大小
  3).session依賴於瀏覽器的cookie機制,對於移動客戶端就很難支持。移動端使用token。

 

二、基於token的身份認證方案

優點:

  1.token方案保證了服務的無狀態,所有的信息都是存在分布式緩存中。基於分布式存儲,這樣可以水平擴展來支持高並發。
  2.不依賴與cookie機制,可以通過客戶端約定的協議來傳輸token,瀏覽器可以存在cookie中,手機端可以放在內存或者本地文件中,代價是增加了外部存儲依賴以及在代碼方面要復雜。
 
注:
Session 是一種HTTP存儲機制,目的是為無狀態的HTTP提供的持久機制。所謂 Session 認證只是簡單的把 User 信息存儲到 Session 里,因為 SID 的不可預測性,暫且認為是安全的。這是一種認證手段。 
 Token,指的是 OAuth Token 或類似的機制的話,提供的是 認證 和 授權 ,認證是針對用戶,授權是針對 App 。其目的是讓 某App 有權利訪問 某用戶 的信息。
這里的 Token 是唯一的。不可以轉移到其它 App 上,也不可以轉到其它 用戶 上。 
 
下一篇將詳細介紹基於JWT的身份認證方案


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM