|
|
|
@ -26,22 +26,32 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
|
|
|
|
|
import org.springframework.security.authentication.AuthenticationManager; |
|
|
|
|
import org.springframework.security.core.Authentication; |
|
|
|
|
import org.springframework.security.core.AuthenticationException; |
|
|
|
|
import org.springframework.security.core.context.SecurityContext; |
|
|
|
|
import org.springframework.security.core.context.SecurityContextHolder; |
|
|
|
|
import org.springframework.security.crypto.password.PasswordEncoder; |
|
|
|
|
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.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; |
|
|
|
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; |
|
|
|
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; |
|
|
|
|
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext; |
|
|
|
|
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder; |
|
|
|
|
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; |
|
|
|
|
import org.springframework.security.web.authentication.WebAuthenticationDetails; |
|
|
|
|
import org.springframework.stereotype.Controller; |
|
|
|
|
import org.springframework.util.Assert; |
|
|
|
|
import org.springframework.util.CollectionUtils; |
|
|
|
|
import org.springframework.web.bind.annotation.ExceptionHandler; |
|
|
|
|
import org.springframework.web.bind.annotation.PostMapping; |
|
|
|
|
import org.springframework.web.bind.annotation.RequestBody; |
|
|
|
|
import org.springframework.web.bind.annotation.ResponseBody; |
|
|
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.time.Instant; |
|
|
|
|
import java.time.temporal.ChronoUnit; |
|
|
|
|
import java.util.Arrays; |
|
|
|
|
import java.util.Map; |
|
|
|
@ -63,6 +73,8 @@ public class OAuthRestController {
|
|
|
|
|
|
|
|
|
|
private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; |
|
|
|
|
|
|
|
|
|
private static final String CLIENT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final AuthenticationRestConverter authenticationConverter; |
|
|
|
|
|
|
|
|
@ -74,9 +86,19 @@ public class OAuthRestController {
|
|
|
|
|
|
|
|
|
|
private AuthenticationManager authenticationManager; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
private ApplicationContext applicationContext; |
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
private RegisteredClientRepository registeredClientRepository; |
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
private PasswordEncoder passwordEncoder; |
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
private AuthorizationServerSettings authorizationServerSettings; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public OAuthRestController() { |
|
|
|
|
|
|
|
|
@ -92,41 +114,107 @@ public class OAuthRestController {
|
|
|
|
|
* Replace OAuth2TokenEndpointFilter flow use restful API |
|
|
|
|
* |
|
|
|
|
* @param parameters request params |
|
|
|
|
* @see org.springframework.security.oauth2.server.authorization.authentication.ClientSecretAuthenticationProvider |
|
|
|
|
*/ |
|
|
|
|
@PostMapping("/oauth2/rest_token") |
|
|
|
|
@ResponseBody |
|
|
|
|
public void postAccessToken(@RequestBody Map<String, String> parameters, HttpServletResponse response) throws IOException { |
|
|
|
|
public void postAccessToken(@RequestBody Map<String, String> parameters, HttpServletResponse response) |
|
|
|
|
throws OAuth2AuthenticationException, IOException { |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
String grantType = parameters.get(OAuth2ParameterNames.GRANT_TYPE); |
|
|
|
|
if (grantType == null) { |
|
|
|
|
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.GRANT_TYPE); |
|
|
|
|
} |
|
|
|
|
//init OAuth2 contexts
|
|
|
|
|
initialOAuth2Contexts(parameters); |
|
|
|
|
|
|
|
|
|
Authentication authorizationGrantAuthentication = this.authenticationConverter.convert(parameters); |
|
|
|
|
if (authorizationGrantAuthentication == null) { |
|
|
|
|
throwError(OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE, OAuth2ParameterNames.GRANT_TYPE); |
|
|
|
|
} |
|
|
|
|
if (authorizationGrantAuthentication instanceof AbstractAuthenticationToken) { |
|
|
|
|
((AbstractAuthenticationToken) authorizationGrantAuthentication) |
|
|
|
|
// oauth2 flow start...
|
|
|
|
|
String grantType = parameters.get(OAuth2ParameterNames.GRANT_TYPE); |
|
|
|
|
if (grantType == null) { |
|
|
|
|
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.GRANT_TYPE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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)); |
|
|
|
|
} |
|
|
|
|
.setDetails(new WebAuthenticationDetails(WebUtils.getIp(), null)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
checkAndInitialAuthenticationManager(); |
|
|
|
|
|
|
|
|
|
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = |
|
|
|
|
(OAuth2AccessTokenAuthenticationToken) this.authenticationManager.authenticate(authorizationGrantAuthentication); |
|
|
|
|
this.sendAccessTokenResponse(response, accessTokenAuthentication); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void initialOAuth2Contexts(Map<String, String> parameters) { |
|
|
|
|
String clientId = parameters.get(OAuth2ParameterNames.CLIENT_ID); |
|
|
|
|
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId); |
|
|
|
|
if (registeredClient == null) { |
|
|
|
|
throwInvalidClient(OAuth2ParameterNames.CLIENT_ID); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (LOG.isTraceEnabled()) { |
|
|
|
|
LOG.trace("Retrieved registered client"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!registeredClient.getClientAuthenticationMethods().contains( |
|
|
|
|
ClientAuthenticationMethod.CLIENT_SECRET_POST)) { |
|
|
|
|
throwInvalidClient("authentication_method"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
String clientSecret = parameters.get(OAuth2ParameterNames.CLIENT_SECRET); |
|
|
|
|
if (clientSecret == null) { |
|
|
|
|
throwInvalidClient("credentials"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// String clientSecret = clientAuthentication.getCredentials().toString();
|
|
|
|
|
if (!this.passwordEncoder.matches(clientSecret, registeredClient.getClientSecret())) { |
|
|
|
|
throwInvalidClient(OAuth2ParameterNames.CLIENT_SECRET); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (registeredClient.getClientSecretExpiresAt() != null && |
|
|
|
|
Instant.now().isAfter(registeredClient.getClientSecretExpiresAt())) { |
|
|
|
|
throwInvalidClient("client_secret_expires_at"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
checkAndInitialAuthenticationManager(); |
|
|
|
|
if (LOG.isTraceEnabled()) { |
|
|
|
|
LOG.trace("Authenticated client secret"); |
|
|
|
|
} |
|
|
|
|
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(registeredClient, |
|
|
|
|
ClientAuthenticationMethod.CLIENT_SECRET_POST, clientSecret); |
|
|
|
|
SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); |
|
|
|
|
securityContext.setAuthentication(authentication); |
|
|
|
|
SecurityContextHolder.setContext(securityContext); |
|
|
|
|
|
|
|
|
|
// init AuthorizationServerContext
|
|
|
|
|
AuthorizationServerContext authorizationServerContext = new AuthorizationServerContext() { |
|
|
|
|
@Override |
|
|
|
|
public String getIssuer() { |
|
|
|
|
return authorizationServerSettings.getIssuer(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
@Override |
|
|
|
|
public AuthorizationServerSettings getAuthorizationServerSettings() { |
|
|
|
|
return authorizationServerSettings; |
|
|
|
|
} |
|
|
|
|
this.sendErrorResponse(response, ex); |
|
|
|
|
}; |
|
|
|
|
AuthorizationServerContextHolder.setContext(authorizationServerContext); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 异常处理 |
|
|
|
|
*/ |
|
|
|
|
@ExceptionHandler(OAuth2AuthenticationException.class) |
|
|
|
|
public void handleOAuth2AuthenticationException(OAuth2AuthenticationException ex, HttpServletResponse response) throws IOException { |
|
|
|
|
SecurityContextHolder.clearContext(); |
|
|
|
|
if (LOG.isTraceEnabled()) { |
|
|
|
|
LOG.trace("Token request failed: {}", ex.getError(), ex); |
|
|
|
|
} |
|
|
|
|
this.sendErrorResponse(response, ex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void checkAndInitialAuthenticationManager() { |
|
|
|
|
if (this.authenticationManager == null) { |
|
|
|
|
OAuth2ServerConfiguration serverConfiguration = applicationContext.getBean(OAuth2ServerConfiguration.class); |
|
|
|
@ -178,5 +266,13 @@ public class OAuthRestController {
|
|
|
|
|
throw new OAuth2AuthenticationException(error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void throwInvalidClient(String parameterName) { |
|
|
|
|
OAuth2Error error = new OAuth2Error( |
|
|
|
|
OAuth2ErrorCodes.INVALID_CLIENT, |
|
|
|
|
"Client authentication failed: " + parameterName, |
|
|
|
|
CLIENT_ERROR_URI |
|
|
|
|
); |
|
|
|
|
throw new OAuth2AuthenticationException(error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|