1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.security.oauth2.client.http;
17
18 import org.springframework.http.HttpHeaders;
19 import org.springframework.http.HttpStatus;
20 import org.springframework.http.client.ClientHttpResponse;
21 import org.springframework.http.converter.HttpMessageConversionException;
22 import org.springframework.http.converter.HttpMessageConverter;
23 import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
24 import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
25 import org.springframework.security.oauth2.common.OAuth2AccessToken;
26 import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
27 import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
28 import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
29 import org.springframework.util.FileCopyUtils;
30 import org.springframework.web.client.*;
31
32 import java.io.ByteArrayInputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.util.List;
36 import java.util.Map;
37
38
39
40
41
42 public class OAuth2ErrorHandler implements ResponseErrorHandler {
43
44 private final ResponseErrorHandler errorHandler;
45
46 private final OAuth2ProtectedResourceDetails resource;
47
48 private List<HttpMessageConverter<?>> messageConverters = new RestTemplate().getMessageConverters();
49
50
51
52
53 public OAuth2ErrorHandler(OAuth2ProtectedResourceDetails resource) {
54 this.resource = resource;
55 this.errorHandler = new DefaultResponseErrorHandler();
56 }
57
58
59
60
61 public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
62 this.messageConverters = messageConverters;
63 }
64
65
66
67
68
69
70 public OAuth2ErrorHandler(ResponseErrorHandler errorHandler, OAuth2ProtectedResourceDetails resource) {
71 this.resource = resource;
72 this.errorHandler = errorHandler;
73 }
74
75 public boolean hasError(ClientHttpResponse response) throws IOException {
76 return HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())
77 || this.errorHandler.hasError(response);
78 }
79
80 public void handleError(final ClientHttpResponse response) throws IOException {
81 if (!HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())) {
82
83
84 errorHandler.handleError(response);
85 }
86 else {
87
88 ClientHttpResponse bufferedResponse = new ClientHttpResponse() {
89 private byte[] lazyBody;
90
91 public HttpStatus getStatusCode() throws IOException {
92 return response.getStatusCode();
93 }
94
95 public synchronized InputStream getBody() throws IOException {
96 if (lazyBody == null) {
97 InputStream bodyStream = response.getBody();
98 if (bodyStream != null) {
99 lazyBody = FileCopyUtils.copyToByteArray(bodyStream);
100 }
101 else {
102 lazyBody = new byte[0];
103 }
104 }
105 return new ByteArrayInputStream(lazyBody);
106 }
107
108 public HttpHeaders getHeaders() {
109 return response.getHeaders();
110 }
111
112 public String getStatusText() throws IOException {
113 return response.getStatusText();
114 }
115
116 public void close() {
117 response.close();
118 }
119
120 public int getRawStatusCode() throws IOException {
121 return this.getStatusCode().value();
122 }
123 };
124
125 try {
126 HttpMessageConverterExtractor<OAuth2Exception> extractor = new HttpMessageConverterExtractor<OAuth2Exception>(
127 OAuth2Exception.class, messageConverters);
128 try {
129 OAuth2Exception oauth2Exception = extractor.extractData(bufferedResponse);
130 if (oauth2Exception != null) {
131
132 if (oauth2Exception.getClass() == UserDeniedAuthorizationException.class &&
133 bufferedResponse.getStatusCode().equals(HttpStatus.FORBIDDEN)) {
134 oauth2Exception = new OAuth2AccessDeniedException(oauth2Exception.getMessage());
135 }
136
137
138 throw oauth2Exception;
139 }
140 }
141 catch (RestClientException e) {
142
143 }
144 catch (HttpMessageConversionException e){
145
146 }
147
148
149 List<String> authenticateHeaders = bufferedResponse.getHeaders().get("WWW-Authenticate");
150 if (authenticateHeaders != null) {
151 for (String authenticateHeader : authenticateHeaders) {
152 maybeThrowExceptionFromHeader(authenticateHeader, OAuth2AccessToken.BEARER_TYPE);
153 maybeThrowExceptionFromHeader(authenticateHeader, OAuth2AccessToken.OAUTH2_TYPE);
154 }
155 }
156
157
158 errorHandler.handleError(bufferedResponse);
159 }
160 catch (InvalidTokenException ex) {
161
162 throw new AccessTokenRequiredException(resource);
163 }
164 catch (OAuth2Exception ex) {
165 if (!ex.getClass().equals(OAuth2Exception.class)) {
166
167
168 throw ex;
169 }
170
171
172 errorHandler.handleError(bufferedResponse);
173 }
174 }
175 }
176
177 private void maybeThrowExceptionFromHeader(String authenticateHeader, String headerType) {
178 headerType = headerType.toLowerCase();
179 if (authenticateHeader.toLowerCase().startsWith(headerType)) {
180 Map<String, String> headerEntries = StringSplitUtils.splitEachArrayElementAndCreateMap(
181 StringSplitUtils.splitIgnoringQuotes(authenticateHeader.substring(headerType.length()), ','), "=",
182 "\"");
183 OAuth2Exception ex = OAuth2Exception.valueOf(headerEntries);
184 if (ex instanceof InvalidTokenException) {
185
186 throw new AccessTokenRequiredException(resource);
187 }
188 throw ex;
189 }
190 }
191
192 }