phi*_*sch 13 authentication spring-security spring-boot totp multi-factor-authentication
我想使用 TOTP 软令牌将多因素身份验证添加到 Angular 和 Spring 应用程序,同时使所有内容尽可能接近Spring Boot Security Starter的默认值。
令牌验证发生在本地(使用 aerogear-otp-java 库),没有第三方 API 提供者。
为用户设置令牌有效,但通过利用 Spring Security Authentication Manager/Providers 验证它们无效。
API 有一个端点/auth/token,前端可以通过提供用户名和密码从中获取 JWT 令牌。响应还包括身份验证状态,可以是AUTHENTICATED或PRE_AUTHENTICATED_MFA_REQUIRED。
如果用户需要 MFA,则颁发令牌的单个授予权限PRE_AUTHENTICATED_MFA_REQUIRED为 5 分钟,到期时间为 5 分钟。这允许用户访问端点/auth/mfa-token,在那里他们可以从他们的 Authenticator 应用程序提供 TOTP 代码,并获得完全验证的令牌以访问站点。
我已经创建了我的自定义MfaAuthenticationProvider实现AuthenticationProvider:
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// validate the OTP code
}
@Override
public boolean supports(Class<?> authentication) {
return OneTimePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
Run Code Online (Sandbox Code Playgroud)
还有一个OneTimePasswordAuthenticationToken扩展AbstractAuthenticationToken到保存用户名(取自签名的 JWT)和 OTP 代码。
我有我的自定义WebSecurityConfigurerAdapter,我AuthenticationProvider通过http.authenticationProvider(). 根据 JavaDoc,这似乎是正确的地方:
允许添加额外的 AuthenticationProvider 以供使用
我的相关部分SecurityConfig看起来像这样。
@Configuration
@EnableWebSecurity
@EnableJpaAuditing(auditorAwareRef = "appSecurityAuditorAware")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenProvider tokenProvider;
public SecurityConfig(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authenticationProvider(new MfaAuthenticationProvider());
http.authorizeRequests()
// Public endpoints, HTML, Assets, Error Pages and Login
.antMatchers("/", "favicon.ico", "/asset/**", "/pages/**", "/api/auth/token").permitAll()
// MFA auth endpoint
.antMatchers("/api/auth/mfa-token").hasAuthority(ROLE_PRE_AUTH_MFA_REQUIRED)
// much more config
Run Code Online (Sandbox Code Playgroud)
在AuthController已经将AuthenticationManagerBuilder注入是拉动它一起。
@RestController
@RequestMapping(AUTH)
public class AuthController {
private final TokenProvider tokenProvider;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public AuthController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
this.tokenProvider = tokenProvider;
this.authenticationManagerBuilder = authenticationManagerBuilder;
}
@PostMapping("/mfa-token")
public ResponseEntity<Token> mfaToken(@Valid @RequestBody OneTimePassword oneTimePassword) {
var username = SecurityUtils.getCurrentUserLogin().orElse("");
var authenticationToken = new OneTimePasswordAuthenticationToken(username, oneTimePassword.getCode());
var authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
// rest of class
Run Code Online (Sandbox Code Playgroud)
但是,发布反对/auth/mfa-token导致此错误:
"error": "Forbidden",
"message": "Access Denied",
"trace": "org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for de.....OneTimePasswordAuthenticationToken
Run Code Online (Sandbox Code Playgroud)
为什么 Spring Security 不选择我的身份验证提供程序?调试控制器向我展示了它DaoAuthenticationProvider是AuthenticationProviderManager.
如果我将我的MfaAuthenticationProvideras bean公开,它是唯一注册的 Provider,所以我得到相反的结果:
No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken.
Run Code Online (Sandbox Code Playgroud)
那么,我如何获得两者?
将附加组件集成AuthenticationProvider到Spring Boot Security Starter配置的系统中的推荐方法是什么,以便我同时获得DaoAuthenticationProvider和我自己的自定义MfaAuthenticationProvider?我想保留Spring Boot Scurity Starter的默认值并另外拥有自己的 Provider。
我知道 OTP 算法本身并不能在代码有效的时间片内防止重放攻击;RFC 6238 清楚地说明了这一点
在为第一个 OTP 发出成功验证后,验证者不得接受 OTP 的第二次尝试,这确保一次性仅使用 OTP。
我想知道是否有推荐的方法来实施保护。由于 OTP 令牌是基于时间的,我想将最后一次成功登录存储在用户模型上,并确保每 30 秒时间片只有一次成功登录。这当然意味着用户模型上的同步。有什么更好的方法吗?
谢谢你。
——
PS:由于这是一个关于安全的问题,我正在寻找来自可靠和/或官方来源的答案。谢谢你。
为了回答我自己的问题,这是我在进一步研究后实施它的方式。
我有一个提供者作为实现AuthenticationProvider. 它故意不是 Bean/组件。否则 Spring 会将其注册为唯一的 Provider。
public class MfaAuthenticationProvider implements AuthenticationProvider {
private final AccountService accountService;
@Override
public Authentication authenticate(Authentication authentication) {
// here be code
}
Run Code Online (Sandbox Code Playgroud)
在我的 SecurityConfig 中,我让 Spring 自动装配AuthenticationManagerBuilder并手动注入我的MfaAuthenticationProvider
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
@Override
protected void configure(HttpSecurity http) throws Exception {
// other code
authenticationManagerBuilder.authenticationProvider(getMfaAuthenticationProvider());
// more code
}
// package private for testing purposes.
MfaAuthenticationProvider getMfaAuthenticationProvider() {
return new MfaAuthenticationProvider(accountService);
}
Run Code Online (Sandbox Code Playgroud)
在标准身份验证之后,如果用户启用了 MFA,他们将使用PRE_AUTHENTICATED_MFA_REQUIRED的授予权限进行预身份验证。这允许他们访问单个端点/auth/mfa-token. 此端点从有效的 JWT 和提供的 TOTP 中获取用户名,并将其发送到authenticate()authenticationManagerBuilder的方法,该方法选择MfaAuthenticationProvider可以处理的OneTimePasswordAuthenticationToken。
var authenticationToken = new OneTimePasswordAuthenticationToken(usernameFromJwt, providedOtp);
var authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4650 次 |
| 最近记录: |