diff --git a/src/main/java/com/monkeyk/sos/config/WebSecurityConfigurer.java b/src/main/java/com/monkeyk/sos/config/WebSecurityConfigurer.java index 9ed99c2..514b76b 100644 --- a/src/main/java/com/monkeyk/sos/config/WebSecurityConfigurer.java +++ b/src/main/java/com/monkeyk/sos/config/WebSecurityConfigurer.java @@ -53,7 +53,7 @@ public class WebSecurityConfigurer { http.authorizeHttpRequests(matcherRegistry -> { // permitAll() 的URL路径属于公开访问,不需要权限 matcherRegistry - .requestMatchers("/favicon.ico*", "/oauth/rest_token*", "*.js", "*.css").permitAll() + .requestMatchers("/favicon.ico*", "/oauth2/rest_token*", "*.js", "*.css").permitAll() .requestMatchers("/api/public/**").permitAll() .requestMatchers(HttpMethod.GET, "/login*").anonymous() diff --git a/src/main/java/com/monkeyk/sos/web/authentication/AbstractAuthenticationRestConverter.java b/src/main/java/com/monkeyk/sos/web/authentication/AbstractAuthenticationRestConverter.java new file mode 100644 index 0000000..fa49edd --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/authentication/AbstractAuthenticationRestConverter.java @@ -0,0 +1,22 @@ +package com.monkeyk.sos.web.authentication; + +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2Error; + +/** + * 2023/10/31 10:35 + * + * @author Shengzhao Li + * @since 3.0.0 + */ +public abstract class AbstractAuthenticationRestConverter implements AuthenticationRestConverter { + + static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + + + protected void throwError(String errorCode, String parameterName, String errorUri) { + OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri); + throw new OAuth2AuthenticationException(error); + } + +} diff --git a/src/main/java/com/monkeyk/sos/web/authentication/AuthenticationRestConverter.java b/src/main/java/com/monkeyk/sos/web/authentication/AuthenticationRestConverter.java new file mode 100644 index 0000000..778c029 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/authentication/AuthenticationRestConverter.java @@ -0,0 +1,24 @@ +package com.monkeyk.sos.web.authentication; + +import org.springframework.security.core.Authentication; + +import java.util.Map; + +/** + * 2023/10/31 10:27 + * + * @author Shengzhao Li + * @see org.springframework.security.web.authentication.AuthenticationConverter + * @since 3.0.0 + */ +public interface AuthenticationRestConverter { + + /** + * 从请求参数中转化到 Authentication + * + * @param parameters 请求参数 + * @return Authentication or null + */ + Authentication convert(Map parameters); + +} diff --git a/src/main/java/com/monkeyk/sos/web/authentication/DelegatingAuthenticationRestConverter.java b/src/main/java/com/monkeyk/sos/web/authentication/DelegatingAuthenticationRestConverter.java new file mode 100644 index 0000000..d92db80 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/authentication/DelegatingAuthenticationRestConverter.java @@ -0,0 +1,45 @@ +package com.monkeyk.sos.web.authentication; + +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationConverter; +import org.springframework.util.Assert; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * 2023/10/31 10:30 + * + * @author Shengzhao Li + * @see org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter + * @since 3.0.0 + */ +public final class DelegatingAuthenticationRestConverter implements AuthenticationRestConverter { + + private final List converters; + + /** + * Constructs a {@code DelegatingAuthenticationConverter} using the provided parameters. + * + * @param converters a {@code List} of {@link AuthenticationConverter}(s) + */ + public DelegatingAuthenticationRestConverter(List converters) { + Assert.notEmpty(converters, "converters cannot be empty"); + this.converters = Collections.unmodifiableList(new LinkedList<>(converters)); + } + + + @Override + public Authentication convert(Map parameters) { + Assert.notNull(parameters, "parameters cannot be null"); + for (AuthenticationRestConverter converter : this.converters) { + Authentication authentication = converter.convert(parameters); + if (authentication != null) { + return authentication; + } + } + return null; + } +} diff --git a/src/main/java/com/monkeyk/sos/web/authentication/OAuth2AuthorizationCodeAuthenticationRestConverter.java b/src/main/java/com/monkeyk/sos/web/authentication/OAuth2AuthorizationCodeAuthenticationRestConverter.java new file mode 100644 index 0000000..299afe1 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/authentication/OAuth2AuthorizationCodeAuthenticationRestConverter.java @@ -0,0 +1,69 @@ +package com.monkeyk.sos.web.authentication; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.OAuth2ErrorCodes; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken; +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * 2023/10/31 10:33 + * + * @author Shengzhao Li + * @see org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter + * @since 3.0.0 + */ +public final class OAuth2AuthorizationCodeAuthenticationRestConverter extends AbstractAuthenticationRestConverter { + + + @Override + public Authentication convert(Map parameters) { + // grant_type (REQUIRED) + String grantType = parameters.get(OAuth2ParameterNames.GRANT_TYPE); + if (!AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(grantType)) { + return null; + } + + Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); + +// MultiValueMap parameters = OAuth2EndpointUtils.getParameters(request); + + // code (REQUIRED) + String code = parameters.get(OAuth2ParameterNames.CODE); + if (!StringUtils.hasText(code)) { + throwError( + OAuth2ErrorCodes.INVALID_REQUEST, + OAuth2ParameterNames.CODE, + ACCESS_TOKEN_REQUEST_ERROR_URI); + } + + // redirect_uri (REQUIRED) + // Required only if the "redirect_uri" parameter was included in the authorization request + String redirectUri = parameters.get(OAuth2ParameterNames.REDIRECT_URI); + if (!StringUtils.hasText(redirectUri)) { + throwError( + OAuth2ErrorCodes.INVALID_REQUEST, + OAuth2ParameterNames.REDIRECT_URI, + ACCESS_TOKEN_REQUEST_ERROR_URI); + } + + Map additionalParameters = new HashMap<>(); + parameters.forEach((key, value) -> { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && + !key.equals(OAuth2ParameterNames.CLIENT_ID) && + !key.equals(OAuth2ParameterNames.CODE) && + !key.equals(OAuth2ParameterNames.REDIRECT_URI)) { +// additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); + additionalParameters.put(key, value); + } + }); + + return new OAuth2AuthorizationCodeAuthenticationToken( + code, clientPrincipal, redirectUri, additionalParameters); + } +} diff --git a/src/main/java/com/monkeyk/sos/web/authentication/OAuth2ClientCredentialsAuthenticationRestConverter.java b/src/main/java/com/monkeyk/sos/web/authentication/OAuth2ClientCredentialsAuthenticationRestConverter.java new file mode 100644 index 0000000..b467227 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/authentication/OAuth2ClientCredentialsAuthenticationRestConverter.java @@ -0,0 +1,60 @@ +package com.monkeyk.sos.web.authentication; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken; +import org.springframework.util.StringUtils; + +import java.util.*; + +/** + * 2023/10/31 10:33 + * + * @author Shengzhao Li + * @see org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter + * @since 3.0.0 + */ +public final class OAuth2ClientCredentialsAuthenticationRestConverter extends AbstractAuthenticationRestConverter { + + + @Override + public Authentication convert(Map parameters) { + // grant_type (REQUIRED) + String grantType = parameters.get(OAuth2ParameterNames.GRANT_TYPE); + if (!AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(grantType)) { + return null; + } + + Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); + +// MultiValueMap parameters = OAuth2EndpointUtils.getParameters(request); + + // scope (OPTIONAL) + String scope = parameters.get(OAuth2ParameterNames.SCOPE); +// if (StringUtils.hasText(scope) && +// parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { +// throwError( +// OAuth2ErrorCodes.INVALID_REQUEST, +// OAuth2ParameterNames.SCOPE, +// ACCESS_TOKEN_REQUEST_ERROR_URI); +// } + Set requestedScopes = null; + if (StringUtils.hasText(scope)) { + requestedScopes = new HashSet<>( + Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); + } + + Map additionalParameters = new HashMap<>(); + parameters.forEach((key, value) -> { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && + !key.equals(OAuth2ParameterNames.SCOPE)) { + additionalParameters.put(key, value); + } + }); + + return new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, requestedScopes, additionalParameters); + } +} diff --git a/src/main/java/com/monkeyk/sos/web/authentication/OAuth2DeviceCodeAuthenticationRestConverter.java b/src/main/java/com/monkeyk/sos/web/authentication/OAuth2DeviceCodeAuthenticationRestConverter.java new file mode 100644 index 0000000..9f56eed --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/authentication/OAuth2DeviceCodeAuthenticationRestConverter.java @@ -0,0 +1,56 @@ +package com.monkeyk.sos.web.authentication; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.OAuth2ErrorCodes; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationToken; +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * 2023/10/31 10:33 + * + * @author Shengzhao Li + * @see org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceCodeAuthenticationConverter + * @since 3.0.0 + */ +public final class OAuth2DeviceCodeAuthenticationRestConverter extends AbstractAuthenticationRestConverter { + + + @Override + public Authentication convert(Map parameters) { + // grant_type (REQUIRED) + String grantType = parameters.get(OAuth2ParameterNames.GRANT_TYPE); + if (!AuthorizationGrantType.DEVICE_CODE.getValue().equals(grantType)) { + return null; + } + + Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); + +// MultiValueMap parameters = OAuth2EndpointUtils.getParameters(request); + + // device_code (REQUIRED) + String deviceCode = parameters.get(OAuth2ParameterNames.DEVICE_CODE); + if (!StringUtils.hasText(deviceCode)) { + throwError( + OAuth2ErrorCodes.INVALID_REQUEST, + OAuth2ParameterNames.DEVICE_CODE, + ACCESS_TOKEN_REQUEST_ERROR_URI); + } + + Map additionalParameters = new HashMap<>(); + parameters.forEach((key, value) -> { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && + !key.equals(OAuth2ParameterNames.CLIENT_ID) && + !key.equals(OAuth2ParameterNames.DEVICE_CODE)) { + additionalParameters.put(key, value); + } + }); + + return new OAuth2DeviceCodeAuthenticationToken(deviceCode, clientPrincipal, additionalParameters); + } +} diff --git a/src/main/java/com/monkeyk/sos/web/authentication/OAuth2RefreshTokenAuthenticationRestConverter.java b/src/main/java/com/monkeyk/sos/web/authentication/OAuth2RefreshTokenAuthenticationRestConverter.java new file mode 100644 index 0000000..b775755 --- /dev/null +++ b/src/main/java/com/monkeyk/sos/web/authentication/OAuth2RefreshTokenAuthenticationRestConverter.java @@ -0,0 +1,70 @@ +package com.monkeyk.sos.web.authentication; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.OAuth2ErrorCodes; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken; +import org.springframework.util.StringUtils; + +import java.util.*; + +/** + * 2023/10/31 10:33 + * + * @author Shengzhao Li + * @see org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter + * @since 3.0.0 + */ +public final class OAuth2RefreshTokenAuthenticationRestConverter extends AbstractAuthenticationRestConverter { + + + @Override + public Authentication convert(Map parameters) { + // grant_type (REQUIRED) + String grantType = parameters.get(OAuth2ParameterNames.GRANT_TYPE); + if (!AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(grantType)) { + return null; + } + + Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); + +// MultiValueMap parameters = OAuth2EndpointUtils.getParameters(request); + + // refresh_token (REQUIRED) + String refreshToken = parameters.get(OAuth2ParameterNames.REFRESH_TOKEN); + if (!StringUtils.hasText(refreshToken)) { + throwError( + OAuth2ErrorCodes.INVALID_REQUEST, + OAuth2ParameterNames.REFRESH_TOKEN, + ACCESS_TOKEN_REQUEST_ERROR_URI); + } + + // scope (OPTIONAL) + String scope = parameters.get(OAuth2ParameterNames.SCOPE); +// if (!StringUtils.hasText(scope)) { +// throwError( +// OAuth2ErrorCodes.INVALID_REQUEST, +// OAuth2ParameterNames.SCOPE, +// ACCESS_TOKEN_REQUEST_ERROR_URI); +// } + Set requestedScopes = null; + if (StringUtils.hasText(scope)) { + requestedScopes = new HashSet<>( + Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); + } + + Map additionalParameters = new HashMap<>(); + parameters.forEach((key, value) -> { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && + !key.equals(OAuth2ParameterNames.REFRESH_TOKEN) && + !key.equals(OAuth2ParameterNames.SCOPE)) { + additionalParameters.put(key, value); + } + }); + + return new OAuth2RefreshTokenAuthenticationToken( + refreshToken, clientPrincipal, requestedScopes, additionalParameters); + } +} diff --git a/src/main/java/com/monkeyk/sos/web/controller/OAuthRestController.java b/src/main/java/com/monkeyk/sos/web/controller/OAuthRestController.java index f448384..9558d1a 100644 --- a/src/main/java/com/monkeyk/sos/web/controller/OAuthRestController.java +++ b/src/main/java/com/monkeyk/sos/web/controller/OAuthRestController.java @@ -11,23 +11,38 @@ */ package com.monkeyk.sos.web.controller; +import com.monkeyk.sos.web.WebUtils; +import com.monkeyk.sos.web.authentication.*; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServletServerHttpResponse; +import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.*; +import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; +import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; +import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.*; +import java.io.IOException; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.Map; /** @@ -45,165 +60,115 @@ public class OAuthRestController implements InitializingBean, ApplicationContext private static final Logger LOG = LoggerFactory.getLogger(OAuthRestController.class); -// @Autowired -// private ClientDetailsService clientDetailsService; -// -// // consumerTokenServices,defaultAuthorizationServerTokenServices -// @Autowired -// @Qualifier("defaultAuthorizationServerTokenServices") -// private AuthorizationServerTokenServices tokenServices; -// @Autowired -// private AuthorizationCodeServices authorizationCodeServices; - - @Autowired - private PasswordEncoder passwordEncoder; - -// private AuthenticationManager authenticationManager; - -// private OAuth2RequestFactory oAuth2RequestFactory; -// -// private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator(); -// private WebResponseExceptionTranslator providerExceptionHandler = new DefaultWebResponseExceptionTranslator(); + private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; - @RequestMapping(value = "/oauth2/rest_token", method = RequestMethod.POST) + private final AuthenticationRestConverter authenticationConverter; + + private final HttpMessageConverter accessTokenHttpResponseConverter = + new OAuth2AccessTokenResponseHttpMessageConverter(); + private final HttpMessageConverter errorHttpResponseConverter = + new OAuth2ErrorHttpMessageConverter(); + + + private AuthenticationManager authenticationManager; + + public OAuthRestController() { + + this.authenticationConverter = new DelegatingAuthenticationRestConverter( + Arrays.asList( + new OAuth2AuthorizationCodeAuthenticationRestConverter(), + new OAuth2RefreshTokenAuthenticationRestConverter(), + new OAuth2ClientCredentialsAuthenticationRestConverter(), + new OAuth2DeviceCodeAuthenticationRestConverter())); + } + + /** + * Replace OAuth2TokenEndpointFilter flow use restful API + * + * @param parameters request params + */ + @PostMapping("/oauth2/rest_token") @ResponseBody - public OAuth2AccessToken postAccessToken(@RequestBody Map parameters) { + public void postAccessToken(@RequestBody Map parameters, HttpServletResponse response) throws IOException { + try { + String grantType = parameters.get(OAuth2ParameterNames.GRANT_TYPE); + if (grantType == null) { + throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.GRANT_TYPE); + } -// String clientId = getClientId(parameters); -// ClientDetails authenticatedClient = clientDetailsService.loadClientByClientId(clientId); -// -// //validate client_secret -// String clientSecret = getClientSecret(parameters); -// if (clientSecret == null || clientSecret.equals("")) { -// throw new InvalidClientException("Bad client credentials"); -// } else { -// if (!this.passwordEncoder.matches(clientSecret, authenticatedClient.getClientSecret())) { -// throw new InvalidClientException("Bad client credentials"); -// } -// } -// -// TokenRequest tokenRequest = oAuth2RequestFactory.createTokenRequest(parameters, authenticatedClient); + Authentication authorizationGrantAuthentication = this.authenticationConverter.convert(parameters); + if (authorizationGrantAuthentication == null) { + throwError(OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE, OAuth2ParameterNames.GRANT_TYPE); + } + if (authorizationGrantAuthentication instanceof AbstractAuthenticationToken) { + ((AbstractAuthenticationToken) authorizationGrantAuthentication) +// .setDetails(this.authenticationDetailsSource.buildDetails(request)); + .setDetails(new WebAuthenticationDetails(WebUtils.getIp(), null)); + } -// if (clientId != null && !clientId.equals("")) { -// // Only validate the client details if a client authenticated during this -// // request. -// if (!clientId.equals(tokenRequest.getClientId())) { -// // double check to make sure that the client ID in the token request is the same as that in the -// // authenticated client -// throw new InvalidClientException("Given client ID does not match authenticated client"); -// } -// } -// -// oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient); - -// final String grantType = tokenRequest.getGrantType(); -// if (!StringUtils.hasText(grantType)) { -// throw new InvalidRequestException("Missing grant type"); -// } -// if (grantType.equals("implicit")) { -// throw new InvalidGrantException("Implicit grant type not supported from token endpoint"); -// } -// -// if (isAuthCodeRequest(parameters)) { -// // The scope was requested or determined during the authorization step -// if (!tokenRequest.getScope().isEmpty()) { -// LOG.debug("Clearing scope of incoming token request"); -// tokenRequest.setScope(Collections.emptySet()); -// } -// } - - -// if (isRefreshTokenRequest(parameters)) { -// // A refresh token has its own default scopes, so we should ignore any added by the factory here. -// tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE))); -// } -// -// OAuth2AccessToken token = getTokenGranter(grantType).grant(grantType, tokenRequest); -// if (token == null) { -// throw new UnsupportedGrantTypeException("Unsupported grant type: " + grantType); -// } - - -// return token; - throw new UnsupportedOperationException("Not yet implements"); - } - -// protected TokenGranter getTokenGranter(String grantType) { -// -// if ("authorization_code".equals(grantType)) { -// return new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, this.oAuth2RequestFactory); -// } else if ("password".equals(grantType)) { -// return new ResourceOwnerPasswordTokenGranter(getAuthenticationManager(), tokenServices, clientDetailsService, this.oAuth2RequestFactory); -// } else if ("refresh_token".equals(grantType)) { -// return new RefreshTokenGranter(tokenServices, clientDetailsService, this.oAuth2RequestFactory); -// } else if ("client_credentials".equals(grantType)) { -// return new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, this.oAuth2RequestFactory); -// } else if ("implicit".equals(grantType)) { -// return new ImplicitTokenGranter(tokenServices, clientDetailsService, this.oAuth2RequestFactory); -// } else { -// throw new UnsupportedGrantTypeException("Unsupport grant_type: " + grantType); -// } -// } - - -// @ExceptionHandler(Exception.class) -// public ResponseEntity handleException(Exception e) throws Exception { -// LOG.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); -// return getExceptionTranslator().translate(e); -// } - -// @ExceptionHandler(ClientRegistrationException.class) -// public ResponseEntity handleClientRegistrationException(Exception e) throws Exception { -// LOG.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); -// return getExceptionTranslator().translate(new BadClientCredentialsException()); -// } -// -// @ExceptionHandler(OAuth2Exception.class) -// public ResponseEntity handleException(OAuth2Exception e) throws Exception { -// LOG.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); -// return getExceptionTranslator().translate(e); -// } - - -// private boolean isRefreshTokenRequest(Map parameters) { -// return "refresh_token".equals(parameters.get(OAuth2Utils.GRANT_TYPE)) && parameters.get("refresh_token") != null; -// } -// -// private boolean isAuthCodeRequest(Map parameters) { -// return "authorization_code".equals(parameters.get(OAuth2Utils.GRANT_TYPE)) && parameters.get("code") != null; -// } - - -// protected String getClientId(Map parameters) { -// return parameters.get(OAuth2Utils.CLIENT_ID); -// } - - protected String getClientSecret(Map parameters) { - return parameters.get("client_secret"); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = + (OAuth2AccessTokenAuthenticationToken) this.authenticationManager.authenticate(authorizationGrantAuthentication); + this.sendAccessTokenResponse(response, accessTokenAuthentication); + } catch (OAuth2AuthenticationException ex) { + SecurityContextHolder.clearContext(); + if (LOG.isTraceEnabled()) { + LOG.trace("Token request failed: {}", ex.getError(), ex); + } + this.sendErrorResponse(response, ex); + } } -// private AuthenticationManager getAuthenticationManager() { -// return this.authenticationManager; -// } + private void sendErrorResponse(HttpServletResponse response, + AuthenticationException exception) throws IOException { + + OAuth2Error error = ((OAuth2AuthenticationException) exception).getError(); + ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); + httpResponse.setStatusCode(HttpStatus.BAD_REQUEST); + this.errorHttpResponseConverter.write(error, null, httpResponse); + } + + private void sendAccessTokenResponse(HttpServletResponse response, Authentication authentication) throws IOException { + + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = + (OAuth2AccessTokenAuthenticationToken) authentication; + + OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken(); + OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken(); + Map additionalParameters = accessTokenAuthentication.getAdditionalParameters(); + + OAuth2AccessTokenResponse.Builder builder = + OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()) + .tokenType(accessToken.getTokenType()) + .scopes(accessToken.getScopes()); + if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) { + builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt())); + } + if (refreshToken != null) { + builder.refreshToken(refreshToken.getTokenValue()); + } + if (!CollectionUtils.isEmpty(additionalParameters)) { + builder.additionalParameters(additionalParameters); + } + OAuth2AccessTokenResponse accessTokenResponse = builder.build(); + ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); + this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse); + } + + + private static void throwError(String errorCode, String parameterName) { + OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, DEFAULT_ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + @Override public void afterPropertiesSet() throws Exception { - -// Assert.state(clientDetailsService != null, "ClientDetailsService must be provided"); // Assert.state(authenticationManager != null, "AuthenticationManager must be provided"); - - Assert.notNull(this.passwordEncoder, "PasswordEncoder is null"); - -// oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService); } -// protected WebResponseExceptionTranslator getExceptionTranslator() { -// return providerExceptionHandler; -// } - @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { @@ -211,4 +176,5 @@ public class OAuthRestController implements InitializingBean, ApplicationContext // this.authenticationManager = (AuthenticationManager) applicationContext.getBean("authenticationManagerBean"); // } } + }