CAS-SSO 和 Spring 安全性(Spring 引导)中的无限重定向登录循环
Infinite redirection login loop in CAS-SSO & Spring Security (SpringBoot)
我正在执行后续步骤:
- 我访问我的 java 应用程序的受限 URL (/myapp/login)
- 我被重定向到 /cas/login 页面
- 我介绍正确的凭据
- CAS 将请求重定向到受限 URL(即:/myapp/login)
- 我的应用程序没有接受请求,而是再次检测到此 URL 受保护并再次将请求重定向到 CAS:/cas/login
- auth-cookies 在浏览器中,因此身份验证正常
- 第 4 步
- 第 5 步
- 第 6 步等
我的 CAS 服务器版本:
- CAS 版本:6.1.0-RC3-SNAPSHOT
- Spring启动版本:2.2.0.M1
- Spring版本:5.1.5.RELEASE
- Java 主页:C:\Program Files\Java\jdk-11.0.2
cas.properties:
cas.server.name=https://cas.example.org:8443
cas.server.prefix=${cas.server.name}/cas
logging.config: file:/etc/cas/config/log4j2.xml
cas.serviceRegistry.initFromJson=true
cas.serviceRegistry.json.location=file:/etc/cas/services
已添加 CAS 依赖项:
compile "org.apereo.cas:cas-server-webapp${project.appServer}:${casServerVersion}"
compile "org.apereo.cas:cas-server-support-json-service-registry:${project.'cas.version'}"
compile "org.apereo.cas:cas-server-support-rest:${project.'cas.version'}"
compile "org.apereo.cas:cas-server-support-rest-tokens:${project.'cas.version'}"
我的CAS服务声明App-1001.json里面/etc/cas/service:
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "http://127.0.0.1:9000/imq/ptp/pspp/login",
"name" : "App",
"id" : 1001,
"evaluationOrder" : 10
}
在我的 java 申请中:
CAS Bean 声明:
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(serviceId);
serviceProperties.setSendRenew(false);
return serviceProperties;
}
@Bean
@Primary
public AuthenticationEntryPoint authenticationEntryPoint(
ServiceProperties sP) {
CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
entryPoint.setLoginUrl("https://localhost:8443/cas/login");
entryPoint.setServiceProperties(sP);
return entryPoint;
}
@Bean
public TicketValidator ticketValidator() {
return new Cas30ServiceTicketValidator("https://localhost:8443/cas");
}
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider provider = new CasAuthenticationProvider();
provider.setServiceProperties(serviceProperties());
provider.setTicketValidator(ticketValidator());
provider.setUserDetailsService(
s -> new User(casUsername, casPassword, true, true, true, true,
AuthorityUtils.createAuthorityList(casRole)
)
);
provider.setKey(casKey);
return provider;
}
/////
@Bean
public SecurityContextLogoutHandler securityContextLogoutHandler() {
return new SecurityContextLogoutHandler();
}
@Bean
public LogoutFilter logoutFilter() {
LogoutFilter logoutFilter = new LogoutFilter(
"https://localhost:8443/cas/logout",
securityContextLogoutHandler());
logoutFilter.setFilterProcessesUrl("/logout/cas");
return logoutFilter;
}
@Bean
public SingleSignOutFilter singleSignOutFilter() {
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setCasServerUrlPrefix("https://localhost:8443/cas");
singleSignOutFilter.setIgnoreInitConfiguration(true);
return singleSignOutFilter;
}
@EventListener
public SingleSignOutHttpSessionListener singleSignOutHttpSessionListener(
HttpSessionEvent event) {
return new SingleSignOutHttpSessionListener();
}
我的 WebSecurity class 声明:
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private AuthenticationProvider authenticationProvider;
private AuthenticationEntryPoint authenticationEntryPoint;
private SingleSignOutFilter singleSignOutFilter;
private LogoutFilter logoutFilter;
@Autowired
public SecurityConfig(CasAuthenticationProvider casAuthenticationProvider,
AuthenticationEntryPoint eP,
LogoutFilter lF,
SingleSignOutFilter ssF) {
this.authenticationProvider = casAuthenticationProvider;
this.authenticationEntryPoint = eP;
this.logoutFilter = lF;
this.singleSignOutFilter = ssF;
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);
return firewall;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.regexMatchers("/imq/ptp/pspp.*")
.authenticated()
.and()
.authorizeRequests()
.regexMatchers("/")
.permitAll()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout().logoutSuccessUrl("/logout")
.and()
.addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
.addFilterBefore(logoutFilter, LogoutFilter.class);
}
// ...
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(Arrays.asList(authenticationProvider));
}
@Bean
public CasAuthenticationFilter casAuthenticationFilter(ServiceProperties sP) throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setServiceProperties(sP);
filter.setAuthenticationManager(authenticationManager());
return filter;
}
}
提前致谢!
我认为您应该将 casAuthenticationFilter 置于其他设置之上。像这样:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilter(casAuthenticationFilter())
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
我正在执行后续步骤:
- 我访问我的 java 应用程序的受限 URL (/myapp/login)
- 我被重定向到 /cas/login 页面
- 我介绍正确的凭据
- CAS 将请求重定向到受限 URL(即:/myapp/login)
- 我的应用程序没有接受请求,而是再次检测到此 URL 受保护并再次将请求重定向到 CAS:/cas/login
- auth-cookies 在浏览器中,因此身份验证正常
- 第 4 步
- 第 5 步
- 第 6 步等
我的 CAS 服务器版本:
- CAS 版本:6.1.0-RC3-SNAPSHOT
- Spring启动版本:2.2.0.M1
- Spring版本:5.1.5.RELEASE
- Java 主页:C:\Program Files\Java\jdk-11.0.2
cas.properties:
cas.server.name=https://cas.example.org:8443
cas.server.prefix=${cas.server.name}/cas
logging.config: file:/etc/cas/config/log4j2.xml
cas.serviceRegistry.initFromJson=true
cas.serviceRegistry.json.location=file:/etc/cas/services
已添加 CAS 依赖项:
compile "org.apereo.cas:cas-server-webapp${project.appServer}:${casServerVersion}"
compile "org.apereo.cas:cas-server-support-json-service-registry:${project.'cas.version'}"
compile "org.apereo.cas:cas-server-support-rest:${project.'cas.version'}"
compile "org.apereo.cas:cas-server-support-rest-tokens:${project.'cas.version'}"
我的CAS服务声明App-1001.json里面/etc/cas/service:
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "http://127.0.0.1:9000/imq/ptp/pspp/login",
"name" : "App",
"id" : 1001,
"evaluationOrder" : 10
}
在我的 java 申请中:
CAS Bean 声明:
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(serviceId);
serviceProperties.setSendRenew(false);
return serviceProperties;
}
@Bean
@Primary
public AuthenticationEntryPoint authenticationEntryPoint(
ServiceProperties sP) {
CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
entryPoint.setLoginUrl("https://localhost:8443/cas/login");
entryPoint.setServiceProperties(sP);
return entryPoint;
}
@Bean
public TicketValidator ticketValidator() {
return new Cas30ServiceTicketValidator("https://localhost:8443/cas");
}
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider provider = new CasAuthenticationProvider();
provider.setServiceProperties(serviceProperties());
provider.setTicketValidator(ticketValidator());
provider.setUserDetailsService(
s -> new User(casUsername, casPassword, true, true, true, true,
AuthorityUtils.createAuthorityList(casRole)
)
);
provider.setKey(casKey);
return provider;
}
/////
@Bean
public SecurityContextLogoutHandler securityContextLogoutHandler() {
return new SecurityContextLogoutHandler();
}
@Bean
public LogoutFilter logoutFilter() {
LogoutFilter logoutFilter = new LogoutFilter(
"https://localhost:8443/cas/logout",
securityContextLogoutHandler());
logoutFilter.setFilterProcessesUrl("/logout/cas");
return logoutFilter;
}
@Bean
public SingleSignOutFilter singleSignOutFilter() {
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setCasServerUrlPrefix("https://localhost:8443/cas");
singleSignOutFilter.setIgnoreInitConfiguration(true);
return singleSignOutFilter;
}
@EventListener
public SingleSignOutHttpSessionListener singleSignOutHttpSessionListener(
HttpSessionEvent event) {
return new SingleSignOutHttpSessionListener();
}
我的 WebSecurity class 声明:
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private AuthenticationProvider authenticationProvider;
private AuthenticationEntryPoint authenticationEntryPoint;
private SingleSignOutFilter singleSignOutFilter;
private LogoutFilter logoutFilter;
@Autowired
public SecurityConfig(CasAuthenticationProvider casAuthenticationProvider,
AuthenticationEntryPoint eP,
LogoutFilter lF,
SingleSignOutFilter ssF) {
this.authenticationProvider = casAuthenticationProvider;
this.authenticationEntryPoint = eP;
this.logoutFilter = lF;
this.singleSignOutFilter = ssF;
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);
return firewall;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.regexMatchers("/imq/ptp/pspp.*")
.authenticated()
.and()
.authorizeRequests()
.regexMatchers("/")
.permitAll()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout().logoutSuccessUrl("/logout")
.and()
.addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
.addFilterBefore(logoutFilter, LogoutFilter.class);
}
// ...
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(Arrays.asList(authenticationProvider));
}
@Bean
public CasAuthenticationFilter casAuthenticationFilter(ServiceProperties sP) throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setServiceProperties(sP);
filter.setAuthenticationManager(authenticationManager());
return filter;
}
}
提前致谢!
我认为您应该将 casAuthenticationFilter 置于其他设置之上。像这样:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilter(casAuthenticationFilter())
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))