Skip to content

Commit

Permalink
Apply specific headers for portal endpoints (halo-dev#2972)
Browse files Browse the repository at this point in the history
#### 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 halo-dev#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 的问题
```
  • Loading branch information
JohnNiang authored Dec 16, 2022
1 parent 090b28b commit 09d4b40
Showing 1 changed file with 34 additions and 11 deletions.
45 changes: 34 additions & 11 deletions src/main/java/run/halo/app/config/WebServerSecurityConfig.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -40,18 +47,15 @@ SecurityWebFilterChain apiFilterChain(ServerHttpSecurity http,
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
Expand All @@ -61,6 +65,25 @@ SecurityWebFilterChain apiFilterChain(ServerHttpSecurity http,
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) {
Expand Down

0 comments on commit 09d4b40

Please sign in to comment.