springboot+springsecurity+cas實現sso,並開啟註解方法級別的許可權控制
springsecurity整合cas
springsecurity的springsecurityfilterchian中有一個cas的攔截器位置,因此可以通過它把cas整合進springsecurity中 cas負責認證,security通過userDetails負責載入許可權,通過認證管理器賦予許可權
所以在springsecurity的configeration沒有太多變化,變化的地方如下: 第一:AuthenticationManager認證管理器,需要更換成casAuthenticationProvider物件。cas提供的認證管理器,把認證管理工作交給cas 第二:認證的entypiont認證入口點需要更換成cas提供的casAuthenticationFilter,該物件會攔截請求,進而呼叫cas提供的falter進行認證處理
如下配置security的configuration public class CasWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired private CasAuthenticationEntryPoint casAuthenticationEntryPoint; @Autowired private CasAuthenticationProvider casAuthenticationProvider; @Autowired private CasAuthenticationFilter casAuthenticationFilter; @Autowired private LogoutFilter logoutFilter; @Autowired private CasServerProperties casServerProperties; @Override protected void configure(HttpSecurity http) throws Exception { http.headers().frameOptions().disable(); http.csrf().disable(); http.authorizeRequests() .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() .antMatchers("/static/**").permitAll() // 不攔截靜態資源 .antMatchers("/api/**").permitAll() // 不攔截對外API .antMatchers("/index").permitAll() // "/index"路徑可以匿名訪問 .anyRequest().authenticated(); // 所有資源都需要登陸後才可以訪問。 http.logout().permitAll(); // 不攔截登出 http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint); // 單點登出的過濾器,必須配置在SpringSecurity的過濾器鏈中,如果直接配置在Web容器中,貌似是不起作用的。我自己的是不起作用的。 SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter(); singleSignOutFilter.setCasServerUrlPrefix(casServerProperties.getCasServerUrlPrefix()); http.addFilter(casAuthenticationFilter) .addFilterBefore(logoutFilter, LogoutFilter.class) .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class); http.antMatcher("/**"); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(casAuthenticationProvider); } @Bean public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener(){ ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>(); servletListenerRegistrationBean.setListener(new SingleSignOutHttpSessionListener()); return servletListenerRegistrationBean; }
}
casAuthenticationProvider實現了AuthenticationProvider介面,所以可以作為認證管理器提供者載入到security中
那麼接下來就需要配置cas中的認證功能,也就是casAuthenticationFilter物件,由於casAuthenticationFilter物件涉及較多的依賴因此具體配置如下 /** ** cas與sercurity整合,需要配置的物件 *
-
在cas與security整合中, 首先需要做的是將應用的登入認證入口改為使用CasAuthenticationEntryPoint。
-
所以首先我們需要配置一個CasAuthenticationEntryPoint對應的bean,
-
然後指定需要進行登入認證時使用該AuthenticationEntryPoint。
-
配置CasAuthenticationEntryPoint時需要指定一個ServiceProperties,
-
該物件主要用來描述service(Cas概念)相關的屬性,主要是指定在Cas Server認證成功後將要跳轉的地址。
-
CasAuthenticationFilter認真過濾器,負責認證跳轉和票據驗證
-
@author houzhonglan
-
@date 2018年12月10日 上午11:12:01 */ @Configuration @EnableConfigurationProperties(value= {CasServerProperties.class}) public class SecurityConfiguration {
@Autowired private CasServerProperties casServerProperties;
@Autowired private AuthenticationUserDetailsService userDetailsService;
@Bean public AuthenticationManager authenticationManager(CasAuthenticationProvider provider) {
List<AuthenticationProvider> providers = new ArrayList<>(); providers.add(provider); ProviderManager providerManager = new ProviderManager(providers); return providerManager;
}
/** ** 我們自己應用的配置資訊,該物件主要用於構建CasAuthenticationEntryPoint。
- @return
- @date 2018年12月10日 上午11:21:16 */ @Bean public ServiceProperties serviceProperties() { ServiceProperties serviceProperties = new ServiceProperties(); //設定預設的cas登陸後回跳地址 serviceProperties.setService(casServerProperties.getServerName()+"/index"); //設定我們應用是否敏感 serviceProperties.setSendRenew(false); //設定是否對未擁有ticket的訪問均需要驗證 serviceProperties.setAuthenticateAllArtifacts(true); return serviceProperties; }
/** ** CAS認證過濾器,主要實現票據認證和認證成功後的跳轉。
- @return
- @date 2018年12月10日 上午11:25:56 */ @Bean public CasAuthenticationFilter casAuthenticationFilter(AuthenticationManager auth,ServiceProperties serviceProperties) { CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter(); //給過濾器設定我們應用的基本配置 casAuthenticationFilter.setServiceProperties(serviceProperties); //給過濾器設定認證管理器 casAuthenticationFilter.setAuthenticationManager(auth); //設定過濾器到cas server認證的地址 casAuthenticationFilter.setFilterProcessesUrl(casServerProperties.getCasServerLoginUrl()); //設定是否繼續執行其他過濾器,在完成認證前 casAuthenticationFilter.setContinueChainBeforeSuccessfulAuthentication(false); //設定認證成功後的處理handler casAuthenticationFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/demo/admin")); return casAuthenticationFilter; }
/** ** 認證的入口,即跳轉至服務端的cas地址
-
security框架整合cas認證的入口,也就是security不再走自己的認證入口,而是cas的,該物件就是cas的認證入口
-
@return
-
@date 2018年12月10日 上午11:41:54 */ @Bean public CasAuthenticationEntryPoint casAuthenticationEntryPoint(ServiceProperties serviceProperties) {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint(); //security框架整合cas認證的入口,也就是security不再走自己的認證入口,而是cas的,該物件就是cas的認證入口 casAuthenticationEntryPoint.setServiceProperties(serviceProperties); casAuthenticationEntryPoint.setLoginUrl(casServerProperties.getCasServerLoginUrl()); return casAuthenticationEntryPoint; }
/** ** 配置TicketValidator在登入認證成功後驗證ticket
- 該物件就是一個ticket校驗器
- @return
- @date 2018年12月10日 上午11:47:36 */ @Bean public Cas20ServiceTicketValidator cas20ServiceTicketValidator() { //需要設定cas server的字首,也就是根路徑 Cas20ServiceTicketValidator cas20ServiceTicketValidator = new Cas20ServiceTicketValidator(casServerProperties.getCasServerUrlPrefix()); return cas20ServiceTicketValidator; }
/** ** 該物件為cas校驗物件,TicketValidator、AuthenticationUserDetailService屬性必須設定;
-
serviceProperties屬性主要應用於ticketValidator用於去cas服務端檢驗ticket
-
@param userDetailsService
-
@param serviceProperties
-
@param ticketValidator
-
@return
-
@date 2018年12月10日 下午3:45:13 */ @Bean("casProvider") public CasAuthenticationProvider casAuthenticationProvider(AuthenticationUserDetailsService userDetailsService, ServiceProperties serviceProperties, Cas20ServiceTicketValidator ticketValidator) {
CasAuthenticationProvider provider = new CasAuthenticationProvider(); provider.setKey("casProvider"); provider.setServiceProperties(serviceProperties); provider.setTicketValidator(ticketValidator); provider.setAuthenticationUserDetailsService(userDetailsService); return provider; }
@Bean public LogoutFilter logoutFilter() { String logoutRedirectPath = casServerProperties.getCasServerLogoutUrl()+ "?service=" +casServerProperties.getServerName()+"/index"; LogoutFilter logoutFilter = new LogoutFilter(logoutRedirectPath, new SecurityContextLogoutHandler()); logoutFilter.setFilterProcessesUrl(casServerProperties.getCasServerLogoutUrl()); return logoutFilter;
}
}
那麼接下來分析幾個關鍵的物件 security中認證由AuthenticationManager管理器呼叫Authentication物件。AuthenticationManager有兩種構建方式:1、由AuthenticationManagerBuilder物件構建,builder可以傳入記憶體中使用者資訊,也可以傳入兩個sql語句,告訴builder去那裡自己查詢使用者資訊,或者實現userDetailsService介面中的loadByUsername方法,自定義提供一個userDetails物件,userDetails是個介面,一般提供其實現類user物件即可。2、AuthenticationManager也是一個介面,可以通過其實現類ProviderManager的構造方法,構建一個AuthenticationManager物件,該構造方法需要傳入使用者的認證資訊也就是AuthenticationProvider物件集合。我們可以自定義獲取使用者資訊的方式並且生成一個List物件注入到ProviderManager中。
cas中負責認證,認證的主要是認證跳轉和票據驗證 casAuthenticationFilter就是其中負責跳轉和票據驗證的類,要完成認證,該類需要依賴認證管理器,因此需要把AuthenticationManager注入。要完成跳轉還需要設定cas server的url資訊等和本地伺服器的資訊,因此還需要一個ServiceProperties物件,該物件就是儲存這些資訊的。認證和票據驗證的功能也是由其他類提供實現的,這個filter只是排程這些類
認證功能,根據剛剛的分析我們知道security要實現認證,就必須用到AuthenticationManager認證管理器,而AuthenticationManager是個介面一般提供其實現類ProviderManager即可,構造ProviderManager物件需要傳入AuthenticationProvider使用者資訊提供者物件的集合,一般情況下,我們都是通過資料庫查詢生成AuthenticationProvider這個物件。但是與cas整合過程中,使用者資訊由cas提供,因此cas提供了AuthenticationProvider介面的實現類CasAuthenticationProvider,因此我們在cas中需要配置CasAuthenticationProvider cas認證資訊提供者,並把他注入到AuthenticationManager認證管理器中。
票據驗證功能:票據驗證功能由cas20ServiceTicketValidator物件提供,因此我們還需要配置一個票據驗證的物件,該物件實現了AbstractUrlBasedTicketValidator物件,因此具有票據驗證的功能,還有一些物件也繼承了它,也能實現票據驗證,具體怎麼選擇,視情況而定
功能都全部準備好了,但是還需要一個入口,也就是與security的結合點,因此還需要配置一個AuthenticationEntryPoint認證接入點物件。在cas中提供一個它的實現類CasAuthenticationEntryPoint,因此還需要在configuration中注入這個物件
demo程式碼地址已經放在github https://github.com/guojizhongnan/springsecurity-cas.git
demo中原始碼註釋比較多,大家可以借鑑。demo中cas伺服器是自己搭建的,可以任意修改