我想在Angular&Spring应用程序中添加具有TOTP软令牌的多因素身份验证,同时使所有内容尽可能接近Spring Boot Security Starter的默认值。
令牌验证在本地进行(使用aerogear-otp-java库),没有第三方API提供程序。
为用户设置令牌是可行的,但无法通过利用Spring Security Authentication Manager / Providers来验证令牌。
TL; DR
- 将额外的AuthenticationProvider集成到Spring Boot Security Starter配置的系统中的正式方法是什么?
- 建议采用什么方法来防止重放攻击?
长版
该API具有一个端点/auth/token
,前端可以通过提供用户名和密码从该端点获取JWT令牌。该响应还包括一个身份验证状态,可以为AUTHENTICATED或PRE_AUTHENTICATED_MFA_REQUIRED。
如果用户需要MFA,则使用授予的单个授权PRE_AUTHENTICATED_MFA_REQUIRED
和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);
}
还有一个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
控制者
在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
但是,针对发布/auth/mfa-token
导致此错误:
"error": "Forbidden",
"message": "Access Denied",
"trace": "org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for de.....OneTimePasswordAuthenticationToken
为什么Spring Security无法选择我的身份验证提供程序?调试控制器将向我显示这DaoAuthenticationProvider
是中唯一的身份验证提供程序AuthenticationProviderManager
。
如果我将自己公开MfaAuthenticationProvider
为bean,则它是唯一注册的Provider,所以我得到相反的结果:
No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken.
那么,我如何同时获得两者呢?
我的问题
建议将附加组件集成AuthenticationProvider
到Spring Boot Security Starter配置的系统中的推荐方法是什么,这样我就可以得到DaoAuthenticationProvider
和我自己的自定义MfaAuthenticationProvider
?我想保留Spring Boot Scurity Starter的默认值,并另外拥有自己的提供程序。
防止重放攻击
我知道,OTP算法本身并不能在代码有效的时间段内防止重放攻击。RFC 6238明确了这一点
对于第一个OTP发出成功的验证后,验证者不得接受OTP的第二次尝试,以确保一次性使用一次OTP。
我想知道是否有推荐的方法来实现保护。由于OTP令牌是基于时间的,因此我正在考虑将最后一次成功的登录存储在用户的模型上,并确保每30秒的时间片只有一次成功的登录。当然,这意味着在用户模型上进行同步。还有更好的方法吗?
谢谢。
-
PS:由于这是关于安全性的问题,我正在寻找可靠和/或官方来源的答案。谢谢。