文章目錄
任何的流程必須通過語言能夠描述,才達到最終效果
認證和授權總流程
AnonymousAuthenticationFilter在所有認證過濾器最後,判斷SpringSecurityConetxt中是否存在Authentication,若不存在則設置一個AnonymousAuthentication。
認證技術技術選型
企業中大多使用Shiro框架和Spring Security進行用戶身份認證,而實現SSO的技術選型又有Spring Security Oauth2.0和CAS,它們都可以很好的實現單點登錄,我對Oauth2.0比較推介,因爲它不僅在現有框架Spring Security下進行很好的實現,而且與其他技術的兼容性比較好,最重要的是Spring Cloud對它有現成的實現,搭建微服務認證中心非常合適。而CAS代碼實現是是通過一些零散的過濾器實現,而且對前後端分離不好實現。
CAS對各個子系統的資源請求進行認證處理,類似於Oauth2.0基於授權碼的授權模式,客戶端應用不接觸用戶的賬號密碼信息,只有認證中心能接受用戶的用戶名密碼等安全信息,其他系統不提供登錄入口,只接受認證中心的間接授權。而Oauth2.0提供的賬號密碼授權模式客戶端應用是接觸到用戶的安全信息的,但只適合對APP的認證處理。當用戶再次請求時,它們都需要通過認證中心驗證令牌的有效性,但是基於JWT的oauth2.0協議可以在令牌中取出用戶信息即可,省去了再次驗證信息
具體來講,有單機版的認證,多機版的認證,基於微服務的認證。
單機版的認證可以通過框架直接完成
多機版的認證可以通過Spring Security整合Oauth2.0協議,也可以整合CAS來實現,也可使用CAS單獨實現
微服務的認證其實就是基於網關的認證和授權,所有對微服務的請求都通過網關進行細粒度的控制,通過認證中心進行實際的認證和授權,網關除了解決認證和授權,還進行日誌收集和熔斷限流
cas基本流程
https://blog.csdn.net/anumbrella/category_7765386.html
首次登陸流程
- 首先用戶訪問系統1受保護的資源,系統1發現未登陸,跳轉至SSO認證中心
- SSO認證中心發現用戶未登錄,將用戶引導至登錄頁面
- 用戶輸入用戶名和密碼提交至SSO認證中心
- SSO認證中心校驗用戶信息,創建用戶與SSO認證中心之間的會全局會話,同時創建授權令牌
- SSO認證中心帶着令牌跳轉回最初的請求地址(系統1)
- 系統1拿到令牌,去SSO認證中心校驗令牌是否有效
- SSO認證中心校驗令牌,返回有效,註冊系統1的地址
- 系統1使用該令牌創建與用戶的會話,稱爲局部會話,返回給用戶受保護資源
再次登陸流程
- 用戶訪問系統2受保護的資源
- 系統2發現用戶未登錄,跳轉至SSO認證中心,並將自己的地址作爲參數傳遞過去
- SSO認證中心發現用戶已登錄,跳轉回系統2的地址,並附上令牌
- 系統2拿到令牌,去SSO認證中心校驗令牌是否有效
- SSO認證中心校驗令牌,返回有效,註冊系統2地址
- 系統2使用該令牌創建與用戶的局部會話,返回給用戶受保護資源
註銷流程
- 系統1根據用戶與系統1建立的會話id拿到令牌,向SSO認證中心發起註銷請求
- SSO認證中心校驗令牌有效,銷燬全局會話,同時取出所有用此令牌註冊的系統地址
- SSO認證中心向所有註冊系統發起註銷請求
- 各註冊系統接收SSO認證中心的註銷請求,銷燬局部會話
- SSO認證中心引導用戶至登錄頁面
TGC:用戶持有的令牌,表示成功登陸casClient web應用
TGT:CAS爲用戶簽發的全局令牌,構建全局會話
ST(TGT簽發):TGT簽發的某服務的令牌,構建局部會話
ST是TGT簽發的。用戶在CAS上認證成功後生成TGT,然後用TGT簽發一個ST,ST包含TGT屬性,然後redirect到客戶端應用
引入app的代理,爲用戶去申請PT,PT代表用戶成功登陸casClient app應用
a) PGT(ST簽發):代理服務持有的令牌,表示用戶成功登陸某一代理服務,可以代理應用爲其申請PT
b) PT:CAS爲用戶簽發的訪問app服務的令牌,表示成功登陸casClient app應用
cas實現相關
如果要驗證其他信息,比如郵箱,手機號,但是郵箱,手機信息在另一個數據庫,還有在一段時間內同一IP輸入錯誤次數限制等。這裏就需要我們自定義認證策略,自定義CAS的web認證流程
通過攔截請求獲取到Handler,來實現自定義認證策略
自定義登陸邏輯
- 通過繼承AbstractUsernamePasswordAuthenticationHandler實現用戶名密碼登陸,或者AbstractPreAndPostProcessingAuthenticationHandler實現其他數據的處理邏輯
- 通過繼承AuthenticationEventExecutionPlanConfigurer註冊自定義的認證邏輯
cas service配置
- 配置內容主要是:服務訪問配置,服務屬性配置,服務到期策略配置
- 配置存儲主要是:JSON file,Redis,JPA
cas service管理
- 啓用線程項目cas-management-overlay進行服務管理
- 自定義服務管理程序,通過實現ServiceManager接口
自定義用戶登陸界面
認證登陸頁面耦合於cas server
- 根據配置的cas service存儲方式,覆蓋默認的登陸界面
- 自定義驗證碼,使用Google的kaptcha類
SpringBoot配置cas client
- 配置攔截請求和退出的路徑,並注入自定義配置類
- 注入FilterRegistrationBean,然後通過它配置登陸認證過濾器,登出過濾器,ticket效驗過濾器,獲取登陸信息
SpringSecurity基本流程
認證流程
認證請求到達SecurityContextPersistenceFilter檢查是否有session,若有則直接返回登陸成功,否則進入UsernamePasswordFilter進行賬號密碼認證,它底層通過AuthenticationManager遍歷可用的AuthentcationProvider進行具體的認證操作,底層通過UserDetailsService根據用戶名稱獲取用戶信息與表單填入的進行匹配,若認證成功則將用戶信息放SecurityContextHolder
實現remember功能
UsernamePasswordAuthenticationFilter認證成功後,在成功處理器就會自動調用RememberMeService的方法創建token和用戶名一起存儲到數據庫中,然後將token寫到瀏覽器的Cookie中。
當用戶再次訪問系統時,被RememberMeAuthenticationFilter過濾器所攔截,它讀取cookie中的token,通過RememberService從數據庫中查出token對應的用戶名,然後通過UserDetailService再根據用戶名查詢用戶信息,最後放到SecuritContext裏。
圖形驗證碼效驗
- 自定義控制器生成圖形驗證碼,並生成圖片返回前端,然後將其保存到redis和session中
- 創建驗證碼過濾器,判斷用戶填寫的驗證碼和系統生成的驗證碼是否匹配,若不匹配則拋異常
短信驗證碼登陸效驗
- 自定義控制器生成短信驗證碼,將其發送給用戶,將驗證碼保存到redis或者session中
- 自定義過濾器攔截短信登陸請求,然後自定義AuthenticationProvider返回已認證Authentication
- 自定義短信驗證碼過濾器,判斷用戶填寫的短信驗證碼和存儲中的短信驗證嗎是否匹配,若不匹配則拋異常
|——爲何不在provider進行匹配短信驗證碼呢??爲了使的驗證短信驗證碼這個流程可以複用,比如讓支付流程調用等
|——有兩條線,一條是根據手機號進行身份認證(只保證系統中存在此用戶),另一條是驗證短信驗證碼是否匹配的邏輯
使用SpringSocial實現第三方登陸
- 用戶訪問客戶端前端,瀏覽器將請求導向認證服務器進行認證
- 用戶同意授權,攜帶授權碼返回給客戶端前端
- 客戶端後臺攜帶授權碼申請令牌
- 認證服務器發放令牌,客戶端後臺訪問服務提供商的用戶數據,攜帶令牌返回客戶端前端
Oauth2.0實現認證服務器
相比CAS,第三方
- Oauth生成令牌流程(認證服務器)
客戶端應用向認證服務器申請令牌,TokenEndpoint通過ClientDetailsService從存儲中獲取註冊的第三方應用信息,然後將第三方用戶信息和用戶請求信息封裝爲Oauth2Authentication,AuthorizationServerTokenService創建Oatuth2AccessToken令牌,然後TokenStore將其存到某存儲器中 - Oauth效驗令牌流程(資源服務器)
客戶端攜帶令牌訪問請求到達Oauth2AuthenticationProcessFilter,tokenExtractor從request中獲取Authentication,OAuth2AuthenticationManager通過tokenService查詢token對應的OAuth2Authentication對應,若登陸成功將Authentication放到SpringSecurityConetxt中。
Oauth2.0重構用戶名密碼登陸
在成功處理器中,封裝Oauth2Authentication,然後AuthorizationServerTokenService創建Oatuth2AccessToken令牌,然後TokenStore將其存到某存儲器中
基於Oauth2.0 jwt實現SSO
- 創建認證服務器,配置認證服務器客戶端信息,配置token存儲策略,配置tokenkey需要身份認證
- 創建rest服務器1,@EnableOauth2Sso,配置客戶端配置(客戶端信息,訪問認證服務器url,請求令牌url,獲取密鑰url)
- 創建rest服務器2,@EnableOauth2Sso,配置客戶端配置(客戶端信息,訪問認證服務器url,請求令牌url,獲取密鑰url)
Session管理
- session超時處理:server.session.timeout= 最少一分鐘,可配置失效處理控制器
- session併發控制
用戶在其他瀏覽器登陸,踢掉前面那個:maximumSession(1)
用戶不允許在其他地方登陸:maxSessionPreventLogin(true) - 集羣session處理
spring.session.store-type=REDIS - 退出登陸
訪問/logout後,當前session失效,清空remember-me記錄,清空securityConetxt,重定向到登錄頁
logoutUrl()配置退出控制器,logoutSuccessHandler()配置退出重定向url,deleteCookies()刪除瀏覽器cookies
授權
系統配置信息:權限規則基本不變,antMatchers(方法,路由).hasRole(角色)
用戶權限信息 :權限規則靈活變化
SpringSecurity授權源碼流程
請求信息vs系統配置權限vs用戶擁有的權限——>AccessDecisionManager(策略)——>AccessDecisionVoter(WebExpressionVoter)多個投票者進行投票
SpringSecurity將權限轉換成一個權限表達式,然後通過WebExpressionVoter進行評估,只要有一個通過就通過
連用表達式
基於配置文件進行權限控制
- 只區分是否登錄or只區分簡單角色
- 權限規則基本不變
問題:在使用配置文件配置權限時,安全模塊事先並不知道業務模塊具體有哪些API需要權限控制,怎麼將配置信息進行全局管理呢???
AuthorizeConfigManager收集所有AuthorizeConfigProvider的實現,然後進行統一配置
基於角色的權限控制
角色衆多,隨着公司發展權限不斷變化
資源表(開發人員維護):菜單,按鈕及其URL
用戶表,角色表,用戶-角色表,角色-權限表
.access("@rbacService.hasPermission(request,authentication)")
.access("#rbacService.hasPermission(request,authentication)") //需要配置相應的權限表達式處理器
怎樣對接自己寫的權限模塊 和 SpringSecurity
a)定義一個RbacService接口,根據request和Authentication判斷是否有權限
b)實現授權服務方法,根據用戶名查詢所擁有權限的所有URL,只要有一個匹配request.getRequestURI()就判定權限通過
c)實現AuthorizeConfigProvider,將自己自定義的權限服務,以表達式的方式配置進去
d) 實現一個AuthorizeConfigManager,注入所有模塊的AuthorizeConfigProvider,然後將授權配置管理器配置到SecurityCofig默認配置