1、Oauth 是一個關於受權(authorization)的開網絡標準(規範)前端
OAuth2: 解決的是不一樣的企業之間的登陸,本質是受權redis
要能訪問各類資源重點是要獲取令牌(token),但根據令牌的獲取方式不一樣,又會有四種受權方式spring
受權碼:這是最經常使用的一種方式,指的是第三方應用先申請一個受權碼,而後再用該碼獲取令牌,項目中用的就是這種後端
隱藏式:容許直接向前端頒發令牌。這種方式沒有受權碼這個中間步驟,因此稱爲(受權碼)"隱藏式"(implicit),通常應用於純前端項目跨域
密碼式:直接經過用戶名和密碼的方式申請令牌,這方式是最不安全的方式瀏覽器
憑證式:這種方式的令牌是針對第三方應用,而不是針對用戶的,既某個第三方應用的全部用戶共用一個令牌,通常用於沒有前端的命令行應用tomcat
受權碼受權流程:安全
第一步,A 網站提供一個連接,用戶點擊後就會跳轉到 B 網站(權限驗證系統)服務器
http://b.com/oauth/authorize?cookie
response_type=code&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
第二步,用戶跳轉後,B 網站若是沒有登陸會要求用戶登陸,而後詢問是否贊成給予 A 網站受權。用戶表示贊成,這時 B 網站就會跳回redirect_uri參數指定的網址,並附加受權碼code
http://a.com/callback?code=AUTHORIZATION_CODE
第三步,A 網站拿到受權碼之後,在後端,向 B 網站請求令牌。
http://b.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL
上面 URL 中,client_id參數和client_secret參數用來讓 B 確認 A 的身份(client_secret參數是保密的,所以只能在後端發請求),grant_type參數的值是AUTHORIZATION_CODE,表示採用的受權方式是受權碼,code參數是上一步拿到的受權碼,redirect_uri參數是令牌頒發後的回調網址。
第四步,B 網站收到請求之後,就會頒發令牌。具體作法是向redirect_uri指定的網址,發送一段 JSON 數據。
{
"access_token":"ACCESS_TOKEN",
"info":{...}
}
接下來用戶就能夠根據這個access_token來進行訪問了,
如A網站拿着token,申請獲取用戶信息,B網站確認令牌無誤,贊成向A網站開放資源。
2、單點: 是解決企業內部的一系列產品登陸問題,安全信任度要比oauth2高
(一)session-cookie機制
一、session-cookie機制出現的根源, http鏈接是無狀態的鏈接
-------- 同一瀏覽器向服務端發送屢次請求,服務器沒法識別,哪些請求是同一個瀏覽器發出的
二、爲了標識哪些請求是屬於同一我的 ---------- 須要在請求里加一個標識參數
方法1-----------直接在url里加一個標識參數(對前端開發有侵入性),如: token
方法2-----------http請求時,自動攜帶瀏覽器的cookie(對前端開發無知覺),如:jsessionid=XXXXXXX
三、瀏覽器標識在網絡上的傳輸,是明文的,不安全的
-----------安全措施:改https來保障
四、cookie的使用限制---依賴域名
-------------- 頂級域名下cookie,會被二級如下的域名請求,自動攜帶
-------------- 二級域名的cookie,不能攜帶被其它域名下的請求攜帶
五、在服務器後臺,經過解讀標識信息(token或jsessionid),來對應會話是哪一個session
--------------- 一個tomcat,被1000個用戶登錄,tomcat裏必定有1000個session -------》存儲格式map《sessionid,session對象》
--------------- 經過前端傳遞的jsessionid,來對應取的session ------ 動做發生時機request.getsession
(二)session共享方式,實現的單點登錄
一、多個應用共用同一個頂級域名,sessionid被種在頂級域名的cookie裏
二、後臺session經過redis實現共享(重寫httprequest、httpsession 或使用springsession框架),即每一個tomcat都在請求開始時,到redis查詢session;在請求返回時,將自身session對象存入redis
三、當請求到達服務器時,服務器直接解讀cookie中的sessionid,而後經過sessionid到redis中查找到對應會話session對象
四、後臺判斷請求是否已登錄,主要校驗session對象中,是否存在登錄用戶信息
五、整個校驗過程,經過filter過濾器來攔截切入,以下圖:
六、登錄成功時,後臺須要給頁面種cookie方法以下:
response裏,反映的種cookie效果以下:
七、爲了request.getsession時,自動能拿到redis中共享的session,
咱們須要重寫request的getsession方法(使用HttpServletRequestWrapper包裝原request)
(三)cas單點登錄方案
一、對於徹底不一樣域名的系統,cookie是沒法跨域名共享的
二、cas方案,直接啓用一個專業的用來登錄的域名(好比:cas.com)來供全部的系統登錄。
三、當業務系統(如b.com)被打開時,藉助cas系統來登錄,過程以下:
cas登錄的全過程:
(1)、b.com打開時,發現本身未登錄 ----》 因而跳轉到cas.com去登錄
(2)、cas.com登錄頁面被打開,用戶輸入賬戶/密碼登錄成功
(3)、cas.com登錄成功,種cookie到cas.com域名下 -----------》把sessionid放入後臺redis《ticket,sesssionid》---頁面跳回b.com
String ticket = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(ticket,request.getSession().getId(),20, TimeUnit.SECONDS);//必定要設置過時時間 CookieBasedSession.onNewSession(request,response); response.sendRedirect(user.getBackurl()+"?ticket="+ticket);
(4)、b.com從新被打開,發現仍然是未登錄,可是有了一個ticket值
(5)、b.com用ticket值,到redis裏查到sessionid,並作session同步 ------ 》種cookie給本身,頁面原地重跳
(6)、b.com打開本身頁面,此時有了cookie,後臺校驗登錄狀態,成功
(7)整個過程交互,列圖以下:
四、cas.com的登錄頁面被打開時,若是此時cas.com原本就是登錄狀態的,則自動返回生成ticket給業務系統
整個單點登錄的關鍵部位,是利用cas.com的cookie保持cas.com是登錄狀態,此後任何第三個系統跳入,都將自動完成登錄過程
5,本示例中,使用了redis來作cas的服務接口,請根據工做狀況,自行替換爲合適的服務接口(主要是根據sessionid來判斷用戶是否已登錄)
6,爲提升安全性,ticket應該使用過即做廢(本例中,會用有效期機制)
public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; MyRequestWrapper myRequestWrapper = new MyRequestWrapper(request,redisTemplate); //若是未登錄狀態,進入下面邏輯 String requestUrl = request.getServletPath(); if (!"/toLogin".equals(requestUrl) && !requestUrl.startsWith("/login") && !myRequestWrapper.isLogin()) { /** * ticket爲空,或無對應sessionid爲空 * --- 代表不是自動登錄請求--直接強制到登錄頁面 */ String ticket = request.getParameter("ticket"); if (null == ticket || null == redisTemplate.opsForValue().get(ticket)){ HttpServletResponse response = (HttpServletResponse)servletResponse; response.sendRedirect("http://cas.com:8090/toLogin?url="+request.getRequestURL().toString()); return ; } /** * 是自動登錄請求,則種cookie值進去---本次請求是302重定向 * 重定向後的下次請求,自帶本cookie,將直接是登錄狀態 */ myRequestWrapper.setSessionId((String) redisTemplate.opsForValue().get(ticket)); myRequestWrapper.createSession(); //種cookie CookieBasedSession.onNewSession(myRequestWrapper,(HttpServletResponse)servletResponse); //重定向自流轉一次,原地跳轉重向一次 HttpServletResponse response = (HttpServletResponse)servletResponse; response.sendRedirect(request.getRequestURL().toString()); return; } try { filterChain.doFilter(myRequestWrapper,servletResponse); } finally { myRequestWrapper.commitSession(); } }
public static void onNewSession(HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(); String sessionId = session.getId(); Cookie cookie = new Cookie(COOKIE_NAME_SESSION, sessionId); cookie.setHttpOnly(true); cookie.setPath(request.getContextPath() + "/"); cookie.setMaxAge(Integer.MAX_VALUE); response.addCookie(cookie); }