AbstractResourceServerAutoConfiguration.java
/*
* Copyright 2020 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.bremersee.security.authentication;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.bremersee.security.authentication.AuthProperties.PathMatcherProperties;
import org.bremersee.web.CorsProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* The abstract resource server security auto configuration.
*
* @author Christian Bremer
*/
@Slf4j
public abstract class AbstractResourceServerAutoConfiguration extends WebSecurityConfigurerAdapter
implements Ordered {
@Getter(AccessLevel.PROTECTED)
private final Environment environment;
@Getter(AccessLevel.PROTECTED)
private final SecurityProperties securityProperties;
@Getter(AccessLevel.PROTECTED)
private final AuthProperties authProperties;
@Getter(AccessLevel.PROTECTED)
private final CorsProperties corsProperties;
@Getter(AccessLevel.PROTECTED)
private final ObjectProvider<JsonPathJwtConverter> jwtConverterProvider;
@Getter(AccessLevel.PROTECTED)
private final ObjectProvider<PasswordEncoder> passwordEncoderProvider;
/**
* Instantiates a new abstract resource server security auto configuration.
*
* @param environment the environment
* @param securityProperties the spring properties
* @param authProperties the authentication and authorization properties
* @param corsProperties the cors properties
* @param jwtConverterProvider the jwt converter provider
* @param passwordEncoderProvider the password encoder provider
*/
protected AbstractResourceServerAutoConfiguration(
Environment environment,
SecurityProperties securityProperties,
AuthProperties authProperties,
CorsProperties corsProperties,
ObjectProvider<JsonPathJwtConverter> jwtConverterProvider,
ObjectProvider<PasswordEncoder> passwordEncoderProvider) {
this.environment = environment;
this.securityProperties = securityProperties;
this.authProperties = authProperties;
this.corsProperties = corsProperties;
this.jwtConverterProvider = jwtConverterProvider;
this.passwordEncoderProvider = passwordEncoderProvider;
}
@Override
public int getOrder() {
return authProperties.getResourceServerOrder();
}
/**
* Init.
*/
protected void init() {
final boolean hasJwkUriSet = StringUtils
.hasText(environment.getProperty("spring.security.oauth2.resourceserver.jwt.jwk-set-uri"));
log.info("\n"
+ "*********************************************************************************\n"
+ "* {}\n"
+ "*********************************************************************************\n"
+ "* enable = {}\n"
+ "* order = {}\n"
+ "* jwt = {}\n"
+ "* cors = {}\n"
+ "*********************************************************************************",
ClassUtils.getUserClass(getClass()).getSimpleName(),
authProperties.getResourceServer().name(),
authProperties.getResourceServerOrder(),
hasJwkUriSet,
corsProperties.isEnable());
}
/**
* Init expression url authorization configurer . expression intercept url registry.
*
* @param httpSecurity the http security
* @return the expression url authorization configurer . expression intercept url registry
* @throws Exception the exception
*/
protected abstract ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
init(HttpSecurity httpSecurity) throws Exception;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
HttpSecurity http = httpSecurity;
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry reg = init(http);
if (authProperties.getResourceServer() == AutoSecurityMode.NONE) {
http = reg
.anyRequest().permitAll()
.and()
.httpBasic().disable();
} else {
reg = configurePathMatchers(reg);
http = configureAuthenticationProvider(reg.and());
}
http = http
.headers().frameOptions(customizer -> {
switch (authProperties.getFrameOptionsMode()) {
case DISABLE: {
customizer.disable();
break;
}
case SAMEORIGIN: {
customizer.sameOrigin();
}
default:
customizer.deny();
}
})
.and()
.csrf().disable();
if (corsProperties.isEnable()) {
http.cors();
} else {
http.cors().disable();
}
}
private ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
configurePathMatchers(
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry reg) {
for (PathMatcherProperties props : authProperties.preparePathMatchers(corsProperties)) {
log.info("Securing requests to {}", props);
HttpMethod httpMethod = props.httpMethod();
if (httpMethod == null) {
reg = reg.antMatchers(props.getAntPattern())
.access(props.accessExpression(
authProperties::ensureRolePrefix));
} else {
reg = reg.antMatchers(httpMethod, props.getAntPattern())
.access(props.accessExpression(
authProperties::ensureRolePrefix));
}
}
return reg;
}
private HttpSecurity configureAuthenticationProvider(HttpSecurity http) throws Exception {
if (jwtConverterProvider.getIfAvailable() != null) {
log.info("Configure authentication provider with JWT.");
return http
.oauth2ResourceServer((rs) -> rs
.jwt()
.jwtAuthenticationConverter(jwtConverterProvider.getIfAvailable())
.and());
}
log.info("Configure authentication provider with basic auth and user details service.");
String realm = environment.getProperty("spring.application.name", "Restricted area");
return http
.formLogin().disable()
.httpBasic().realmName(realm)
.and();
}
}