chore: cleanup deprecated class (#1859)

pull/1863/head
guqing 2022-04-20 11:14:10 +08:00 committed by GitHub
parent f1ccccb557
commit 8ac819ad14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 11 additions and 454 deletions

View File

@ -7,7 +7,7 @@ import org.springframework.util.Assert;
* A facility for holding information associated to a specific context.
*
* @author guqing
* @date 2022-04-14
* @since 2.0.0
*/
public interface Context {
/**

View File

@ -8,7 +8,7 @@ import org.springframework.util.Assert;
* Default implementation of {@link OAuth2TokenContext}.
*
* @author guqing
* @date 2022-04-14
* @since 2.0.0
*/
public record DefaultOAuth2TokenContext(Map<Object, Object> context) implements OAuth2TokenContext {
public DefaultOAuth2TokenContext {

View File

@ -1,153 +0,0 @@
package run.halo.app.identity.authentication;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
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.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* Processes an authentication client request. Called
* {@code AuthenticationProcessingFilter} prior to Spring Security 3.0.
* <p>
* Request parameter must present two parameters to this filter: a username and password. The
* default parameter names to use are contained in the static fields
* {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and
* {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}. The parameter names can also be changed by
* setting the {@code usernameParameter} and {@code passwordParameter} properties.
* <p>
* This filter by default responds to the URL {@code /api/v1/oauth2/login}.
*
* @author guqing
* @see UsernamePasswordAuthenticationFilter
* @see AbstractAuthenticationProcessingFilter
* @see OAuth2AccessTokenAuthenticationToken
* @since 2.0.0
*/
public class JwtUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
/**
* The default endpoint {@code URI} for access token requests.
*/
private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/api/v1/oauth2/token";
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter =
new OAuth2ErrorHttpMessageConverter();
private boolean postOnly = true;
public JwtUsernamePasswordAuthenticationFilter() {
this(DEFAULT_TOKEN_ENDPOINT_URI, null);
}
public JwtUsernamePasswordAuthenticationFilter(String defaultFilterProcessesUrl) {
this(defaultFilterProcessesUrl, null);
}
public JwtUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
this(DEFAULT_TOKEN_ENDPOINT_URI, authenticationManager);
}
public JwtUsernamePasswordAuthenticationFilter(String defaultFilterProcessesUrl,
AuthenticationManager authenticationManager) {
super(authenticationManager);
if (!StringUtils.hasText(defaultFilterProcessesUrl)) {
throw new IllegalArgumentException("tokenEndpointUri cannot be empty.");
}
setFilterProcessesUrl(defaultFilterProcessesUrl);
setAuthenticationSuccessHandler(this::sendAccessTokenResponse);
setAuthenticationFailureHandler(this::sendErrorResponse);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response)
throws AuthenticationException {
if (this.postOnly && !HttpMethod.POST.name().equals(request.getMethod())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
if (!AuthorizationGrantType.PASSWORD.getValue().equals(grantType)) {
return null;
}
String username = obtainUsername(request);
username = (username != null) ? username : "";
username = username.trim();
String password = obtainPassword(request);
password = (password != null) ? password : "";
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) authentication;
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
Map<String, Object> 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 void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
//OAuth2Error error = ((AuthenticationServiceException) exception).getMessage();
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
httpResponse.setStatusCode(HttpStatus.BAD_REQUEST);
this.errorHttpResponseConverter.write(new OAuth2Error(exception.getMessage()), null,
httpResponse);
}
@Override
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
super.setPostOnly(postOnly);
}
}

View File

@ -28,8 +28,6 @@ import org.springframework.util.StringUtils;
* @since 2.0.0
*/
public class OAuth2RefreshTokenAuthenticationConverter implements AuthenticationConverter {
static final String ACCESS_TOKEN_REQUEST_ERROR_URI =
"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
@Nullable
@Override
@ -48,7 +46,7 @@ public class OAuth2RefreshTokenAuthenticationConverter implements Authentication
|| parameters.get(OAuth2ParameterNames.REFRESH_TOKEN).size() != 1) {
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.REFRESH_TOKEN,
ACCESS_TOKEN_REQUEST_ERROR_URI);
OAuth2EndpointUtils.ERROR_URI);
}
// scope (OPTIONAL)
@ -58,7 +56,7 @@ public class OAuth2RefreshTokenAuthenticationConverter implements Authentication
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.SCOPE,
ACCESS_TOKEN_REQUEST_ERROR_URI);
OAuth2EndpointUtils.ERROR_URI);
}
Set<String> requestedScopes = null;
if (StringUtils.hasText(scope)) {

View File

@ -47,8 +47,6 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
*/
private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/api/v1/oauth2/token";
private static final String DEFAULT_ERROR_URI =
"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
private final AuthenticationManager authenticationManager;
private final RequestMatcher tokenEndpointMatcher;
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
@ -103,14 +101,15 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
try {
String[] grantTypes = request.getParameterValues(OAuth2ParameterNames.GRANT_TYPE);
if (grantTypes == null || grantTypes.length != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.GRANT_TYPE);
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.GRANT_TYPE, OAuth2EndpointUtils.ERROR_URI);
}
Authentication authorizationGrantAuthentication =
this.authenticationConverter.convert(request);
if (authorizationGrantAuthentication == null) {
throwError(OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE,
OAuth2ParameterNames.GRANT_TYPE);
OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE,
OAuth2ParameterNames.GRANT_TYPE, OAuth2EndpointUtils.ERROR_URI);
}
if (authorizationGrantAuthentication instanceof AbstractAuthenticationToken) {
((AbstractAuthenticationToken) authorizationGrantAuthentication)
@ -221,10 +220,4 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
httpResponse.setStatusCode(HttpStatus.BAD_REQUEST);
this.errorHttpResponseConverter.write(error, 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);
}
}

View File

@ -10,7 +10,7 @@ import org.springframework.security.oauth2.core.OAuth2Token;
*
* @param <T> the type of the OAuth 2.0 Token
* @author guqing
* @date 2022-04-14
* @since 2.0.0
* @see OAuth2Token
* @see OAuth2TokenContext
* @see ClaimAccessor

View File

@ -5,7 +5,7 @@ import org.springframework.util.Assert;
/**
* @author guqing
* @date 2022-04-14
* @since 2.0.0
*/
public record OAuth2TokenType(String value) implements Serializable {
public static final OAuth2TokenType ACCESS_TOKEN = new OAuth2TokenType("access_token");

View File

@ -8,7 +8,7 @@ import org.springframework.util.Assert;
* A context that holds information of the Provider.
*
* @author guqing
* @date 2022-04-14
* @since 2.0.0
*/
public record ProviderContext(ProviderSettings providerSettings,
@Nullable Supplier<String> issuerSupplier) {

View File

@ -1,28 +0,0 @@
package run.halo.app.identity.authentication;
/**
* @author guqing
* @date 2022-04-13
*/
public interface SecurityConstant {
/**
* 30 mins
*/
long EXPIRATION_TIME = 1_800_000;
/**
* Token prefix
*/
String TOKEN_PREFIX = "Bearer";
/**
* Authentication header
*/
String HEADER_STRING = "Authorization";
/**
* login uri
*/
String LOGIN_URL = "/api/v1/oauth/login";
}

View File

@ -1,253 +0,0 @@
package run.halo.app.authentication;
import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
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.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import run.halo.app.identity.authentication.JwtUsernamePasswordAuthenticationFilter;
import run.halo.app.identity.authentication.OAuth2AccessTokenAuthenticationToken;
/**
* Tests for {@link JwtUsernamePasswordAuthenticationFilter}.
*
* @author guqing
* @since 2.0.0
*/
@Disabled
public class JwtUsernamePasswordAuthenticationFilterTests {
private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/api/v1/oauth2/login";
private static final String REMOTE_ADDRESS = "remote-address";
private AuthenticationManager authenticationManager;
private JwtUsernamePasswordAuthenticationFilter filter;
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
@BeforeEach
public void setUp() {
this.authenticationManager = mock(AuthenticationManager.class);
this.filter = new JwtUsernamePasswordAuthenticationFilter(this.authenticationManager);
}
@AfterEach
public void cleanup() {
SecurityContextHolder.clearContext();
}
@Test
public void constructorWhenTokenEndpointUriNullThenThrowIllegalArgumentException() {
assertThatThrownBy(
() -> new JwtUsernamePasswordAuthenticationFilter(null, this.authenticationManager))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("tokenEndpointUri cannot be empty.");
}
@Test
public void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.filter.setAuthenticationDetailsSource(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("AuthenticationDetailsSource required");
}
@Test
public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("successHandler cannot be null");
}
@Test
public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("failureHandler cannot be null");
}
@Test
public void doFilterWhenNotTokenRequestThenNotProcessed() throws Exception {
String requestUri = "/path";
MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri);
request.setServletPath(requestUri);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain);
verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
}
@Test
public void doFilterWhenTokenRequestGetThenNotProcessed() throws Exception {
String requestUri = DEFAULT_TOKEN_ENDPOINT_URI;
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
request.setServletPath(requestUri);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
AuthenticationFailureHandler authenticationFailureHandler =
mock(AuthenticationFailureHandler.class);
filter.setAuthenticationFailureHandler(authenticationFailureHandler);
this.filter.doFilter(request, response, filterChain);
verify(authenticationFailureHandler).onAuthenticationFailure(any(HttpServletRequest.class),
any(HttpServletResponse.class), any(AuthenticationException.class));
verifyNoInteractions(filterChain);
}
@Test
public void doFilterWhenAuthorizationCodeTokenRequestThenAccessTokenResponse()
throws Exception {
OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, "token",
Instant.now(), Instant.now().plus(Duration.ofHours(1)),
new HashSet<>(Arrays.asList("scope1", "scope2")));
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(
"refresh-token", Instant.now(), Instant.now().plus(Duration.ofDays(1)));
Authentication clientPrincipal =
new UsernamePasswordAuthenticationToken("guqing", "123456");
Map<String, Object>
additionalParameters = Collections.singletonMap("custom-param", "custom-value");
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
new OAuth2AccessTokenAuthenticationToken(clientPrincipal, accessToken, refreshToken,
additionalParameters);
when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication);
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(clientPrincipal);
SecurityContextHolder.setContext(securityContext);
MockHttpServletRequest request = createClientTokenRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain);
verifyNoInteractions(filterChain);
ArgumentCaptor<UsernamePasswordAuthenticationToken>
authorizationCodeAuthenticationCaptor =
ArgumentCaptor.forClass(UsernamePasswordAuthenticationToken.class);
verify(this.authenticationManager).authenticate(
authorizationCodeAuthenticationCaptor.capture());
UsernamePasswordAuthenticationToken accessTokenAuthenticationToken =
authorizationCodeAuthenticationCaptor.getValue();
assertThat(accessTokenAuthenticationToken.getName()).isEqualTo("guqing");
assertThat(accessTokenAuthenticationToken.getDetails())
.asInstanceOf(type(WebAuthenticationDetails.class))
.extracting(WebAuthenticationDetails::getRemoteAddress)
.isEqualTo(REMOTE_ADDRESS);
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);
OAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken();
assertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType());
assertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue());
assertThat(accessTokenResult.getIssuedAt()).isBetween(
accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));
assertThat(accessTokenResult.getExpiresAt()).isBetween(
accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));
assertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes());
assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(
refreshToken.getTokenValue());
assertThat(accessTokenResponse.getAdditionalParameters()).containsExactly(
entry("custom-param", "custom-value"));
}
@Test
public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {
AuthenticationSuccessHandler authenticationSuccessHandler =
mock(AuthenticationSuccessHandler.class);
this.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
Authentication clientPrincipal =
new UsernamePasswordAuthenticationToken("guqing", "123456");
OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, "token",
Instant.now(), Instant.now().plus(Duration.ofHours(1)),
new HashSet<>(Arrays.asList("scope1", "scope2")));
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
new OAuth2AccessTokenAuthenticationToken(clientPrincipal, accessToken);
when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication);
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(clientPrincipal);
SecurityContextHolder.setContext(securityContext);
MockHttpServletRequest request = createClientTokenRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain);
verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());
}
private OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response)
throws Exception {
MockClientHttpResponse httpResponse = new MockClientHttpResponse(
response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus()));
return this.accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class,
httpResponse);
}
private static MockHttpServletRequest createClientTokenRequest() {
String requestUri = DEFAULT_TOKEN_ENDPOINT_URI;
MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri);
request.setServletPath(requestUri);
request.setRemoteAddr(REMOTE_ADDRESS);
request.addParameter("username", "guqing");
request.addParameter("password", "123456");
request.addParameter(OAuth2ParameterNames.GRANT_TYPE,
AuthorizationGrantType.PASSWORD.getValue());
request.addParameter("custom-param-1", "custom-value-1");
return request;
}
}