|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2021 the original author or authors. |
| 2 | + * Copyright 2002-2024 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
17 | 17 | package org.springframework.security.oauth2.server.resource.web.server.authentication;
|
18 | 18 |
|
19 | 19 | import java.util.List;
|
20 |
| -import java.util.regex.Matcher; |
21 |
| -import java.util.regex.Pattern; |
22 | 20 |
|
23 | 21 | import reactor.core.publisher.Mono;
|
24 | 22 |
|
25 |
| -import org.springframework.http.HttpHeaders; |
26 | 23 | import org.springframework.http.HttpMethod;
|
27 | 24 | import org.springframework.http.server.reactive.ServerHttpRequest;
|
28 | 25 | import org.springframework.security.core.Authentication;
|
29 |
| -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
30 |
| -import org.springframework.security.oauth2.server.resource.BearerTokenError; |
31 |
| -import org.springframework.security.oauth2.server.resource.BearerTokenErrors; |
32 |
| -import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken; |
| 26 | +import org.springframework.security.oauth2.server.resource.web.AbstractBearerTokenAuthenticationConverter; |
33 | 27 | import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
|
34 |
| -import org.springframework.util.CollectionUtils; |
35 |
| -import org.springframework.util.StringUtils; |
36 | 28 | import org.springframework.web.server.ServerWebExchange;
|
37 | 29 |
|
38 | 30 | /**
|
|
45 | 37 | * @see <a href="https://tools.ietf.org/html/rfc6750#section-2" target="_blank">RFC 6750
|
46 | 38 | * Section 2: Authenticated Requests</a>
|
47 | 39 | */
|
48 |
| -public class ServerBearerTokenAuthenticationConverter implements ServerAuthenticationConverter { |
49 |
| - |
50 |
| - private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$", |
51 |
| - Pattern.CASE_INSENSITIVE); |
52 |
| - |
53 |
| - private boolean allowUriQueryParameter = false; |
54 |
| - |
55 |
| - private String bearerTokenHeaderName = HttpHeaders.AUTHORIZATION; |
| 40 | +public class ServerBearerTokenAuthenticationConverter |
| 41 | + extends AbstractBearerTokenAuthenticationConverter<ServerHttpRequest> implements ServerAuthenticationConverter { |
56 | 42 |
|
57 | 43 | @Override
|
58 | 44 | public Mono<Authentication> convert(ServerWebExchange exchange) {
|
59 |
| - return Mono.fromCallable(() -> token(exchange.getRequest())).map((token) -> { |
60 |
| - if (token.isEmpty()) { |
61 |
| - BearerTokenError error = invalidTokenError(); |
62 |
| - throw new OAuth2AuthenticationException(error); |
63 |
| - } |
64 |
| - return new BearerTokenAuthenticationToken(token); |
65 |
| - }); |
| 45 | + // @formatter:off |
| 46 | + return Mono.fromCallable(() -> token(exchange.getRequest())) |
| 47 | + .map(this::convertBearerToken); |
| 48 | + // @formatter:on |
66 | 49 | }
|
67 | 50 |
|
68 |
| - private String token(ServerHttpRequest request) { |
69 |
| - String authorizationHeaderToken = resolveFromAuthorizationHeader(request.getHeaders()); |
70 |
| - String parameterToken = resolveAccessTokenFromRequest(request); |
71 |
| - |
72 |
| - if (authorizationHeaderToken != null) { |
73 |
| - if (parameterToken != null) { |
74 |
| - BearerTokenError error = BearerTokenErrors |
75 |
| - .invalidRequest("Found multiple bearer tokens in the request"); |
76 |
| - throw new OAuth2AuthenticationException(error); |
77 |
| - } |
78 |
| - return authorizationHeaderToken; |
79 |
| - } |
80 |
| - if (parameterToken != null && isParameterTokenSupportedForRequest(request)) { |
81 |
| - return parameterToken; |
82 |
| - } |
83 |
| - return null; |
84 |
| - } |
85 |
| - |
86 |
| - private static String resolveAccessTokenFromRequest(ServerHttpRequest request) { |
87 |
| - List<String> parameterTokens = request.getQueryParams().get("access_token"); |
88 |
| - if (CollectionUtils.isEmpty(parameterTokens)) { |
89 |
| - return null; |
90 |
| - } |
91 |
| - if (parameterTokens.size() == 1) { |
92 |
| - return parameterTokens.get(0); |
93 |
| - } |
94 |
| - |
95 |
| - BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request"); |
96 |
| - throw new OAuth2AuthenticationException(error); |
97 |
| - |
98 |
| - } |
99 |
| - |
100 |
| - /** |
101 |
| - * Set if transport of access token using URI query parameter is supported. Defaults |
102 |
| - * to {@code false}. |
103 |
| - * |
104 |
| - * The spec recommends against using this mechanism for sending bearer tokens, and |
105 |
| - * even goes as far as stating that it was only included for completeness. |
106 |
| - * @param allowUriQueryParameter if the URI query parameter is supported |
107 |
| - */ |
108 |
| - public void setAllowUriQueryParameter(boolean allowUriQueryParameter) { |
109 |
| - this.allowUriQueryParameter = allowUriQueryParameter; |
110 |
| - } |
111 |
| - |
112 |
| - /** |
113 |
| - * Set this value to configure what header is checked when resolving a Bearer Token. |
114 |
| - * This value is defaulted to {@link HttpHeaders#AUTHORIZATION}. |
115 |
| - * |
116 |
| - * This allows other headers to be used as the Bearer Token source such as |
117 |
| - * {@link HttpHeaders#PROXY_AUTHORIZATION} |
118 |
| - * @param bearerTokenHeaderName the header to check when retrieving the Bearer Token. |
119 |
| - * @since 5.4 |
120 |
| - */ |
121 |
| - public void setBearerTokenHeaderName(String bearerTokenHeaderName) { |
122 |
| - this.bearerTokenHeaderName = bearerTokenHeaderName; |
123 |
| - } |
124 |
| - |
125 |
| - private String resolveFromAuthorizationHeader(HttpHeaders headers) { |
126 |
| - String authorization = headers.getFirst(this.bearerTokenHeaderName); |
127 |
| - if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) { |
128 |
| - return null; |
129 |
| - } |
130 |
| - Matcher matcher = authorizationPattern.matcher(authorization); |
131 |
| - if (!matcher.matches()) { |
132 |
| - BearerTokenError error = invalidTokenError(); |
133 |
| - throw new OAuth2AuthenticationException(error); |
134 |
| - } |
135 |
| - return matcher.group("token"); |
| 51 | + @Override |
| 52 | + protected String resolveAuthorizationHeaderToken(ServerHttpRequest request) { |
| 53 | + String authorization = request.getHeaders().getFirst(this.bearerTokenHeaderName); |
| 54 | + return resolveFromAuthorizationHeader(authorization); |
136 | 55 | }
|
137 | 56 |
|
138 |
| - private static BearerTokenError invalidTokenError() { |
139 |
| - return BearerTokenErrors.invalidToken("Bearer token is malformed"); |
| 57 | + @Override |
| 58 | + protected List<String> resolveParameterTokens(ServerHttpRequest request) { |
| 59 | + return request.getQueryParams().get("access_token"); |
140 | 60 | }
|
141 | 61 |
|
142 |
| - private boolean isParameterTokenSupportedForRequest(ServerHttpRequest request) { |
143 |
| - return this.allowUriQueryParameter && HttpMethod.GET.equals(request.getMethod()); |
| 62 | + @Override |
| 63 | + protected HttpMethod getHttpMethod(ServerHttpRequest request) { |
| 64 | + return request.getMethod(); |
144 | 65 | }
|
145 | 66 |
|
146 | 67 | }
|
0 commit comments