mirror of https://github.com/halo-dev/halo
Apply specific headers for portal endpoints (#2972)
#### 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 的问题 ```pull/2964/head
parent
090b28b399
commit
09d4b40da8
|
@ -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<SecurityConfigurer> 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) {
|
||||
|
|
Loading…
Reference in New Issue