WebApplicationSecurityAutoConfiguration.java
package cn.home1.oss.lib.security.starter;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.springframework.core.Ordered.LOWEST_PRECEDENCE;
import com.google.common.collect.ImmutableList;
import cn.home1.oss.boot.autoconfigure.AppProperties;
import cn.home1.oss.lib.errorhandle.starter.ErrorHandleAutoConfiguration;
import cn.home1.oss.lib.security.CompositeAuthenticationProvider;
import cn.home1.oss.lib.security.api.BaseUserDetailsAuthenticationProvider;
import cn.home1.oss.lib.security.internal.feign.FeignTokenConfiguration;
import cn.home1.oss.lib.security.internal.zuul.ZuulTokenConfiguration;
import cn.home1.oss.lib.webmvc.starter.WebApplicationAutoConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.web.context.NullSecurityContextRepository;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* <p>
* see: {@link org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration}.
* see: {@link org.springframework.security.config.annotation.web.builders.WebSecurity}.
* see: {@link org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler}.
* </p>
* <p>
* see: {@link org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler}.
* see: {@link org.springframework.security.access.PermissionEvaluator}.
* </p>
* Created by zhanghaolun on 16/6/28.
*/
@AutoConfigureAfter({ //
WebApplicationAutoConfiguration.class, //
ErrorHandleAutoConfiguration.class, //
SecurityAutoConfiguration.class})
@AutoConfigureBefore(FallbackWebSecurityAutoConfiguration.class)
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@Configuration
@Import({ //
VerifyCodeConfiguration.class, //
PermitedRequestConfiguration.class, //
PreAuthConfiguration.class, //
BasicAuthConfiguration.class, //
FormAuthConfiguration.class, //
CsrfConfiguration.class, //
MethodSecurityConfiguration.class, //
FeignTokenConfiguration.class, //
ZuulTokenConfiguration.class, //
SwaggerConfiguration.class})
@Order(WebApplicationSecurityAutoConfiguration.ORDER_AFTER_MANAGEMENT_BEFORE_FALLBACK)
@Slf4j
public class WebApplicationSecurityAutoConfiguration extends WebSecurityConfigurerAdapter {
/**
* between LOWEST_PRECEDENCE - 5 and LOWEST_PRECEDENCE - 10.
*/
public static final int ORDER_AFTER_MANAGEMENT_BEFORE_FALLBACK = LOWEST_PRECEDENCE - 6;
@Autowired
private AppProperties appProperties;
@Qualifier(PermitedRequestConfiguration.PERMITED_REQUESTS)
@Autowired
public Map<String, List<String>> permitedRequests;
@Autowired
private AuthenticationManager parentAuthenticationManager;
@Qualifier(PreAuthConfiguration.PRE_AUTH_AUTHENTICATION_PROVIDER)
@Autowired(required = false)
private AuthenticationProvider preAuthAuthenticationProvider;
@Autowired(required = false)
@SuppressWarnings("rawtypes")
private BaseUserDetailsAuthenticationProvider userDetailsAuthenticationProvider;
@Autowired(required = false)
private List<SecurityConfigurer> securityConfigurers;
// see: ResourceOwnerPasswordTokenGranter,
// AuthorizationServerEndpointsConfigurer,
// ApprovalStoreUserApprovalHandler#updateAfterApproval
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
for (final SecurityConfigurer configurer : this.sortedSecurityConfigurers()) {
configurer.configure(auth);
}
final List<AuthenticationProvider> providers = newArrayList(
this.preAuthAuthenticationProvider, //
this.userDetailsAuthenticationProvider //
).stream().filter(Objects::nonNull).collect(toList());
if (!providers.isEmpty()) {
final CompositeAuthenticationProvider provider = new CompositeAuthenticationProvider();
provider.setDelegates(ImmutableList.copyOf(providers));
auth //
.eraseCredentials(false) //
.parentAuthenticationManager(this.parentAuthenticationManager) //
// build a ProviderManager of a DaoAuthenticationProvider
// .userDetailsService(this.userService) //
// .passwordEncoder(this.userService.getPasswordEncoder()) //
// .and()
.authenticationProvider(provider);
}
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.securityContext().securityContextRepository(new NullSecurityContextRepository()); // not use session
if (this.appProperties.getSecurityEnabled()) {
//final String contextPath = this.serverProperties.getContextPath();
//final RequestMatcher contextPatchMatcher = isNotBlank(contextPath)
// ? new AntPathRequestMatcher(this.serverProperties.getPath(contextPath) + "/**", null) : null;
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = //
http
//.requestMatcher(AnyRequestMatcher.INSTANCE)
.authorizeRequests();
for (final Map.Entry<String, List<String>> entry : this.permitedRequests.entrySet()) {
final String method = entry.getKey();
final HttpMethod httpMethod = isBlank(method) ? null : HttpMethod.valueOf(method);
final String[] patterns = entry.getValue().stream().toArray(String[]::new);
if (httpMethod == null) {
registry = registry.antMatchers(patterns).permitAll();
} else {
registry = registry.antMatchers(httpMethod, patterns).permitAll();
}
}
}
for (final SecurityConfigurer configurer : this.sortedSecurityConfigurers()) {
configurer.configure(http);
}
if (this.appProperties.getSecurityEnabled()) {
http //
.authorizeRequests() //
.anyRequest().authenticated();
} else {
http //
.authorizeRequests() //
.anyRequest().permitAll() //
;
}
http.headers().frameOptions().sameOrigin();
}
@Override
public void init(final WebSecurity web) throws Exception {
for (final SecurityConfigurer configurer : this.sortedSecurityConfigurers()) {
configurer.init(web);
}
super.init(web);
}
public List<SecurityConfigurer> sortedSecurityConfigurers() {
final List<SecurityConfigurer> all = this.securityConfigurers != null ? this.securityConfigurers : newArrayList();
final List<SecurityConfigurer> sorted = all.stream().sorted().collect(toList());
for (final SecurityConfigurer configurer : sorted) {
log.info("security configurer '{}', order '{}'", configurer.getClass().getName(), configurer.getOrder());
}
return sorted;
}
}