用 Spring Security 配置替代登录可用性 Filter
This commit is contained in:
+5
-1
@@ -29,6 +29,9 @@ try-with-resources 管理已认证用户仓储会话;基础设施的 `AdUserRe
|
|||||||
|
|
||||||
## 启用
|
## 启用
|
||||||
|
|
||||||
|
`iam.ad.enabled=true` 时才装配登录 Controller 与浏览器安全链(含 formLogin)。
|
||||||
|
未启用时入口由兜底安全链拒绝,匿名 GET 返回 401;不注册密码处理端点。
|
||||||
|
|
||||||
正常 JVM 构建:
|
正常 JVM 构建:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -77,7 +80,8 @@ LDAP endpoint identification 或用信任所有证书的 socket factory。连接
|
|||||||
AD 根范围查询可能返回 DomainDnsZones/ForestDnsZones 等分区 referral;配置为 ignore,
|
AD 根范围查询可能返回 DomainDnsZones/ForestDnsZones 等分区 referral;配置为 ignore,
|
||||||
由仓储的 LdapTemplate 忽略 partial result,不使用 throw 打断用户查询,也不 follow 转发用户凭据。
|
由仓储的 LdapTemplate 忽略 partial result,不使用 throw 打断用户查询,也不 follow 转发用户凭据。
|
||||||
|
|
||||||
浏览器入口只接受 HTTPS;明文请求返回 426。默认不信任转发头。若以后由代理终结 TLS,
|
浏览器安全链通过 Spring Security `redirectToHttps` 将明文请求重定向至 HTTPS,
|
||||||
|
重定向前不执行密码验证或退出。默认不信任转发头。若以后由代理终结 TLS,
|
||||||
必须配合仅受信代理可达的后端网络和转发头配置,不能公开一个信任任意 forwarded header
|
必须配合仅受信代理可达的后端网络和转发头配置,不能公开一个信任任意 forwarded header
|
||||||
的 HTTP 端口。当前开发验收由应用直接终结 TLS。
|
的 HTTP 端口。当前开发验收由应用直接终结 TLS。
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ WebAuthn 集成;TOTP、恢复方式与已有 Authelia MFA 的迁移方式需
|
|||||||
## Native 与监控
|
## Native 与监控
|
||||||
|
|
||||||
- CI 构建 Native 产物并对该产物执行集成测试;测试报告区分 JVM 与 Native。
|
- CI 构建 Native 产物并对该产物执行集成测试;测试报告区分 JVM 与 Native。
|
||||||
|
- 登录 Controller 与安全链使用 `@ConditionalOnProperty(iam.ad.enabled)`;AOT 在构建时
|
||||||
|
决定 bean 是否存在。AD Native 产物必须在 AOT 阶段启用此属性,验证实际入口与兜底链,
|
||||||
|
不能假设运行时修改属性会重新装配 bean。本轮只验证 JVM,尚未验收该 Native 路径。
|
||||||
- 最终运行镜像无需 JRE,不允许以回退 JVM 的方式令 Native 验收通过。
|
- 最终运行镜像无需 JRE,不允许以回退 JVM 的方式令 Native 验收通过。
|
||||||
- LDAP、MFA、数据库、TLS、JSON 和 Hydra HTTP 客户端全部在 Native 中执行。
|
- LDAP、MFA、数据库、TLS、JSON 和 Hydra HTTP 客户端全部在 Native 中执行。
|
||||||
- 纳入 Actuator、Micrometer Prometheus 与 OpenTelemetry/分布式追踪;实际发起请求后
|
- 纳入 Actuator、Micrometer Prometheus 与 OpenTelemetry/分布式追踪;实际发起请求后
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ def main():
|
|||||||
ready_seconds = time.monotonic() - started
|
ready_seconds = time.monotonic() - started
|
||||||
assert request('/actuator/prometheus')[0] == 401, 'Anonymous metrics must be rejected'
|
assert request('/actuator/prometheus')[0] == 401, 'Anonymous metrics must be rejected'
|
||||||
assert request('/')[0] == 401, 'Anonymous application access must be rejected'
|
assert request('/')[0] == 401, 'Anonymous application access must be rejected'
|
||||||
assert request('/signin')[0] == 404, 'Sign-in must be disabled without directory configuration'
|
assert request('/signin')[0] == 401, 'Sign-in must be disabled without directory configuration'
|
||||||
status, before = request('/actuator/prometheus', authenticated=True)
|
status, before = request('/actuator/prometheus', authenticated=True)
|
||||||
assert status == 200, 'Authenticated Prometheus scrape failed'
|
assert status == 200, 'Authenticated Prometheus scrape failed'
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
|
|||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
package top.ddupan.iam.login.authentication.interfaces.web;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
|
||||||
|
|
||||||
/** Rejects disabled or plaintext sign-in before any credentials reach authentication. */
|
|
||||||
public final class SignInAvailabilityFilter extends OncePerRequestFilter {
|
|
||||||
private final boolean enabled;
|
|
||||||
|
|
||||||
public SignInAvailabilityFilter(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
|
||||||
String path = request.getServletPath();
|
|
||||||
if (path.isEmpty()) path = request.getRequestURI().substring(request.getContextPath().length());
|
|
||||||
return !path.equals("/signin") && !path.startsWith("/signin/");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
|
||||||
FilterChain chain) throws ServletException, IOException {
|
|
||||||
if (!enabled || !request.isSecure()) {
|
|
||||||
response.setHeader("Cache-Control", "no-store");
|
|
||||||
response.setStatus(enabled ? 426 : 404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package top.ddupan.iam.login.authentication.interfaces.web;
|
package top.ddupan.iam.login.authentication.interfaces.web;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
@@ -13,6 +14,7 @@ import top.ddupan.iam.login.authentication.infrastructure.security.DirectoryPrin
|
|||||||
|
|
||||||
/** Renders Spring Security's login pages; form processing belongs to the security filters. */
|
/** Renders Spring Security's login pages; form processing belongs to the security filters. */
|
||||||
@RestController
|
@RestController
|
||||||
|
@ConditionalOnProperty(prefix = "iam.ad", name = "enabled", havingValue = "true")
|
||||||
public class SignInController {
|
public class SignInController {
|
||||||
private final PageRenderer renderer;
|
private final PageRenderer renderer;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package top.ddupan.iam.login.configuration;
|
package top.ddupan.iam.login.configuration;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
@@ -14,12 +15,9 @@ import org.springframework.security.web.SecurityFilterChain;
|
|||||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.csrf.CsrfFilter;
|
|
||||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||||
import top.ddupan.iam.login.authentication.application.VerifyPassword;
|
import top.ddupan.iam.login.authentication.application.VerifyPassword;
|
||||||
import top.ddupan.iam.login.authentication.infrastructure.ad.AdProperties;
|
|
||||||
import top.ddupan.iam.login.authentication.infrastructure.security.DirectoryAuthenticationProvider;
|
import top.ddupan.iam.login.authentication.infrastructure.security.DirectoryAuthenticationProvider;
|
||||||
import top.ddupan.iam.login.authentication.interfaces.web.SignInAvailabilityFilter;
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableMultiFactorAuthentication(authorities = {})
|
@EnableMultiFactorAuthentication(authorities = {})
|
||||||
@@ -42,13 +40,14 @@ class SecurityConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(2)
|
@Order(2)
|
||||||
SecurityFilterChain browser(HttpSecurity http, VerifyPassword passwords, AdProperties ad) throws Exception {
|
@ConditionalOnProperty(prefix = "iam.ad", name = "enabled", havingValue = "true")
|
||||||
|
SecurityFilterChain browser(HttpSecurity http, VerifyPassword passwords) throws Exception {
|
||||||
var passwordFactor = AuthorizationManagerFactories.<RequestAuthorizationContext>multiFactor()
|
var passwordFactor = AuthorizationManagerFactories.<RequestAuthorizationContext>multiFactor()
|
||||||
.requireFactor(factor -> factor.passwordAuthority().validDuration(Duration.ofMinutes(10)))
|
.requireFactor(factor -> factor.passwordAuthority().validDuration(Duration.ofMinutes(10)))
|
||||||
.build();
|
.build();
|
||||||
return http
|
return http.securityMatcher("/signin", "/signin/**", "/assets/**")
|
||||||
|
.redirectToHttps(Customizer.withDefaults())
|
||||||
.authenticationManager(new ProviderManager(new DirectoryAuthenticationProvider(passwords)))
|
.authenticationManager(new ProviderManager(new DirectoryAuthenticationProvider(passwords)))
|
||||||
.addFilterBefore(new SignInAvailabilityFilter(ad.enabled()), CsrfFilter.class)
|
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("/error", "/signin", "/signin/password", "/assets/**").permitAll()
|
.requestMatchers("/error", "/signin", "/signin/password", "/assets/**").permitAll()
|
||||||
.requestMatchers("/signin/mfa").access(passwordFactor.authenticated())
|
.requestMatchers("/signin/mfa").access(passwordFactor.authenticated())
|
||||||
@@ -68,4 +67,18 @@ class SecurityConfiguration {
|
|||||||
+ "object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'")))
|
+ "object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'")))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(3)
|
||||||
|
SecurityFilterChain fallback(HttpSecurity http) throws Exception {
|
||||||
|
return http.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers("/error").permitAll()
|
||||||
|
.anyRequest().denyAll())
|
||||||
|
.exceptionHandling(exceptions -> exceptions
|
||||||
|
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
|
||||||
|
.requestCache(cache -> cache.disable())
|
||||||
|
.logout(logout -> logout.disable())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ class IamLoginApplicationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void signInIsDisabledUntilDirectoryIsConfigured() throws Exception {
|
void signInIsDisabledUntilDirectoryIsConfigured() throws Exception {
|
||||||
mvc.perform(get("/signin").secure(true)).andExpect(status().isNotFound());
|
mvc.perform(get("/signin").secure(true)).andExpect(status().isUnauthorized());
|
||||||
mvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post("/signin/password")
|
mvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post("/signin/password")
|
||||||
.secure(true).with(org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf()))
|
.secure(true).with(org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf()))
|
||||||
.andExpect(status().isNotFound());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+30
-21
@@ -54,22 +54,22 @@ class SignInIntegrationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void browserRequiresHttpsCsrfAndOrderedSteps() throws Exception {
|
void browserRequiresHttpsCsrfAndOrderedSteps() throws Exception {
|
||||||
mvc.perform(get("/signin")).andExpect(status().isUpgradeRequired());
|
mvc.perform(get("/signin")).andExpect(redirectedUrl("https://localhost/signin"));
|
||||||
mvc.perform(get("/signin/mfa").secure(true)).andExpect(redirectedUrl("/signin"));
|
mvc.perform(get("/signin/mfa").with(https())).andExpect(redirectedUrl("/signin"));
|
||||||
mvc.perform(post("/signin/password").secure(true).param("username", "alice")
|
mvc.perform(post("/signin/password").with(https()).param("username", "alice")
|
||||||
.param("password", "fixture-password")).andExpect(status().isForbidden());
|
.param("password", "fixture-password")).andExpect(status().isForbidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void successfulPasswordRotatesSessionAndStopsBeforeMfa() throws Exception {
|
void successfulPasswordRotatesSessionAndStopsBeforeMfa() throws Exception {
|
||||||
var session = new MockHttpSession();
|
var session = new MockHttpSession();
|
||||||
mvc.perform(get("/signin").secure(true).session(session)).andExpect(status().isOk());
|
mvc.perform(get("/signin").with(https()).session(session)).andExpect(status().isOk());
|
||||||
String oldId = session.getId();
|
String oldId = session.getId();
|
||||||
mvc.perform(post("/signin/password").secure(true).session(session).with(csrf())
|
mvc.perform(post("/signin/password").with(https()).session(session).with(csrf())
|
||||||
.param("username", "alice").param("password", "fixture-password"))
|
.param("username", "alice").param("password", "fixture-password"))
|
||||||
.andExpect(redirectedUrl("/signin/mfa"));
|
.andExpect(redirectedUrl("/signin/mfa"));
|
||||||
assertThat(session.getId()).isNotEqualTo(oldId);
|
assertThat(session.getId()).isNotEqualTo(oldId);
|
||||||
var html = mvc.perform(get("/signin/mfa").secure(true).session(session))
|
var html = mvc.perform(get("/signin/mfa").with(https()).session(session))
|
||||||
.andExpect(status().isOk()).andExpect(header().string("Cache-Control", "no-store"))
|
.andExpect(status().isOk()).andExpect(header().string("Cache-Control", "no-store"))
|
||||||
.andReturn().getResponse().getContentAsString();
|
.andReturn().getResponse().getContentAsString();
|
||||||
assertThat(html).contains("mfa-pending", "gitea-admins", "\\u003c/script\\u003e")
|
assertThat(html).contains("mfa-pending", "gitea-admins", "\\u003c/script\\u003e")
|
||||||
@@ -78,7 +78,7 @@ class SignInIntegrationTests {
|
|||||||
assertThat(authentication.isAuthenticated()).isTrue();
|
assertThat(authentication.isAuthenticated()).isTrue();
|
||||||
assertThat(authentication.getCredentials()).isNull();
|
assertThat(authentication.getCredentials()).isNull();
|
||||||
assertThat(authentication.getAuthorities()).extracting("authority").containsExactly("FACTOR_PASSWORD");
|
assertThat(authentication.getAuthorities()).extracting("authority").containsExactly("FACTOR_PASSWORD");
|
||||||
mvc.perform(get("/").secure(true).session(session).accept(MediaType.APPLICATION_JSON))
|
mvc.perform(get("/").with(https()).session(session).accept(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -86,14 +86,14 @@ class SignInIntegrationTests {
|
|||||||
@Test
|
@Test
|
||||||
void failedPasswordDoesNotRetainIdentityAndRestartInvalidatesSession() throws Exception {
|
void failedPasswordDoesNotRetainIdentityAndRestartInvalidatesSession() throws Exception {
|
||||||
var session = new MockHttpSession();
|
var session = new MockHttpSession();
|
||||||
mvc.perform(get("/signin").secure(true).session(session));
|
mvc.perform(get("/signin").with(https()).session(session));
|
||||||
mvc.perform(post("/signin/password").secure(true).session(session).with(csrf())
|
mvc.perform(post("/signin/password").with(https()).session(session).with(csrf())
|
||||||
.param("username", "alice").param("password", "wrong"))
|
.param("username", "alice").param("password", "wrong"))
|
||||||
.andExpect(redirectedUrl("/signin?error"));
|
.andExpect(redirectedUrl("/signin?error"));
|
||||||
assertThat(session.getAttribute("SPRING_SECURITY_CONTEXT")).isNull();
|
assertThat(session.getAttribute("SPRING_SECURITY_CONTEXT")).isNull();
|
||||||
mvc.perform(get("/signin").secure(true).param("error", "").session(session))
|
mvc.perform(get("/signin").with(https()).param("error", "").session(session))
|
||||||
.andExpect(content().string(org.hamcrest.Matchers.containsString("无法验证账号")));
|
.andExpect(content().string(org.hamcrest.Matchers.containsString("无法验证账号")));
|
||||||
mvc.perform(post("/signin/restart").secure(true).session(session).with(csrf()))
|
mvc.perform(post("/signin/restart").with(https()).session(session).with(csrf()))
|
||||||
.andExpect(redirectedUrl("/signin"));
|
.andExpect(redirectedUrl("/signin"));
|
||||||
assertThat(session.isInvalid()).isTrue();
|
assertThat(session.isInvalid()).isTrue();
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,7 @@ class SignInIntegrationTests {
|
|||||||
void plaintextPasswordPostCannotReachDirectory() throws Exception {
|
void plaintextPasswordPostCannotReachDirectory() throws Exception {
|
||||||
int before = Directory.INSTANCE.binds.get();
|
int before = Directory.INSTANCE.binds.get();
|
||||||
mvc.perform(post("/signin/password").with(csrf()).param("username", "alice")
|
mvc.perform(post("/signin/password").with(csrf()).param("username", "alice")
|
||||||
.param("password", "fixture-password")).andExpect(status().isUpgradeRequired());
|
.param("password", "fixture-password")).andExpect(redirectedUrl("https://localhost/signin/password"));
|
||||||
assertThat(Directory.INSTANCE.binds.get()).isEqualTo(before);
|
assertThat(Directory.INSTANCE.binds.get()).isEqualTo(before);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,41 +113,50 @@ class SignInIntegrationTests {
|
|||||||
context.setAuthentication(UsernamePasswordAuthenticationToken.authenticated(previous.getPrincipal(), null,
|
context.setAuthentication(UsernamePasswordAuthenticationToken.authenticated(previous.getPrincipal(), null,
|
||||||
List.of(FactorGrantedAuthority.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)
|
List.of(FactorGrantedAuthority.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)
|
||||||
.issuedAt(Instant.now().minusSeconds(601)).build())));
|
.issuedAt(Instant.now().minusSeconds(601)).build())));
|
||||||
mvc.perform(get("/signin/mfa").secure(true).session(session))
|
mvc.perform(get("/signin/mfa").with(https()).session(session))
|
||||||
.andExpect(status().is3xxRedirection());
|
.andExpect(status().is3xxRedirection());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void directorySessionCannotScrapeManagementAndLogoutClearsAuthentication() throws Exception {
|
void directorySessionCannotScrapeManagementAndLogoutClearsAuthentication() throws Exception {
|
||||||
var session = login();
|
var session = login();
|
||||||
mvc.perform(get("/actuator/prometheus").secure(true).session(session))
|
mvc.perform(get("/actuator/prometheus").with(https()).session(session))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
mvc.perform(post("/signin/restart").session(session).with(csrf()))
|
mvc.perform(post("/signin/restart").session(session).with(csrf()))
|
||||||
.andExpect(status().isUpgradeRequired());
|
.andExpect(redirectedUrl("https://localhost/signin/restart"));
|
||||||
assertThat(session.isInvalid()).isFalse();
|
assertThat(session.isInvalid()).isFalse();
|
||||||
mvc.perform(post("/signin/restart").secure(true).session(session))
|
mvc.perform(post("/signin/restart").with(https()).session(session))
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
mvc.perform(post("/signin/restart").secure(true).session(session).with(csrf()))
|
mvc.perform(post("/signin/restart").with(https()).session(session).with(csrf()))
|
||||||
.andExpect(redirectedUrl("/signin"));
|
.andExpect(redirectedUrl("/signin"));
|
||||||
assertThat(session.isInvalid()).isTrue();
|
assertThat(session.isInvalid()).isTrue();
|
||||||
mvc.perform(get("/signin/mfa").secure(true)).andExpect(status().is3xxRedirection());
|
mvc.perform(get("/signin/mfa").with(https())).andExpect(status().is3xxRedirection());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void managementBasicCredentialsCannotAuthenticateBrowserLogin() throws Exception {
|
void managementBasicCredentialsCannotAuthenticateBrowserLogin() throws Exception {
|
||||||
mvc.perform(get("/actuator/prometheus").secure(true).with(
|
mvc.perform(get("/actuator/prometheus").with(https()).with(
|
||||||
org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
||||||
.httpBasic("fixture-monitor", "fixture-monitor-password")))
|
.httpBasic("fixture-monitor", "fixture-monitor-password")))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
mvc.perform(get("/signin/mfa").secure(true).with(
|
mvc.perform(get("/signin/mfa").with(https()).with(
|
||||||
org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
||||||
.httpBasic("fixture-monitor", "fixture-monitor-password")))
|
.httpBasic("fixture-monitor", "fixture-monitor-password")))
|
||||||
.andExpect(status().is3xxRedirection());
|
.andExpect(status().is3xxRedirection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static org.springframework.test.web.servlet.request.RequestPostProcessor https() {
|
||||||
|
return request -> {
|
||||||
|
request.setScheme("https");
|
||||||
|
request.setSecure(true);
|
||||||
|
request.setServerPort(443);
|
||||||
|
return request;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private MockHttpSession login() throws Exception {
|
private MockHttpSession login() throws Exception {
|
||||||
var session = new MockHttpSession();
|
var session = new MockHttpSession();
|
||||||
mvc.perform(post("/signin/password").secure(true).session(session).with(csrf())
|
mvc.perform(post("/signin/password").with(https()).session(session).with(csrf())
|
||||||
.param("username", "alice").param("password", "fixture-password"))
|
.param("username", "alice").param("password", "fixture-password"))
|
||||||
.andExpect(redirectedUrl("/signin/mfa"));
|
.andExpect(redirectedUrl("/signin/mfa"));
|
||||||
return session;
|
return session;
|
||||||
|
|||||||
Reference in New Issue
Block a user