流控、認證、審計、授權以上都做了初步的簡單的實現。
之前寫的代碼,base64加密了用戶名和密碼。
缺點1:每次請求都要帶用戶名密碼 增加了泄露的風險。
每次傳上來用戶名和密碼都要check驗證。check是個非常耗資源的事
基於token的身份認證
token是有實效的
對於java開發,最常見的實現
代碼實現來驗證請求的流程
userController內增加login登陸的方法。
@GetMapping("/login") public void login(@Validated UserInfo user,HttpServletRequest request){ UserInfo info = userService.login(user); request.getSession().setAttribute("user",user); }
userService內增加login方法
UserInfo login(UserInfo user);
實現類實現login的方法
@Override public UserInfo login(UserInfo info) { UserInfo result=null; User user=userRepository.findByUsername(info.getUsername()); if(user!=null && SCryptUtil.check(info.getPassword(),user.getPassword())){ result=user.buildInfo(); } return result; }
因為之前做的授權的攔截器,把所有的請求都給攔截了。修改登陸時候我們不需要攔截,
定義一個數組,把不需要攔截的url放在數組內。
請求的路徑如果不在我們的數組內就執行原來訪問控制的邏輯。
如果是登陸的路徑的話,不判斷Attribute是否存在user了,也不判斷請求的方法是否有權限了。直接返回true
private String[] permitUrls=new String[] {"/users/login"}; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(4); boolean result=true; if(!ArrayUtils.contains(permitUrls,request.getRequestURI())){ User user=(User)request.getAttribute("user"); if (user==null){ response.setContentType("text/plain"); response.getWriter().write("need authentication"); response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; }else{ String method = request.getMethod(); if(!user.hasPermission(method)){ response.setContentType("text/plain"); response.getWriter().write("forbidden"); response.setStatus(HttpStatus.FORBIDDEN.value()); return false; } } } return result; }
啟動服務進行測試
先調用獲取用戶信息的服務,返回了401,沒有帶身份認證信息。
http://localhost:8080/users/13
下面來調用一下login的方法,傳入用戶名和密碼
這個方法返回了200.里面的Reponse Headers里面。Set-Cookies里面寫的JSESSIONID
類似於下面這種:
在chrome的設置里面
這就是我們存起來的 服務器返回的token
session有了信息了 再去訪問
我們可以看發出去的請求里面 Cookie里面有JSessionID,剛才瀏覽器里面存的JSessionID就從這里發出去了。
也就是這里的第四部,第五步
以上就是session的原理。你要明白的是,基於cookie、session的本質上仍然還是基於token的認證方式。只不過他的這種認證方式是servlet規范里面已經都實現好了的。有優點也有缺點,客戶端只存一個串,真的信息是存在服務器端的,另外一個優點就是使用起來很方便,因為所有的東西servlet容器都替你實現好了,你啥都不用管了,代碼里只需要getSession()然后往里面放東西就可以了。驗證的時候只需要從里面拿,然后驗證就可以了。使用起來非常的方便
缺點:只針對瀏覽器起作用,因為我返回的Repoonse Header,瀏覽器收到這個Repoonse Header然后自個去設置cookie這件事只有瀏覽器支持。如果你是一個客戶或者app 甚至一個第三方的應用,你是走不了這個機制的。 當服務器向瀏覽器傳輸cookie的時候,很容易被劫持。並不是絕對安全的。在大型的項目中,我們的servlet容器。服務器往往不是一台,如果一個用戶的信息落在第一台服務,那么后面請求在訪問的是第二台服務器。那么后面的就訪問不到這個用戶信息了。然后又要重新登陸。Spring 有一個Spring Session外部的存儲來當你的Token Store來存session信息。以前沒有Spring Session 要做這個事情還是挺麻煩的,要在好多個tomcat之間同步session信息,然后保持他們的一致性,
所以出現了另外一種自己實現來基於token 的登陸
基於cookie和sesison常見的風險。也就是一些攻擊方式,以及怎么去防護這些攻擊