sé que esto es un poco viejo, pero yo sólo tenía que resolver este mismo problema y realmente no pude encontrar nada en las pilas nuevas.
Tenemos varios entornos que comparten el mismo servicio CAS (piense en entornos de desarrollo dev, qa, uat y locales); tenemos la capacidad de acceder a cada entorno desde más de una url (a través del servidor web del lado del cliente a través de un proxy inverso y directamente al servidor de fondo mismo). Esto significa que especificar una sola URL es difícil en el mejor de los casos. Tal vez hay una manera de hacer esto pero poder usar un ServiceProperties.getService()
dinámico. Probablemente agregue algún tipo de comprobación de sufijo de servidor para garantizar que la url no sea secuestrada en algún momento.
Esto es lo que hice para conseguir el flujo básico CAS trabajar independientemente de la URL utilizada para acceder al recurso protegido ...
- Sustituir el
CasAuthenticationFilter
.
- Anule
CasAuthenticationProvider
.
setAuthenticateAllArtifacts(true)
en el ServiceProperties
.
Aquí está la forma larga de mi grano de configuración del resorte:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class CasSecurityConfiguration extends WebSecurityConfigurerAdapter {
Sólo el grano de configuración del resorte de costumbre.
@Value("${cas.server.url:https://localhost:9443/cas}")
private String casServerUrl;
@Value("${cas.service.validation.uri:/webapi/j_spring_cas_security_check}")
private String casValidationUri;
@Value("${cas.provider.key:whatever_your_key}")
private String casProviderKey;
Algunos parámetros de configuración externalizados.
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(casValidationUri);
serviceProperties.setSendRenew(false);
serviceProperties.setAuthenticateAllArtifacts(true);
return serviceProperties;
}
La clave anterior es la llamada setAuthenticateAllArtifacts(true)
. Esto hará que el validador ticket de servicio utilice la aplicación AuthenticationDetailsSource
en lugar de un hard-coded ServiceProperties.getService()
llamada
@Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
return new Cas20ServiceTicketValidator(casServerUrl);
}
validador billete estándar ..
@Resource
private UserDetailsService userDetailsService;
@Bean
public AuthenticationUserDetailsService authenticationUserDetailsService() {
return new AuthenticationUserDetailsService() {
@Override
public UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException {
String username = (token.getPrincipal() == null) ? "NONE_PROVIDED" : token.getName();
return userDetailsService.loadUserByUsername(username);
}
};
}
gancho estándar a una UserDetailsService existente
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(authenticationUserDetailsService());
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setKey(casProviderKey);
return casAuthenticationProvider;
}
proveedor de autenticación estándar
@Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
casAuthenticationFilter.setServiceProperties(serviceProperties());
casAuthenticationFilter.setAuthenticationDetailsSource(dynamicServiceResolver());
return casAuthenticationFilter;
}
clave aquí es la configuración dynamicServiceResolver()
..
@Bean
AuthenticationDetailsSource<HttpServletRequest,
ServiceAuthenticationDetails> dynamicServiceResolver() {
return new AuthenticationDetailsSource<HttpServletRequest, ServiceAuthenticationDetails>() {
@Override
public ServiceAuthenticationDetails buildDetails(HttpServletRequest context) {
final String url = makeDynamicUrlFromRequest(serviceProperties());
return new ServiceAuthenticationDetails() {
@Override
public String getServiceUrl() {
return url;
}
};
}
};
}
crea dinámicamente la URL del servicio a partir del método makeDynamicUrlFromRequest()
. Este bit se usa al validar el ticket.
@Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint() {
@Override
protected String createServiceUrl(final HttpServletRequest request, final HttpServletResponse response) {
return CommonUtils.constructServiceUrl(null, response, makeDynamicUrlFromRequest(serviceProperties())
, null, serviceProperties().getArtifactParameter(), false);
}
};
casAuthenticationEntryPoint.setLoginUrl(casServerUrl + "/login");
casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
return casAuthenticationEntryPoint;
}
Esta parte utiliza el mismo creador de url dinámico cuando CAS desea redirigir a la pantalla de inicio de sesión.
private String makeDynamicUrlFromRequest(ServiceProperties serviceProperties){
return "https://howeverYouBuildYourOwnDynamicUrl.com";
}
Esto es lo que hacemos de ella. Solo pasé en las propiedades de servicio para albergar el URI del servicio para el que estamos configurados. Utilizamos HATEAOS en el lado posterior y tienen una aplicación como:
return UriComponentsBuilder.fromHttpUrl(
linkTo(methodOn(ExposedRestResource.class)
.aMethodOnThatResource(null)).withSelfRel().getHref())
.replacePath(serviceProperties.getService())
.build(false)
.toUriString();
Editar: esto es lo que hice para la lista de sufijos de servidores válidos ..
private List<String> validCasServerHostEndings;
@Value("${cas.valid.server.suffixes:company.com,localhost}")
private void setValidCasServerHostEndings(String endings){
validCasServerHostEndings = new ArrayList<>();
for (String ending : StringUtils.split(endings, ",")) {
if (StringUtils.isNotBlank(ending)){
validCasServerHostEndings.add(StringUtils.trim(ending));
}
}
}
private String makeDynamicUrlFromRequest(ServiceProperties serviceProperties){
UriComponents url = UriComponentsBuilder.fromHttpUrl(
linkTo(methodOn(ExposedRestResource.class)
.aMethodOnThatResource(null)).withSelfRel().getHref())
.replacePath(serviceProperties.getService())
.build(false);
boolean valid = false;
for (String validCasServerHostEnding : validCasServerHostEndings) {
if (url.getHost().endsWith(validCasServerHostEnding)){
valid = true;
break;
}
}
if (!valid){
throw new AccessDeniedException("The server is unable to authenticate the requested url.");
}
return url.toString();
}
Estoy usando spring 3; tenga en cuenta el enlace a la seguridad de primavera 3 documentos –
Quizás [este enlace] (https://jira.springsource.org/browse/SEC-1374) esté relacionado y proporcione algunas ideas sobre su requisito/problema. – Raghuram
Bueno, ciertamente aprendí algo y eliminé una posible solución. Como no puedo confiar en la solicitud de HTTP, me gustaría configurar el servicio a través de algunos valores derivados en el momento del despliegue, lo que debería ser seguro. –