View Javadoc
1   package cn.home1.oss.lib.security.starter;
2   
3   import static cn.home1.oss.boot.autoconfigure.AppType.RESOURCE;
4   import static com.google.common.collect.Lists.newArrayList;
5   import static com.google.common.collect.Sets.newLinkedHashSet;
6   import static java.util.stream.Collectors.groupingBy;
7   import static java.util.stream.Collectors.mapping;
8   import static java.util.stream.Collectors.toList;
9   import static org.apache.commons.lang3.StringUtils.isBlank;
10  import static org.apache.commons.lang3.StringUtils.isNotBlank;
11  
12  import com.google.common.collect.ImmutableMap;
13  
14  import cn.home1.oss.boot.autoconfigure.AppProperties;
15  import cn.home1.oss.boot.autoconfigure.AppSecurityProperties;
16  import cn.home1.oss.lib.security.internal.VerifyCodeFilter;
17  
18  import org.springframework.beans.factory.annotation.Autowired;
19  import org.springframework.boot.autoconfigure.h2.H2ConsoleProperties;
20  import org.springframework.boot.autoconfigure.security.SecurityProperties;
21  import org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration;
22  import org.springframework.context.annotation.Bean;
23  import org.springframework.context.annotation.Configuration;
24  import org.springframework.core.annotation.Order;
25  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
26  import org.springframework.security.config.annotation.web.builders.WebSecurity;
27  import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
28  import org.springframework.security.web.util.matcher.AnyRequestMatcher;
29  import org.springframework.security.web.util.matcher.OrRequestMatcher;
30  import org.springframework.security.web.util.matcher.RequestMatcher;
31  
32  import java.util.Collection;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  
37  /**
38   * see: {@link SpringBootWebSecurityConfiguration} IgnoredPathsWebSecurityConfigurerAdapter.
39   *
40   * Created by zhanghaolun on 16/8/19.
41   */
42  @Order(PermitedRequestConfiguration.ORDER_PERMITED_REQUEST)
43  @Configuration
44  public class PermitedRequestConfiguration extends SecurityConfigurerAdapter<PermitedRequestConfiguration> {
45  
46    public static final String PERMITED_REQUESTS = "permitedRequests";
47    public static final String PERMITED_REQUEST_MATCHER = "permitedRequestMatcher";
48  
49    public static final int ORDER_PERMITED_REQUEST = SecurityProperties.IGNORED_ORDER + 1;
50  
51    @Autowired
52    private AppProperties appProperties;
53  
54    @Autowired(required = false)
55    private VerifyCodeFilter verifyCodeFilter;
56  
57    @Autowired(required = false)
58    private H2ConsoleProperties h2ConsoleProperties;
59  
60    @Override
61    public void init(final WebSecurity web) {
62      final Set<String> ignored = newLinkedHashSet();
63      // ! TODO ! 其它地方似乎也加了这些, 条件开启, 重复代码 see: Swagger2DocumentationAutoConfiguration.swaggerRequestMatcher
64      // default is: /v2/api-docs
65      //ignored.add("/webjars/**");
66      //ignored.add(this.environment.getProperty("springfox.documentation.swagger.v1.path", "/v1/**"));
67      //ignored.add(this.environment.getProperty("springfox.documentation.swagger.v2.path", "/v2/**"));
68      //ignored.addAll(newArrayList("/swagger-ui.html", "/swagger-resources/**"));
69  
70      ignored.add("/h2-console/**");
71      if (this.h2ConsoleProperties != null) {
72        final String path = this.h2ConsoleProperties.getPath();
73        final String antPattern = (path.endsWith("/") ? path + "**" : path + "/**");
74        ignored.add(antPattern);
75      }
76  
77      // create another filterChain after spring-boot's default (security.ignored) filterChain
78      if (!ignored.isEmpty()) {
79        final List<RequestMatcher> matchers = ignored.stream() //
80          .map(pattern -> new AntPathRequestMatcher(pattern, null)) //
81          .collect(toList());
82        final RequestMatcher requestMatcher = new OrRequestMatcher(matchers);
83        web.ignoring().requestMatchers(requestMatcher);
84      }
85    }
86  
87    @Override
88    public void configure(final HttpSecurity http) {
89      // empty-impl
90    }
91  
92    @Bean(name = PERMITED_REQUEST_MATCHER)
93    public RequestMatcher permitedRequestMatcher() {
94      final RequestMatcher result;
95      if (this.appProperties.getSecurityEnabled()) {
96        // requestMatchers must contain a value
97        final List<RequestMatcher> antMatchers = this.permitedRequests().entrySet().stream()
98          .flatMap(entry -> entry.getValue().stream().map(pattern -> //
99            isBlank(entry.getKey()) //
100             ? new AntPathRequestMatcher(pattern) : new AntPathRequestMatcher(pattern, entry.getKey())
101         )).collect(toList());
102       result = antMatchers.isEmpty() ? request -> false : new OrRequestMatcher(antMatchers);
103     } else {
104       result = AnyRequestMatcher.INSTANCE;
105     }
106     return result;
107   }
108 
109   @Bean(name = PERMITED_REQUESTS)
110   public Map<String, List<String>> permitedRequests() {
111     // permit的还是要过filter和authenticationManager
112     final AppSecurityProperties security = this.appProperties.getSecurity();
113 
114     final String loginPage = security.getLoginPage();
115 
116     final Collection<String> defaultPermited = this.appProperties.getType() != RESOURCE ? //
117       newLinkedHashSet(newArrayList( //
118         security.getLoginPublicKeyUrl(), loginPage, security.getLoginProcessingUrl(), //
119         security.getLogoutUrl() + "/*", security.getLogoutUrl() //
120       )) : newArrayList();
121 
122     final Collection<String> permitedRequests = newLinkedHashSet();
123 
124     if (isNotBlank(security.getPermited())) {
125       permitedRequests.addAll(newArrayList(security.getPermited().split("[ ]*,[ ]*")));
126     }
127 
128     permitedRequests.addAll(defaultPermited);
129 
130     if (this.verifyCodeFilter != null) {
131       permitedRequests.add(this.verifyCodeFilter.getCodeUrl());
132     }
133 
134     final Map<String, List<String>> grouped = permitedRequests.stream()
135       .map(expression -> {
136         final String[] fragments = expression.split(":");
137         return fragments.length == 1 //
138           ? newArrayList("", fragments[0]) : newArrayList(fragments[0].toUpperCase(), fragments[1]);
139       })
140       .collect(groupingBy(o -> o.get(0), mapping(o -> o.get(1), toList())));
141 
142     return ImmutableMap.copyOf(grouped);
143   }
144 }