From 09d4b40da896df19d07691dc9c93e6da551efcdb Mon Sep 17 00:00:00 2001 From: John Niang Date: Fri, 16 Dec 2022 11:32:35 +0800 Subject: [PATCH] Apply specific headers for portal endpoints (#2972) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core #### What this PR does / why we need it: This PR separates security configuration of RESTful APIs and portal pages to configure specific headers for portal pages, such as `Referrer-Policy` and `X-Frame-Options`. #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2900 #### Special notes for your reviewer: You can see the response headers of index page: ```diff HTTP/1.1 200 OK Content-Type: text/html Content-Language: en-US + X-Content-Type-Options: nosniff + X-Frame-Options: SAMEORIGIN + X-XSS-Protection: 0 + Referrer-Policy: strict-origin-when-cross-origin content-encoding: gzip content-length: 4285 ``` and request headers with `Referer`: ```diff GET / HTTP/1.1 Host: localhost:8090 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br + Referer: http://localhost:8090/archives/12341234 Connection: keep-alive Cookie: _ga_Z907HJBP8W=GS1.1.1670164888.1.1.1670165603.0.0.0; _ga=GA1.1.807839437.1670164889; SESSION=539e060e-c11e-4b6d-a749-882905b30a88; XSRF-TOKEN=4b692b55-638c-4497-8a4b-be00986eda90 Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 ``` #### Does this PR introduce a user-facing change? ```release-note 解决访问分析工具无法显示 referer 的问题 ``` --- .../app/config/WebServerSecurityConfig.java | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/main/java/run/halo/app/config/WebServerSecurityConfig.java b/src/main/java/run/halo/app/config/WebServerSecurityConfig.java index 5cb5bdadf..928600f8e 100644 --- a/src/main/java/run/halo/app/config/WebServerSecurityConfig.java +++ b/src/main/java/run/halo/app/config/WebServerSecurityConfig.java @@ -1,20 +1,27 @@ package run.halo.app.config; import static org.springframework.security.config.Customizer.withDefaults; +import static org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN; import static org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN; +import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers; +import java.util.Set; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.core.userdetails.ReactiveUserDetailsService; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.MediaTypeServerWebExchangeMatcher; import run.halo.app.core.extension.service.RoleService; import run.halo.app.core.extension.service.UserService; import run.halo.app.extension.ReactiveExtensionClient; @@ -40,18 +47,15 @@ public class WebServerSecurityConfig { RoleService roleService, ObjectProvider securityConfigurers) { - http.authorizeExchange() - .pathMatchers("/api/**", "/apis/**", "/login", "/logout") - .access(new RequestInfoAuthorizationManager(roleService)) - .pathMatchers("/**").permitAll() - .and() - .headers() - .frameOptions().mode(SAMEORIGIN) - .and() - .anonymous(anonymousSpec -> { - anonymousSpec.authorities(AnonymousUserConst.Role); - anonymousSpec.principal(AnonymousUserConst.PRINCIPAL); + http.securityMatcher(pathMatchers("/api/**", "/apis/**", "/login", "/logout")) + .authorizeExchange().anyExchange() + .access(new RequestInfoAuthorizationManager(roleService)).and() + .anonymous(spec -> { + spec.authorities(AnonymousUserConst.Role); + spec.principal(AnonymousUserConst.PRINCIPAL); }) + .formLogin(withDefaults()) + .logout(withDefaults()) .httpBasic(withDefaults()); // Integrate with other configurers separately @@ -61,6 +65,25 @@ public class WebServerSecurityConfig { return http.build(); } + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE + 1) + SecurityWebFilterChain portalFilterChain(ServerHttpSecurity http) { + var pathMatcher = pathMatchers(HttpMethod.GET, "/**"); + var mediaTypeMatcher = new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML); + mediaTypeMatcher.setIgnoredMediaTypes(Set.of(MediaType.ALL)); + http.securityMatcher(new AndServerWebExchangeMatcher(pathMatcher, mediaTypeMatcher)) + .authorizeExchange().anyExchange().permitAll().and() + .headers() + .frameOptions().mode(SAMEORIGIN) + .referrerPolicy().policy(STRICT_ORIGIN_WHEN_CROSS_ORIGIN).and() + .cache().disable().and() + .anonymous(spec -> { + spec.authorities(AnonymousUserConst.Role); + spec.principal(AnonymousUserConst.PRINCIPAL); + }); + return http.build(); + } + @Bean ReactiveUserDetailsService userDetailsService(UserService userService, RoleService roleService) {