mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 04:39:00 +08:00
fix(auth): 限制表单登录仅使用密码认证
(cherry picked from commit d3c470a848b5369459cd2acc9662276ed52b0e15)
This commit is contained in:
1 parent
3fd14644cd
commit
3b15ea4235
2 files changed
+86
-1
No files matched your search
+11
-1
@@ -1,8 +1,9 @@
|
||||
package com.pig4cloud.pigx.auth.support.filter;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.pig4cloud.pigx.auth.support.core.AuthCaptchaSupport;
|
||||
import com.pig4cloud.pigx.auth.support.handler.FormAuthenticationFailureHandler;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.exception.ValidateCodeException;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
@@ -11,6 +12,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
@@ -45,6 +47,14 @@ public class FormLoginValidateCodeFilter extends OncePerRequestFilter {
|
||||
return;
|
||||
}
|
||||
|
||||
// 表单登录只允许密码认证,防止切换到 APP 模式的免密码校验分支。
|
||||
String[] grantTypes = request.getParameterValues(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (ArrayUtil.length(grantTypes) != 1 || !ArrayUtil.contains(grantTypes, SecurityConstants.PASSWORD)) {
|
||||
failureHandler.onAuthenticationFailure(request, response,
|
||||
new AuthenticationServiceException("Form login only supports password grant"));
|
||||
return;
|
||||
}
|
||||
|
||||
String authClientId = authCaptchaSupport.resolveAuthorizationClientId(request, response, true);
|
||||
String tenantId = authCaptchaSupport.resolveAuthorizationTenantId(request, response, true);
|
||||
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.pig4cloud.pigx.auth.support.filter;
|
||||
|
||||
import com.pig4cloud.pigx.auth.support.core.AuthCaptchaSupport;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class FormLoginValidateCodeFilterTests {
|
||||
|
||||
private final AuthCaptchaSupport captchaSupport = mock(AuthCaptchaSupport.class);
|
||||
|
||||
private final FormLoginValidateCodeFilter filter = new FormLoginValidateCodeFilter(captchaSupport);
|
||||
|
||||
private final FilterChain chain = mock(FilterChain.class);
|
||||
|
||||
private final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/oauth2/form");
|
||||
|
||||
private final MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
static Stream<Arguments> invalidGrants() {
|
||||
return Stream.of(null, new String[] { "" }, new String[] { "app" }, new String[] { "mobile" },
|
||||
new String[] { "client_credentials" }, new String[] { "authorization_code" },
|
||||
new String[] { "refresh_token" }, new String[] { "password", "app" },
|
||||
new String[] { "app", "password" }, new String[] { "password", "password" }).map(values -> Arguments.of((Object) values));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("invalidGrants")
|
||||
void rejectsInvalidGrantsBeforeCaptchaOrAuthentication(String[] grantTypes) throws Exception {
|
||||
if (grantTypes != null) {
|
||||
request.setParameter("grant_type", grantTypes);
|
||||
}
|
||||
applyFilter();
|
||||
assertThat(response.getStatus()).isEqualTo(302);
|
||||
assertThat(response.getRedirectedUrl()).contains("/token/login?error=");
|
||||
verifyNoInteractions(captchaSupport, chain);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = { false, true })
|
||||
void allowsPasswordWithExistingTenantCaptchaPolicy(boolean captchaEnabled) throws Exception {
|
||||
request.setParameter("grant_type", "password");
|
||||
when(captchaSupport.resolveAuthorizationClientId(request, response, true)).thenReturn("client");
|
||||
when(captchaSupport.resolveAuthorizationTenantId(request, response, true)).thenReturn("2");
|
||||
when(captchaSupport.isCaptchaEnabled("2", "client")).thenReturn(captchaEnabled);
|
||||
applyFilter();
|
||||
verify(captchaSupport).isCaptchaEnabled("2", "client");
|
||||
verify(captchaSupport, times(captchaEnabled ? 1 : 0)).validateCode(request);
|
||||
verify(chain).doFilter(request, response);
|
||||
}
|
||||
|
||||
private void applyFilter() throws Exception {
|
||||
request.setServletPath("/oauth2/form");
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));
|
||||
filter.doFilter(request, response, chain);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user