1. 程式人生 > >springboot+springsecurity+cas實現sso,並開啟註解方法級別的許可權控制

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伺服器是自己搭建的,可以任意修改