Browse Source
1. Created new WebFilter AnonymousAuthenticationWebFilter to for anonymous authentication 2. Created class AnonymousSpec, method anonymous to configure anonymous authentication in ServerHttpSecurity 3. Added ANONYMOUS_AUTHENTICATION order after AUTHENTICATION for anonymous authentication in SecurityWebFiltersOrder 4. Added tests for anonymous authentication in AnonymousAuthenticationWebFilterTests and ServerHttpSecurityTests 5. Added support for Controller in WebTestClientBuilder Fixes: gh-5934pull/6279/head
6 changed files with 370 additions and 3 deletions
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.web.server.authentication; |
||||
|
||||
import java.util.List; |
||||
|
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.core.GrantedAuthority; |
||||
import org.springframework.security.core.authority.AuthorityUtils; |
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder; |
||||
import org.springframework.security.core.context.SecurityContext; |
||||
import org.springframework.security.core.context.SecurityContextImpl; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
import org.springframework.web.server.WebFilter; |
||||
import org.springframework.web.server.WebFilterChain; |
||||
|
||||
/** |
||||
* Detects if there is no {@code Authentication} object in the |
||||
* {@code ReactiveSecurityContextHolder}, and populates it with one if needed. |
||||
* |
||||
* @author Ankur Pathak |
||||
* @since 5.2.0 |
||||
*/ |
||||
public class AnonymousAuthenticationWebFilter implements WebFilter { |
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
private String key; |
||||
private Object principal; |
||||
private List<GrantedAuthority> authorities; |
||||
|
||||
/** |
||||
* Creates a filter with a principal named "anonymousUser" and the single authority |
||||
* "ROLE_ANONYMOUS". |
||||
* |
||||
* @param key the key to identify tokens created by this filter |
||||
*/ |
||||
public AnonymousAuthenticationWebFilter(String key) { |
||||
this(key, "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); |
||||
} |
||||
|
||||
/** |
||||
* @param key key the key to identify tokens created by this filter |
||||
* @param principal the principal which will be used to represent anonymous users |
||||
* @param authorities the authority list for anonymous users |
||||
*/ |
||||
public AnonymousAuthenticationWebFilter(String key, Object principal, |
||||
List<GrantedAuthority> authorities) { |
||||
Assert.hasLength(key, "key cannot be null or empty"); |
||||
Assert.notNull(principal, "Anonymous authentication principal must be set"); |
||||
Assert.notNull(authorities, "Anonymous authorities must be set"); |
||||
this.key = key; |
||||
this.principal = principal; |
||||
this.authorities = authorities; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { |
||||
return ReactiveSecurityContextHolder.getContext() |
||||
.switchIfEmpty(Mono.defer(() -> { |
||||
SecurityContext securityContext = new SecurityContextImpl(); |
||||
securityContext.setAuthentication(createAuthentication(exchange)); |
||||
return chain.filter(exchange) |
||||
.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext))) |
||||
.then(Mono.empty()); |
||||
})).flatMap(securityContext -> chain.filter(exchange)); |
||||
|
||||
} |
||||
|
||||
protected Authentication createAuthentication(ServerWebExchange exchange) { |
||||
AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key, |
||||
principal, authorities); |
||||
return auth; |
||||
} |
||||
} |
||||
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.web.server.authentication; |
||||
|
||||
import java.util.UUID; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder; |
||||
import org.springframework.security.core.context.SecurityContext; |
||||
import org.springframework.test.web.reactive.server.WebTestClient; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; |
||||
|
||||
/** |
||||
* @author Ankur Pathak |
||||
* @since 5.2.0 |
||||
*/ |
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class AnonymousAuthenticationWebFilterTests { |
||||
|
||||
@Test |
||||
public void anonymousAuthenticationFilterWorking() { |
||||
|
||||
WebTestClient client = WebTestClientBuilder.bindToControllerAndWebFilters(HttpMeController.class, |
||||
new AnonymousAuthenticationWebFilter(UUID.randomUUID().toString())) |
||||
.build(); |
||||
|
||||
client.get() |
||||
.uri("/me") |
||||
.exchange() |
||||
.expectStatus().isOk() |
||||
.expectBody(String.class).isEqualTo("anonymousUser"); |
||||
} |
||||
|
||||
@RestController |
||||
@RequestMapping("/me") |
||||
public static class HttpMeController { |
||||
@GetMapping |
||||
public Mono<String> me(ServerWebExchange exchange) { |
||||
return ReactiveSecurityContextHolder |
||||
.getContext() |
||||
.map(SecurityContext::getAuthentication) |
||||
.map(Authentication::getPrincipal) |
||||
.ofType(String.class); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue