mirror of https://github.com/halo-dev/halo
				
				
				
			Fix the problem of redirecting to previous URI with fragment after authenticated (#6862)
#### What type of PR is this? /kind bug /area core /milestone 2.20.x #### What this PR does / why we need it: This PR ignores URI fragment while removing redirect URI. Before that, users may be redirected to previous redirect URI that contains fragment. #### Does this PR introduce a user-facing change? ```release-note 修复二次登录后重定向跳转至旧地址的问题 ```pull/6865/head v2.20.1
							parent
							
								
									17eea823a5
								
							
						
					
					
						commit
						dd5f02e505
					
				| 
						 | 
				
			
			@ -4,6 +4,7 @@ import static org.springframework.security.web.server.util.matcher.ServerWebExch
 | 
			
		|||
 | 
			
		||||
import java.net.URI;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.springframework.http.HttpMethod;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
| 
						 | 
				
			
			@ -58,24 +59,49 @@ public class HaloServerRequestCache extends WebSessionServerRequestCache {
 | 
			
		|||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Mono<ServerHttpRequest> removeMatchingRequest(ServerWebExchange exchange) {
 | 
			
		||||
        return super.removeMatchingRequest(exchange);
 | 
			
		||||
        return getRedirectUri(exchange)
 | 
			
		||||
            .flatMap(redirectUri -> {
 | 
			
		||||
                if (redirectUri.getFragment() != null) {
 | 
			
		||||
                    var redirectUriInApplication =
 | 
			
		||||
                        uriInApplication(exchange.getRequest(), redirectUri, false);
 | 
			
		||||
                    var uriInApplication =
 | 
			
		||||
                        uriInApplication(exchange.getRequest(), exchange.getRequest().getURI());
 | 
			
		||||
                    // compare the path and query only
 | 
			
		||||
                    if (!Objects.equals(redirectUriInApplication, uriInApplication)) {
 | 
			
		||||
                        return Mono.empty();
 | 
			
		||||
                    }
 | 
			
		||||
                    // remove the exchange
 | 
			
		||||
                    return exchange.getSession().map(WebSession::getAttributes)
 | 
			
		||||
                        .doOnNext(attributes -> attributes.remove(this.sessionAttrName))
 | 
			
		||||
                        .thenReturn(exchange.getRequest());
 | 
			
		||||
                }
 | 
			
		||||
                return super.removeMatchingRequest(exchange);
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Mono<Void> saveRedirectUri(ServerWebExchange exchange, URI redirectUri) {
 | 
			
		||||
        var requestPath = exchange.getRequest().getPath();
 | 
			
		||||
        var redirectPath = RequestPath.parse(redirectUri, requestPath.contextPath().value());
 | 
			
		||||
        var query = redirectUri.getRawQuery();
 | 
			
		||||
        var fragment = redirectUri.getRawFragment();
 | 
			
		||||
        var finalRedirect = redirectPath.pathWithinApplication()
 | 
			
		||||
            + (query == null ? "" : "?" + query)
 | 
			
		||||
            + (fragment == null ? "" : "#" + fragment);
 | 
			
		||||
 | 
			
		||||
        var redirectUriInApplication = uriInApplication(exchange.getRequest(), redirectUri);
 | 
			
		||||
        return exchange.getSession()
 | 
			
		||||
            .map(WebSession::getAttributes)
 | 
			
		||||
            .doOnNext(attributes -> attributes.put(this.sessionAttrName, finalRedirect))
 | 
			
		||||
            .doOnNext(attributes -> attributes.put(this.sessionAttrName, redirectUriInApplication))
 | 
			
		||||
            .then();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String uriInApplication(ServerHttpRequest request, URI uri) {
 | 
			
		||||
        return uriInApplication(request, uri, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String uriInApplication(
 | 
			
		||||
        ServerHttpRequest request, URI uri, boolean appendFragment
 | 
			
		||||
    ) {
 | 
			
		||||
        var path = RequestPath.parse(uri, request.getPath().contextPath().value());
 | 
			
		||||
        var query = uri.getRawQuery();
 | 
			
		||||
        var fragment = uri.getRawFragment();
 | 
			
		||||
        return path.pathWithinApplication().value()
 | 
			
		||||
            + (query == null ? "" : "?" + query)
 | 
			
		||||
            + (fragment == null || !appendFragment ? "" : "#" + fragment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ServerWebExchangeMatcher createDefaultRequestMatcher() {
 | 
			
		||||
        var get = pathMatchers(HttpMethod.GET, "/**");
 | 
			
		||||
        var notFavicon = new NegatedServerWebExchangeMatcher(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,9 +59,31 @@ class HaloServerRequestCacheTest {
 | 
			
		|||
 | 
			
		||||
    @Test
 | 
			
		||||
    void shouldRemoveIfRedirectUriFound() {
 | 
			
		||||
        var sessionManager = new DefaultWebSessionManager();
 | 
			
		||||
        var mockExchange = MockServerWebExchange.builder(MockServerHttpRequest.get("/login")
 | 
			
		||||
                .queryParam("redirect_uri", "/halo")
 | 
			
		||||
            )
 | 
			
		||||
            .sessionManager(sessionManager)
 | 
			
		||||
            .build();
 | 
			
		||||
        var removeExchange = mockExchange.mutate()
 | 
			
		||||
            .request(builder -> builder.uri(URI.create("/halo")))
 | 
			
		||||
            .build();
 | 
			
		||||
        requestCache.saveRequest(mockExchange)
 | 
			
		||||
            .then(Mono.defer(() -> requestCache.removeMatchingRequest(removeExchange)))
 | 
			
		||||
            .as(StepVerifier::create)
 | 
			
		||||
            .assertNext(request -> {
 | 
			
		||||
                Assertions.assertEquals(URI.create("/halo"), request.getURI());
 | 
			
		||||
            })
 | 
			
		||||
            .verifyComplete();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void shouldRemoveIfRedirectUriFoundAndContainsFragment() {
 | 
			
		||||
        var sessionManager = new DefaultWebSessionManager();
 | 
			
		||||
        var mockExchange =
 | 
			
		||||
            MockServerWebExchange.builder(MockServerHttpRequest.get("/login?redirect_uri=/halo"))
 | 
			
		||||
            MockServerWebExchange.builder(MockServerHttpRequest.get("/login")
 | 
			
		||||
                    .queryParam("redirect_uri", "/halo#fragment")
 | 
			
		||||
                )
 | 
			
		||||
                .sessionManager(sessionManager)
 | 
			
		||||
                .build();
 | 
			
		||||
        var removeExchange = mockExchange.mutate()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue