View Javadoc
1   package cn.home1.oss.lib.security.starter;
2   
3   import static com.google.common.collect.Lists.newArrayList;
4   import static java.util.stream.Collectors.toList;
5   import static org.apache.commons.lang3.StringUtils.isBlank;
6   import static org.springframework.core.Ordered.LOWEST_PRECEDENCE;
7   
8   import com.google.common.collect.ImmutableList;
9   
10  import cn.home1.oss.boot.autoconfigure.AppProperties;
11  import cn.home1.oss.lib.errorhandle.starter.ErrorHandleAutoConfiguration;
12  import cn.home1.oss.lib.security.CompositeAuthenticationProvider;
13  import cn.home1.oss.lib.security.api.BaseUserDetailsAuthenticationProvider;
14  import cn.home1.oss.lib.security.internal.feign.FeignTokenConfiguration;
15  import cn.home1.oss.lib.security.internal.zuul.ZuulTokenConfiguration;
16  import cn.home1.oss.lib.webmvc.starter.WebApplicationAutoConfiguration;
17  
18  import lombok.extern.slf4j.Slf4j;
19  
20  import org.springframework.beans.factory.annotation.Autowired;
21  import org.springframework.beans.factory.annotation.Qualifier;
22  import org.springframework.boot.autoconfigure.AutoConfigureAfter;
23  import org.springframework.boot.autoconfigure.AutoConfigureBefore;
24  import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
25  import org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration;
26  import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
27  import org.springframework.context.annotation.Configuration;
28  import org.springframework.context.annotation.Import;
29  import org.springframework.core.annotation.Order;
30  import org.springframework.http.HttpMethod;
31  import org.springframework.security.authentication.AuthenticationManager;
32  import org.springframework.security.authentication.AuthenticationProvider;
33  import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
34  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
35  import org.springframework.security.config.annotation.web.builders.WebSecurity;
36  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
37  import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
38  import org.springframework.security.web.context.NullSecurityContextRepository;
39  
40  import java.util.List;
41  import java.util.Map;
42  import java.util.Objects;
43  
44  /**
45   * <p>
46   * see: {@link org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration}.
47   * see: {@link org.springframework.security.config.annotation.web.builders.WebSecurity}.
48   * see: {@link org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler}.
49   * </p>
50   * <p>
51   * see: {@link org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler}.
52   * see: {@link org.springframework.security.access.PermissionEvaluator}.
53   * </p>
54   * Created by zhanghaolun on 16/6/28.
55   */
56  @AutoConfigureAfter({ //
57    WebApplicationAutoConfiguration.class, //
58    ErrorHandleAutoConfiguration.class, //
59    SecurityAutoConfiguration.class})
60  @AutoConfigureBefore(FallbackWebSecurityAutoConfiguration.class)
61  @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
62  @Configuration
63  @Import({ //
64    VerifyCodeConfiguration.class, //
65    PermitedRequestConfiguration.class, //
66    PreAuthConfiguration.class, //
67    BasicAuthConfiguration.class, //
68    FormAuthConfiguration.class, //
69    CsrfConfiguration.class, //
70    MethodSecurityConfiguration.class, //
71    FeignTokenConfiguration.class, //
72    ZuulTokenConfiguration.class, //
73    SwaggerConfiguration.class})
74  @Order(WebApplicationSecurityAutoConfiguration.ORDER_AFTER_MANAGEMENT_BEFORE_FALLBACK)
75  @Slf4j
76  public class WebApplicationSecurityAutoConfiguration extends WebSecurityConfigurerAdapter {
77  
78    /**
79     * between LOWEST_PRECEDENCE - 5 and LOWEST_PRECEDENCE - 10.
80     */
81    public static final int ORDER_AFTER_MANAGEMENT_BEFORE_FALLBACK = LOWEST_PRECEDENCE - 6;
82  
83    @Autowired
84    private AppProperties appProperties;
85  
86    @Qualifier(PermitedRequestConfiguration.PERMITED_REQUESTS)
87    @Autowired
88    public Map<String, List<String>> permitedRequests;
89  
90    @Autowired
91    private AuthenticationManager parentAuthenticationManager;
92    @Qualifier(PreAuthConfiguration.PRE_AUTH_AUTHENTICATION_PROVIDER)
93    @Autowired(required = false)
94    private AuthenticationProvider preAuthAuthenticationProvider;
95    @Autowired(required = false)
96    @SuppressWarnings("rawtypes")
97    private BaseUserDetailsAuthenticationProvider userDetailsAuthenticationProvider;
98  
99    @Autowired(required = false)
100   private List<SecurityConfigurer> securityConfigurers;
101 
102   // see: ResourceOwnerPasswordTokenGranter,
103   // AuthorizationServerEndpointsConfigurer,
104   // ApprovalStoreUserApprovalHandler#updateAfterApproval
105   @Override
106   protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
107     for (final SecurityConfigurer configurer : this.sortedSecurityConfigurers()) {
108       configurer.configure(auth);
109     }
110 
111     final List<AuthenticationProvider> providers = newArrayList(
112       this.preAuthAuthenticationProvider, //
113       this.userDetailsAuthenticationProvider //
114     ).stream().filter(Objects::nonNull).collect(toList());
115 
116     if (!providers.isEmpty()) {
117       final CompositeAuthenticationProvider provider = new CompositeAuthenticationProvider();
118       provider.setDelegates(ImmutableList.copyOf(providers));
119 
120       auth //
121         .eraseCredentials(false) //
122         .parentAuthenticationManager(this.parentAuthenticationManager) //
123         // build a ProviderManager of a DaoAuthenticationProvider
124         // .userDetailsService(this.userService) //
125         // .passwordEncoder(this.userService.getPasswordEncoder()) //
126         // .and()
127         .authenticationProvider(provider);
128     }
129   }
130 
131   @Override
132   protected void configure(final HttpSecurity http) throws Exception {
133     http.securityContext().securityContextRepository(new NullSecurityContextRepository()); // not use session
134 
135     if (this.appProperties.getSecurityEnabled()) {
136       //final String contextPath = this.serverProperties.getContextPath();
137       //final RequestMatcher contextPatchMatcher = isNotBlank(contextPath)
138       //  ? new AntPathRequestMatcher(this.serverProperties.getPath(contextPath) + "/**", null) : null;
139       ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = //
140         http
141           //.requestMatcher(AnyRequestMatcher.INSTANCE)
142           .authorizeRequests();
143 
144       for (final Map.Entry<String, List<String>> entry : this.permitedRequests.entrySet()) {
145         final String method = entry.getKey();
146         final HttpMethod httpMethod = isBlank(method) ? null : HttpMethod.valueOf(method);
147         final String[] patterns = entry.getValue().stream().toArray(String[]::new);
148         if (httpMethod == null) {
149           registry = registry.antMatchers(patterns).permitAll();
150         } else {
151           registry = registry.antMatchers(httpMethod, patterns).permitAll();
152         }
153       }
154     }
155 
156     for (final SecurityConfigurer configurer : this.sortedSecurityConfigurers()) {
157       configurer.configure(http);
158     }
159 
160     if (this.appProperties.getSecurityEnabled()) {
161       http //
162         .authorizeRequests() //
163         .anyRequest().authenticated();
164     } else {
165       http //
166         .authorizeRequests() //
167         .anyRequest().permitAll() //
168       ;
169     }
170 
171     http.headers().frameOptions().sameOrigin();
172   }
173 
174   @Override
175   public void init(final WebSecurity web) throws Exception {
176     for (final SecurityConfigurer configurer : this.sortedSecurityConfigurers()) {
177       configurer.init(web);
178     }
179     super.init(web);
180   }
181 
182   public List<SecurityConfigurer> sortedSecurityConfigurers() {
183     final List<SecurityConfigurer> all = this.securityConfigurers != null ? this.securityConfigurers : newArrayList();
184     final List<SecurityConfigurer> sorted = all.stream().sorted().collect(toList());
185     for (final SecurityConfigurer configurer : sorted) {
186       log.info("security configurer '{}', order '{}'", configurer.getClass().getName(), configurer.getOrder());
187     }
188     return sorted;
189   }
190 }