這個(gè)系列有3篇文章:
- spring boot cas 服務(wù)端和spring security客戶端搭建(一):主要介紹怎么用spring boot cas搭建https的服務(wù)端
- spring boot cas 服務(wù)端和spring security客戶端搭建(二):主要介紹怎么用怎么結(jié)合數(shù)據(jù)庫(kù)用cas提供的動(dòng)態(tài)加密方式登錄及相關(guān)配置
- spring boot cas 服務(wù)端和spring security客戶端搭建(三):主要介紹怎么結(jié)合spring security客戶端進(jìn)行整合及http方式登錄配置
終于騰出時(shí)間來(lái)寫完這個(gè)系列了鸦泳。
接著上篇spring boot cas 服務(wù)端和spring security客戶端搭建(二)繼續(xù)介紹cas與spring security客戶端的整合淹真。官網(wǎng)的資料只在github上找到了客戶端的代碼https://github.com/apereo/java-cas-client#spring-security-integration耸携,而且還不是spring boot版本的:
由于學(xué)習(xí)spring boot不久笨忌,還要花更多時(shí)間研究其他功能冬殃,就沒(méi)花時(shí)間去把這些xml配置轉(zhuǎn)成spring boot的配置了魂角;參考https://blog.csdn.net/cl_andywin/article/details/53998986遂填,搭建了基于spring boot的spring security cas客戶端立叛。
引入相關(guān)的依賴包
在對(duì)應(yīng)工程的pom文件中引入spring-security-cas包(其他相關(guān)spring boot 的包就不在這里描述了负敏,這里的包的版本和spring boot parent版本對(duì)應(yīng))
spring boot版本,項(xiàng)目中用的是2.0.3.RELEASE:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
spring boot security和 security cas包
<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>
配置文件和配置類
CasProperties秘蛇,cas服務(wù)端和客戶端相關(guān)的配置信息(這里用到了lombok.Data來(lái)生成get其做,set方法):
@Data
@Component
public class CasProperties {
@Value("${security.cas.server.host}")
private String casServerUrl;
@Value("${security.cas.server.login}")
private String casServerLoginUrl;
@Value("${security.cas.server.logout}")
private String casServerLogoutUrl;
@Value("${security.cas.service.host}")
private String appServerUrl;
@Value("${security.cas.service.login}")
private String appLoginUrl;
@Value("${security.cas.service.logout}")
private String appLogoutUrl;
}
對(duì)應(yīng)的配置信息(這時(shí)cas服務(wù)用的是http形式的了顶考,因?yàn)榄h(huán)境限制不能用域名的,而且http的省事妖泄,在文章的后面會(huì)介紹怎么使用http)驹沿,配置的具體含義這里就不描述了:
security:
cas:
server:
host: http://xxx.xxx.xxx.xxx:8443/cas
login: ${security.cas.server.host}/login
logout: ${security.cas.server.host}/logout?service=${security.cas.service.host}
service:
host: http://localhost:8764
login: /login
logout: /logout
WebSecurityConfig配置類,最核心的配置(上面cas相關(guān)地址的配置不一定要那樣實(shí)現(xiàn)蹈胡,可能有更靈活的辦法渊季;下面登出的相關(guān)配置沒(méi)有起到想象的作用,一個(gè)應(yīng)用登出了罚渐,其他應(yīng)用不會(huì)一起登出却汉;后續(xù)找到辦法了會(huì)繼續(xù)補(bǔ)充):
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CasProperties casProperties;
/**
* configure(AuthenticationManagerBuilder): 身份驗(yàn)證配置,用于注入自定義身份驗(yàn)證Bean和密碼校驗(yàn)規(guī)則
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth.authenticationProvider(casAuthenticationProvider());
}
/**
* configure(WebSecurity): Web層面的配置荷并,一般用來(lái)配置無(wú)需安全檢查的路徑
*
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/**", "/templates/**");
}
/**
* configure(HttpSecurity): Request層面的配置合砂,對(duì)應(yīng)XML Configuration中的<http>元素
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()// 配置安全策略
.antMatchers("/login/cas").permitAll()
.and().authorizeRequests().anyRequest().authenticated()
.and().logout().permitAll()// 定義logout不需要驗(yàn)證
.and().formLogin().defaultSuccessUrl("/index"); // 登錄成功之后的跳轉(zhuǎn)
http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint())
.and().addFilter(casAuthenticationFilter())
.addFilterBefore(casLogoutFilter(), LogoutFilter.class)
.addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class);
http.csrf().disable();
// 關(guān)閉spring security默認(rèn)的frame訪問(wèn)限制
http.headers().frameOptions().sameOrigin();
}
@Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLoginUrl());
casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
return casAuthenticationEntryPoint;
}
/**
* 指定service相關(guān)信息
*/
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl());
// serviceProperties.setAuthenticateAllArtifacts(true);
serviceProperties.setSendRenew(false);
return serviceProperties;
}
/**
* CAS認(rèn)證過(guò)濾器
*/
@Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLoginUrl());
return casAuthenticationFilter;
}
/**
* cas 認(rèn)證 Provider
*/
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService());
// 這里只是接口類型,實(shí)現(xiàn)的接口不一樣源织,都可以的翩伪。
casAuthenticationProvider.setServiceProperties(serviceProperties());
// casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setTicketValidator(cas30ServiceTicketValidator());
casAuthenticationProvider.setKey("casAuthenticationProviderKey");
return casAuthenticationProvider;
}
// 用戶自定義的AuthenticationUserDetailsService
@Bean
public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService() {
return new CustomUserDetailsService();
}
@Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
return new Cas20ServiceTicketValidator(casProperties.getCasServerUrl());
}
@Bean
public Cas30ServiceTicketValidator cas30ServiceTicketValidator() {
return new Cas30ServiceTicketValidator(casProperties.getCasServerUrl());
}
/**
* 單點(diǎn)登出過(guò)濾器
*/
@Bean
public SingleSignOutFilter singleSignOutFilter() {
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setCasServerUrlPrefix(casProperties.getCasServerUrl());
singleSignOutFilter.setIgnoreInitConfiguration(true);
return singleSignOutFilter;
}
/**
* 請(qǐng)求單點(diǎn)退出過(guò)濾器
*/
@Bean
public LogoutFilter casLogoutFilter() {
LogoutFilter logoutFilter = new LogoutFilter(casProperties.getCasServerLogoutUrl(),
new SecurityContextLogoutHandler());
logoutFilter.setFilterProcessesUrl(casProperties.getAppLogoutUrl());
return logoutFilter;
}
public SingleSignOutHttpSessionListener singleSignOutHttpSessionListener() {
return new SingleSignOutHttpSessionListener();
}
@Bean
public ServletListenerRegistrationBean<EventListener> singleSignOutListenerRegistration() {
ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean<EventListener>();
registrationBean.setListener(new SingleSignOutHttpSessionListener());
registrationBean.setOrder(1);
return registrationBean;
}
@Bean
public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListenerBean() {
ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listenerRegistrationBean = new ServletListenerRegistrationBean<>();
listenerRegistrationBean.setEnabled(true);
listenerRegistrationBean.setListener(singleSignOutHttpSessionListener());
listenerRegistrationBean.setOrder(3);
System.out.println("================================singleListener執(zhí)行");
return listenerRegistrationBean;
}
}
其中需要自己去實(shí)現(xiàn)CustomUserDetailsService的用戶認(rèn)證邏輯:
@Service
public class CustomUserDetailsService //實(shí)現(xiàn)AuthenticationUserDetailsService,實(shí)現(xiàn)loadUserDetails方法
implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
@Override
public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {
// 結(jié)合具體的邏輯去實(shí)現(xiàn)用戶認(rèn)證谈息,并返回繼承UserDetails的用戶對(duì)象;
return XXX;
}
}
其中UserDetails是個(gè)接口:
到這里缘屹,spring boot下spring security整合cas的客戶端基本就構(gòu)建完成了。
http方式的CAS服務(wù)端
在第一篇文章(http://www.reibang.com/p/0b3375b10860)介紹的cas-overlay-template下的配置文件application.properties中修改:
# 去掉https驗(yàn)證
cas.tgc.secure=false
cas.warn.cookie.secure=false
server.ssl.enabled=false
其中cas.tgc.secure和cas.warn.cookie.secure是新增的侠仇,server.ssl.enabled默認(rèn)的是true:
默認(rèn)情況下轻姿,CAS是只支持https請(qǐng)求的;還有個(gè)地方需要修改逻炊。因?yàn)槭莝pring boot結(jié)構(gòu)踢代,可以直接覆蓋原有文件,這里就不在額外的加配置了嗅骄,直接在serveices文件夾下修改HTTSPandIMAPS-10000001.json:
這個(gè)文件可以在打包后的target下找到:
在https的邊上多加個(gè)‘|http’就可以了:
這樣CAS就能支持http方式了胳挎。