/* * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.security.config.annotation.web.configurers; import javax.servlet.http.HttpServletRequest; import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails; import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor; import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter; /** * Adds X509 based pre authentication to an application. Since validating the certificate * happens when the client connects, the requesting and validation of the client * certificate should be performed by the container. Spring Security will then use the * certificate to look up the {@link Authentication} for the user. * * <h2>Security Filters</h2> * * The following Filters are populated * * <ul> * <li>{@link X509AuthenticationFilter}</li> * </ul> * * <h2>Shared Objects Created</h2> * * The following shared objects are created * * <ul> * <li> * {@link AuthenticationEntryPoint} is populated with an * {@link Http403ForbiddenEntryPoint}</li> * <li>A {@link PreAuthenticatedAuthenticationProvider} is populated into * {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)} * </li> * </ul> * * <h2>Shared Objects Used</h2> * * The following shared objects are used: * * <ul> * <li>A {@link UserDetailsService} shared object is used if no * {@link AuthenticationUserDetailsService} is specified</li> * </ul> * * @author Rob Winch * @since 3.2 */ public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<X509Configurer<H>, H> { private X509AuthenticationFilter x509AuthenticationFilter; private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService; private String subjectPrincipalRegex; private AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> authenticationDetailsSource; /** * Creates a new instance * @see HttpSecurity#x509() */ public X509Configurer() { } /** * Allows specifying the entire {@link X509AuthenticationFilter}. If this is * specified, the properties on {@link X509Configurer} will not be populated on the * {@link X509AuthenticationFilter}. * * @param x509AuthenticationFilter the {@link X509AuthenticationFilter} to use * @return the {@link X509Configurer} for further customizations */ public X509Configurer<H> x509AuthenticationFilter( X509AuthenticationFilter x509AuthenticationFilter) { this.x509AuthenticationFilter = x509AuthenticationFilter; return this; } /** * Specifies the {@link AuthenticationDetailsSource} * * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} to use * @return the {@link X509Configurer} to use */ public X509Configurer<H> authenticationDetailsSource( AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> authenticationDetailsSource) { this.authenticationDetailsSource = authenticationDetailsSource; return this; } /** * Shortcut for invoking * {@link #authenticationUserDetailsService(AuthenticationUserDetailsService)} with a * {@link UserDetailsByNameServiceWrapper}. * * @param userDetailsService the {@link UserDetailsService} to use * @return the {@link X509Configurer} for further customizations */ public X509Configurer<H> userDetailsService(UserDetailsService userDetailsService) { UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService = new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(); authenticationUserDetailsService.setUserDetailsService(userDetailsService); return authenticationUserDetailsService(authenticationUserDetailsService); } /** * Specifies the {@link AuthenticationUserDetailsService} to use. If not specified, * the shared {@link UserDetailsService} will be used to create a * {@link UserDetailsByNameServiceWrapper}. * * @param authenticationUserDetailsService the * {@link AuthenticationUserDetailsService} to use * @return the {@link X509Configurer} for further customizations */ public X509Configurer<H> authenticationUserDetailsService( AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService) { this.authenticationUserDetailsService = authenticationUserDetailsService; return this; } /** * Specifies the regex to extract the principal from the certificate. If not * specified, the default expression from {@link SubjectDnX509PrincipalExtractor} is * used. * * @param subjectPrincipalRegex the regex to extract the user principal from the * certificate (i.e. "CN=(.*?)(?:,|$)"). * @return the {@link X509Configurer} for further customizations */ public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) { this.subjectPrincipalRegex = subjectPrincipalRegex; return this; } // @formatter:off @Override public void init(H http) throws Exception { PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider(); authenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http)); http .authenticationProvider(authenticationProvider) .setSharedObject(AuthenticationEntryPoint.class,new Http403ForbiddenEntryPoint()); } // @formatter:on @Override public void configure(H http) throws Exception { X509AuthenticationFilter filter = getFilter(http .getSharedObject(AuthenticationManager.class)); http.addFilter(filter); } private X509AuthenticationFilter getFilter(AuthenticationManager authenticationManager) { if (x509AuthenticationFilter == null) { x509AuthenticationFilter = new X509AuthenticationFilter(); x509AuthenticationFilter.setAuthenticationManager(authenticationManager); if (subjectPrincipalRegex != null) { SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor(); principalExtractor.setSubjectDnRegex(subjectPrincipalRegex); x509AuthenticationFilter.setPrincipalExtractor(principalExtractor); } if (authenticationDetailsSource != null) { x509AuthenticationFilter .setAuthenticationDetailsSource(authenticationDetailsSource); } x509AuthenticationFilter = postProcess(x509AuthenticationFilter); } return x509AuthenticationFilter; } private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getAuthenticationUserDetailsService( H http) { if (authenticationUserDetailsService == null) { userDetailsService(http.getSharedObject(UserDetailsService.class)); } return authenticationUserDetailsService; } }