springboot之cas客戶端


  一、CAS Client 與受保護的客戶端應用部署在一起,以 Filter 方式保護受保護的資源。對於訪問受保護資源的每個 Web 請求,CAS Client 會分析該請求的 Http 請求中是否包含 Service Ticket,如果沒有,則說明當前用戶尚未登錄,於是將請求重定向到指定好的 CAS Server 登錄地址,並傳遞 Service (也就是要訪問的目的資源地址),以便登錄成功過后轉回該地址。用戶在第 3 步中輸入認證信息,如果登錄成功,CAS Server 隨機產生一個相當長度、唯一、不可偽造的 Service Ticket,並緩存以待將來驗證,之后系統自動重定向到 Service 所在地址,並為客戶端瀏覽器設置一個 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新產生的 Ticket 過后,在第 5,6 步中與 CAS Server 進行身份核實,以確保 Service Ticket 的合法性。

  二、在該協議中,所有與 CAS 的交互均采用 SSL 協議,確保,ST 和 TGC 的安全性。協議工作過程中會有 2 次重定向的過程,但是 CAS Client 與 CAS Server 之間進行 Ticket 驗證的過程對於用戶是透明的。

  三、cas客戶端主要提供的是業務支持,我們在使用的時候更多是通過cas服務端來做認證支持。這里主要講的是如何搭建cas客戶端,配置的東西其實是通過spring的security來進行過濾。然后達到登錄的目的,認證中主要是通過Ticket的票據進行認證的,當用戶登錄成功。會獲取到登錄的username,然后做進一步處理。

  四、服務端的部署參考:https://www.cnblogs.com/ll409546297/p/10410972.html

  五、客戶端的搭建(這里服務端采用的https的方式)

  1)需要的依賴包

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-cas</artifactId>
        </dependency>
    </dependencies>
  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

  說明:下面這個依賴包主要是用於配置

  2)目錄結構

  

  3)cas的參數配置(cas.properties、CasProperties)

cas.clientUrl=http://localhost:${server.port}
cas.clientLogin=/login
cas.clientLogout=/logout
cas.serverUrl=https://www.casserver.com:8443/cas
cas.serverLogin=/login
cas.serverLogout=/logout
cas.trustStorePath=cas/cas.keystore
cas.trustStorePassword=changeit
@PropertySource(value = "classpath:config/cas.properties")
@ConfigurationProperties(prefix = "cas")
public class CasProperties {

    //客戶端url(本機)
    private String clientUrl;
    //登錄接口
    private String clientLogin;
    //登出接口
    private String clientLogout;
    //服務端url
    private String serverUrl;
    //登錄接口
    private String serverLogin;
    //登出接口
    private String serverLogout;
    //證書密匙路徑
    private String trustStorePath;
    //密碼
    private String trustStorePassword;

    public String getClientUrl() {
        return clientUrl;
    }

    public void setClientUrl(String clientUrl) {
        this.clientUrl = clientUrl;
    }

    public String getClientLogin() {
        return clientLogin;
    }

    public void setClientLogin(String clientLogin) {
        this.clientLogin = clientLogin;
    }

    public String getClientLogout() {
        return clientLogout;
    }

    public void setClientLogout(String clientLogout) {
        this.clientLogout = clientLogout;
    }

    public String getServerUrl() {
        return serverUrl;
    }

    public void setServerUrl(String serverUrl) {
        this.serverUrl = serverUrl;
    }

    public String getServerLogin() {
        return serverLogin;
    }

    public void setServerLogin(String serverLogin) {
        this.serverLogin = serverLogin;
    }

    public String getServerLogout() {
        return serverLogout;
    }

    public void setServerLogout(String serverLogout) {
        this.serverLogout = serverLogout;
    }

    public String getTrustStorePath() {
        return trustStorePath;
    }

    public void setTrustStorePath(String trustStorePath) {
        this.trustStorePath = trustStorePath;
    }

    public String getTrustStorePassword() {
        return trustStorePassword;
    }

    public void setTrustStorePassword(String trustStorePassword) {
        this.trustStorePassword = trustStorePassword;
    }
}

  說明:1、trustStorePath:這個主要使用的服務器上面生成的cas.keystore密鑰、在服務器搭建中我們生成了cas.keystore、域名改成www.casserver.com。目的不支持直接使用IP。

     本地修改hosts:C:\Windows\System32\drivers\etc\hosts

       

     

    2、cas.keystore:服務器生成密鑰,tomcat進行部署,https訪問時需要的私密密鑰

    3、當然可以不使用cas.keystore,通過服務器上面生成的cas.crt證書然后客戶端的jdk也是可以驗證通過的。

keytool -import -keystore "E:\Java\jdk1.8.0_192\jre\lib\security\cacerts" -file cas.crt -alias cas -storepass changeit

  4)cas相關配置(CasConfiguration、SecurityConfiguration)

@Configuration
@Import(CasProperties.class)
public class CasConfiguration {

    //cas相關參數
    @Autowired
    private CasProperties casProperties;

    //客戶端的服務配置,主要用於跳轉
    @Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        //該項目的登錄地址
        serviceProperties.setService(casProperties.getClientUrl() + casProperties.getClientLogin());
        serviceProperties.setAuthenticateAllArtifacts(true);
        return serviceProperties;
    }

    //cas認證點
    @Bean
    public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
        CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
        //cas的登錄地址
        casAuthenticationEntryPoint.setLoginUrl(casProperties.getServerUrl() + casProperties.getServerLogin());
        //入口
        casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
        return casAuthenticationEntryPoint;
    }

    //票據
    @Bean
    public Cas30ServiceTicketValidator cas30ServiceTicketValidator() {
        return new Cas30ServiceTicketValidator(casProperties.getServerUrl());
    }

    //認證支持
    @Bean
    public CasAuthenticationProvider casAuthenticationProvider(AuthDetailsService authDetailsService) {
        CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
        casAuthenticationProvider.setKey("client1");
        casAuthenticationProvider.setServiceProperties(serviceProperties());
        casAuthenticationProvider.setTicketValidator(cas30ServiceTicketValidator());
        //本地登錄后的操作,走security體系
        casAuthenticationProvider.setUserDetailsService(authDetailsService);
        //這里也可以使用setAuthenticationUserDetailsService管理
        //casAuthenticationProvider.setAuthenticationUserDetailsService();
        return casAuthenticationProvider;
    }

    //單點登錄過濾
    @Bean
    public SingleSignOutFilter singleSignOutFilter() {
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setCasServerUrlPrefix(casProperties.getServerUrl());
        singleSignOutFilter.setIgnoreInitConfiguration(true);
        return singleSignOutFilter;
    }

    //登出過濾
    @Bean
    public LogoutFilter logoutFilter() {
        //重定向地址
        String logoutRedirectPath = casProperties.getServerUrl() + casProperties.getServerLogout() + "?service=" + casProperties.getClientUrl();
        LogoutFilter logoutFilter = new LogoutFilter(logoutRedirectPath, new SecurityContextLogoutHandler());
        //登出接口
        logoutFilter.setFilterProcessesUrl(casProperties.getServerLogout());
        return logoutFilter;
    }

}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
@Import(CasProperties.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    //認證
    @Autowired
    private CasAuthenticationProvider authenticationProvider;

    //認證點
    @Autowired
    private CasAuthenticationEntryPoint authenticationEntryPoint;

    //登出過濾
    @Autowired
    private LogoutFilter logoutFilter;

    //單點登出
    @Autowired
    private SingleSignOutFilter singleSignOutFilter;

    //cas配置
    @Autowired
    private CasProperties casProperties;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .exceptionHandling()
            .authenticationEntryPoint(authenticationEntryPoint)
        .and()
            .authorizeRequests()
            .anyRequest().authenticated()
        .and()
            //添加認證過濾(這里我遇到一個坑,如果通過注入方式加入,會出現循環依賴問題)
            .addFilter(casAuthenticationFilter())
            //登出過濾
            .addFilterBefore(logoutFilter, LogoutFilter.class)
            //單點登出過濾
            .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //認證方式
        auth.authenticationProvider(authenticationProvider);
    }

    @Bean
    public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
        //過濾器配置
        CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
        //使用security的認證管理
        casAuthenticationFilter.setAuthenticationManager(authenticationManager());
        //攔截登錄接口
        casAuthenticationFilter.setFilterProcessesUrl(casProperties.getClientLogin());
        return casAuthenticationFilter;
    }
}

  5)登錄后的username處理(AuthDetailsService)

@Service
public class AuthDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (username == null){
            throw new UsernameNotFoundException("用戶不存在!");
        }
        List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
        simpleGrantedAuthorities.add(new SimpleGrantedAuthority("ADMIN"));
        return new org.springframework.security.core.userdetails.User(username, username, simpleGrantedAuthorities);
    }
}

  說明:這里只是簡單處理,實際可以自己綁定用戶處理

  6)https處理(CasIinitTask)

@Component
@Import(CasProperties.class)
public class CasIinitTask {

    @Autowired
    private CasProperties casProperties;

    @PostConstruct
    public void loadKeystore() throws IOException {
        //如果使用https,則必須加入密鑰
        Assert.isTrue(!(casProperties.getServerUrl().startsWith("https") && casProperties.getTrustStorePath() == null),
                "trustStorePath must not null to configuration https");
        //密鑰
        if (!StringUtils.isEmpty(casProperties.getTrustStorePath())) {
            Resource resource = new ClassPathResource(casProperties.getTrustStorePath());
            System.setProperty("javax.net.ssl.trustStore", resource.getFile().getAbsolutePath());
        }
        //有可能密碼的情況
        if (StringUtils.isEmpty(casProperties.getTrustStorePassword())) {
            System.setProperty("javax.net.ssl.trustStorePassword", casProperties.getTrustStorePassword());
        }
    }
}

  7)啟動項目測試:

  六、源碼:https://github.com/lilin409546297/springboot-cas

  七、這里只是簡單的搭建過程,實際cas還需要做二次開發。相比於cas和oauth2我個人更加喜歡oauth2,個人看法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM