All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.c4_soft.springaddons.security.oauth2.config.synchronised.ServletSecurityBeans Maven / Gradle / Ivy

package com.c4_soft.springaddons.security.oauth2.config.synchronised;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.servlet.http.HttpServletRequest;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoders;
import org.springframework.security.oauth2.jwt.SupplierJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import com.c4_soft.springaddons.security.oauth2.OAuthentication;
import com.c4_soft.springaddons.security.oauth2.OpenidClaimSet;
import com.c4_soft.springaddons.security.oauth2.SynchronizedJwt2AuthenticationConverter;
import com.c4_soft.springaddons.security.oauth2.SynchronizedJwt2OAuthenticationConverter;
import com.c4_soft.springaddons.security.oauth2.SynchronizedJwt2OpenidClaimSetConverter;
import com.c4_soft.springaddons.security.oauth2.config.ConfigurableJwtGrantedAuthoritiesConverter;
import com.c4_soft.springaddons.security.oauth2.config.Jwt2AuthoritiesConverter;
import com.c4_soft.springaddons.security.oauth2.config.SpringAddonsSecurityProperties;
import com.c4_soft.springaddons.security.oauth2.config.SpringAddonsSecurityProperties.TokenIssuerProperties;

import lombok.extern.slf4j.Slf4j;

/**
 * 

* Usage
* If not using spring-boot, @Import or @ComponentScan this class. All beans defined here are @ConditionalOnMissingBean => just * define your own @Beans to override. *

*

* Provided @Beans *

*
    *
  • SecurityFilterChain: applies CORS, CSRF, anonymous, sessionCreationPolicy, SSL redirect and 401 instead of redirect to login * properties as defined in {@link SpringAddonsSecurityProperties}
  • *
  • ExpressionInterceptUrlRegistryPostProcessor. Override if you need fined grained HTTP security (more than authenticated() to * all routes but the ones defined as permitAll() in {@link SpringAddonsSecurityProperties}
  • *
  • SimpleJwtGrantedAuthoritiesConverter: responsible for converting the JWT into Collection<? extends * GrantedAuthority>
  • *
  • SynchronizedJwt2OpenidClaimSetConverter<OpenidClaimSet>: responsible for converting the JWT into OpenidClaimSet
  • *
  • SynchronizedJwt2AuthenticationConverter<OAuthentication<T>>: responsible for converting the JWT into an * Authentication (uses both beans above)
  • *
  • JwtIssuerAuthenticationManagerResolver: required to be able to define more than one token issuer until * https://github.com/spring-projects/spring-boot/issues/30108 is solved
  • *
* * @author Jerome Wacongne [email protected] */ @AutoConfiguration @Import({ SpringAddonsSecurityProperties.class }) @EnableWebSecurity @Slf4j public class ServletSecurityBeans { @ConditionalOnMissingBean @Bean public SecurityFilterChain filterChain( HttpSecurity http, AuthenticationManagerResolver authenticationManagerResolver, ExpressionInterceptUrlRegistryPostProcessor expressionInterceptUrlRegistryPostProcessor, ServerProperties serverProperties, SpringAddonsSecurityProperties securityProperties) throws Exception { http.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver)); if (securityProperties.isAnonymousEnabled()) { http.anonymous(); } if (securityProperties.getCors().length > 0) { http.cors().configurationSource(corsConfigurationSource(securityProperties)); } if (!securityProperties.isCsrfEnabled()) { http.csrf().disable(); } if (securityProperties.isStatlessSessions()) { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } if (!securityProperties.isRedirectToLoginIfUnauthorizedOnRestrictedContent()) { http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> { response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\""); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); }); } if (serverProperties.getSsl() != null && serverProperties.getSsl().isEnabled()) { http.requiresChannel().anyRequest().requiresSecure(); } else { http.requiresChannel().anyRequest().requiresInsecure(); } expressionInterceptUrlRegistryPostProcessor.authorizeRequests(http.authorizeRequests().antMatchers(securityProperties.getPermitAll()).permitAll()); return http.build(); } @ConditionalOnMissingBean @Bean public ExpressionInterceptUrlRegistryPostProcessor expressionInterceptUrlRegistryPostProcessor() { return registry -> registry.anyRequest().authenticated(); } @ConditionalOnMissingBean @Bean public SynchronizedJwt2AuthenticationConverter> authenticationConverter( Jwt2AuthoritiesConverter authoritiesConverter, SynchronizedJwt2OpenidClaimSetConverter tokenConverter) { log.debug("Building default SynchronizedJwt2OAuthenticationConverter"); return new SynchronizedJwt2OAuthenticationConverter<>(authoritiesConverter, tokenConverter); } @ConditionalOnMissingBean @Bean public Jwt2AuthoritiesConverter authoritiesConverter(SpringAddonsSecurityProperties securityProperties) { log.debug("Building default SimpleJwtGrantedAuthoritiesConverter with: {}", securityProperties); return new ConfigurableJwtGrantedAuthoritiesConverter(securityProperties); } @ConditionalOnMissingBean @Bean public SynchronizedJwt2OpenidClaimSetConverter tokenConverter() { log.debug("Building default SynchronizedJwt2OpenidClaimSetConverter"); return (var jwt) -> new OpenidClaimSet(jwt.getClaims()); } @ConditionalOnMissingBean @Bean public JwtIssuerAuthenticationManagerResolver authenticationManagerResolver( OAuth2ResourceServerProperties auth2ResourceServerProperties, SpringAddonsSecurityProperties securityProperties, Converter authenticationConverter) { final var locations = Stream .concat( Optional .of(auth2ResourceServerProperties.getJwt()) .map(org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties.Jwt::getIssuerUri) .stream(), Stream.of(securityProperties.getTokenIssuers()).map(TokenIssuerProperties::getLocation)) .filter(Objects::nonNull) .map(Serializable::toString) .filter(StringUtils::hasLength) .collect(Collectors.toSet()); final Map managers = locations.stream().collect(Collectors.toMap(l -> l, l -> { final JwtDecoder decoder = new SupplierJwtDecoder(() -> JwtDecoders.fromIssuerLocation(l)); final var provider = new JwtAuthenticationProvider(decoder); provider.setJwtAuthenticationConverter(authenticationConverter); return provider::authenticate; })); log .debug( "Building default JwtIssuerAuthenticationManagerResolver with: ", auth2ResourceServerProperties.getJwt(), Stream.of(securityProperties.getTokenIssuers()).toList()); return new JwtIssuerAuthenticationManagerResolver((AuthenticationManagerResolver) managers::get); } private CorsConfigurationSource corsConfigurationSource(SpringAddonsSecurityProperties securityProperties) { log.debug("Building default CorsConfigurationSource with: {}", Stream.of(securityProperties.getCors()).toList()); final var source = new UrlBasedCorsConfigurationSource(); for (final var corsProps : securityProperties.getCors()) { final var configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList(corsProps.getAllowedOrigins())); configuration.setAllowedMethods(Arrays.asList(corsProps.getAllowedMethods())); configuration.setAllowedHeaders(Arrays.asList(corsProps.getAllowedHeaders())); configuration.setExposedHeaders(Arrays.asList(corsProps.getExposedHeaders())); source.registerCorsConfiguration(corsProps.getPath(), configuration); } return source; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy