6 changed files with 192 additions and 93 deletions
@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
|
||||
/* |
||||
* Copyright 2002-2017 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.oauth2.client.oidc.userinfo; |
||||
|
||||
import com.nimbusds.oauth2.sdk.ErrorObject; |
||||
import com.nimbusds.oauth2.sdk.ParseException; |
||||
import com.nimbusds.oauth2.sdk.http.HTTPRequest; |
||||
import com.nimbusds.oauth2.sdk.http.HTTPResponse; |
||||
import com.nimbusds.oauth2.sdk.token.BearerAccessToken; |
||||
import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse; |
||||
import com.nimbusds.openid.connect.sdk.UserInfoRequest; |
||||
import org.springframework.core.ParameterizedTypeReference; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.client.AbstractClientHttpResponse; |
||||
import org.springframework.http.client.ClientHttpResponse; |
||||
import org.springframework.http.converter.GenericHttpMessageConverter; |
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; |
||||
import org.springframework.security.authentication.AuthenticationServiceException; |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration; |
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; |
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken; |
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
||||
import org.springframework.security.oauth2.core.OAuth2Error; |
||||
import org.springframework.util.Assert; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.net.URI; |
||||
import java.nio.charset.Charset; |
||||
|
||||
/** |
||||
* NOTE: This is a straight copy of org.springframework.security.oauth2.client.userinfo.NimbusUserInfoResponseClient |
||||
* |
||||
* @author Joe Grandja |
||||
* @since 5.0 |
||||
*/ |
||||
final class NimbusUserInfoResponseClient { |
||||
private static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response"; |
||||
private final GenericHttpMessageConverter genericHttpMessageConverter = new MappingJackson2HttpMessageConverter(); |
||||
|
||||
<T> T getUserInfoResponse(OAuth2UserRequest userInfoRequest, Class<T> returnType) throws OAuth2AuthenticationException { |
||||
ClientHttpResponse userInfoResponse = this.getUserInfoResponse( |
||||
userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken()); |
||||
try { |
||||
return (T) this.genericHttpMessageConverter.read(returnType, userInfoResponse); |
||||
} catch (IOException ex) { |
||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, |
||||
"An error occurred reading the UserInfo Success response: " + ex.getMessage(), null); |
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); |
||||
} |
||||
} |
||||
|
||||
<T> T getUserInfoResponse(OAuth2UserRequest userInfoRequest, ParameterizedTypeReference<T> typeReference) throws OAuth2AuthenticationException { |
||||
ClientHttpResponse userInfoResponse = this.getUserInfoResponse( |
||||
userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken()); |
||||
try { |
||||
return (T) this.genericHttpMessageConverter.read(typeReference.getType(), null, userInfoResponse); |
||||
} catch (IOException ex) { |
||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, |
||||
"An error occurred reading the UserInfo Success response: " + ex.getMessage(), null); |
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); |
||||
} |
||||
} |
||||
|
||||
private ClientHttpResponse getUserInfoResponse(ClientRegistration clientRegistration, |
||||
OAuth2AccessToken oauth2AccessToken) throws OAuth2AuthenticationException { |
||||
URI userInfoUri = URI.create(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri()); |
||||
BearerAccessToken accessToken = new BearerAccessToken(oauth2AccessToken.getTokenValue()); |
||||
|
||||
UserInfoRequest userInfoRequest = new UserInfoRequest(userInfoUri, accessToken); |
||||
HTTPRequest httpRequest = userInfoRequest.toHTTPRequest(); |
||||
httpRequest.setConnectTimeout(30000); |
||||
httpRequest.setReadTimeout(30000); |
||||
HTTPResponse httpResponse; |
||||
|
||||
try { |
||||
httpResponse = httpRequest.send(); |
||||
} catch (IOException ex) { |
||||
throw new AuthenticationServiceException("An error occurred while sending the UserInfo Request: " + |
||||
ex.getMessage(), ex); |
||||
} |
||||
|
||||
if (httpResponse.getStatusCode() == HTTPResponse.SC_OK) { |
||||
return new NimbusClientHttpResponse(httpResponse); |
||||
} |
||||
|
||||
UserInfoErrorResponse userInfoErrorResponse; |
||||
try { |
||||
userInfoErrorResponse = UserInfoErrorResponse.parse(httpResponse); |
||||
} catch (ParseException ex) { |
||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, |
||||
"An error occurred parsing the UserInfo Error response: " + ex.getMessage(), null); |
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); |
||||
} |
||||
ErrorObject errorObject = userInfoErrorResponse.getErrorObject(); |
||||
|
||||
StringBuilder errorDescription = new StringBuilder(); |
||||
errorDescription.append("An error occurred while attempting to access the UserInfo Endpoint -> "); |
||||
errorDescription.append("Error details: ["); |
||||
errorDescription.append("UserInfo Uri: ").append(userInfoUri.toString()); |
||||
errorDescription.append(", Http Status: ").append(errorObject.getHTTPStatusCode()); |
||||
if (errorObject.getCode() != null) { |
||||
errorDescription.append(", Error Code: ").append(errorObject.getCode()); |
||||
} |
||||
if (errorObject.getDescription() != null) { |
||||
errorDescription.append(", Error Description: ").append(errorObject.getDescription()); |
||||
} |
||||
errorDescription.append("]"); |
||||
|
||||
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, errorDescription.toString(), null); |
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); |
||||
} |
||||
|
||||
private static class NimbusClientHttpResponse extends AbstractClientHttpResponse { |
||||
private final HTTPResponse httpResponse; |
||||
private final HttpHeaders headers; |
||||
|
||||
private NimbusClientHttpResponse(HTTPResponse httpResponse) { |
||||
Assert.notNull(httpResponse, "httpResponse cannot be null"); |
||||
this.httpResponse = httpResponse; |
||||
this.headers = new HttpHeaders(); |
||||
this.headers.setAll(httpResponse.getHeaders()); |
||||
} |
||||
|
||||
@Override |
||||
public int getRawStatusCode() throws IOException { |
||||
return this.httpResponse.getStatusCode(); |
||||
} |
||||
|
||||
@Override |
||||
public String getStatusText() throws IOException { |
||||
return String.valueOf(this.getRawStatusCode()); |
||||
} |
||||
|
||||
@Override |
||||
public void close() { |
||||
} |
||||
|
||||
@Override |
||||
public InputStream getBody() throws IOException { |
||||
InputStream inputStream = new ByteArrayInputStream( |
||||
this.httpResponse.getContent().getBytes(Charset.forName("UTF-8"))); |
||||
return inputStream; |
||||
} |
||||
|
||||
@Override |
||||
public HttpHeaders getHeaders() { |
||||
return this.headers; |
||||
} |
||||
} |
||||
} |
||||
@ -1,38 +0,0 @@
@@ -1,38 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 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.oauth2.client.userinfo; |
||||
|
||||
import org.springframework.core.ParameterizedTypeReference; |
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
||||
|
||||
/** |
||||
* A strategy for retrieving the user attributes |
||||
* of the <i>End-User</i> (Resource Owner) from the <i>UserInfo Endpoint</i> |
||||
* using the provided {@link OAuth2UserRequest#getAccessToken() Access Token}. |
||||
* |
||||
* @author Joe Grandja |
||||
* @author Rob Winch |
||||
* @since 5.0 |
||||
* @see OAuth2UserRequest |
||||
* @see OAuth2UserService |
||||
*/ |
||||
public interface UserInfoRetriever { |
||||
|
||||
<T> T retrieve(OAuth2UserRequest userRequest, Class<T> responseType) throws OAuth2AuthenticationException; |
||||
|
||||
<T> T retrieve(OAuth2UserRequest userRequest, ParameterizedTypeReference<T> typeReference) throws OAuth2AuthenticationException; |
||||
|
||||
} |
||||
Loading…
Reference in new issue