1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.github.rose.security.rest.mfa.provider.impl;
17
18 import io.github.rose.core.json.JsonUtils;
19 import io.github.rose.security.rest.mfa.config.OtpBasedMfaConfig;
20 import io.github.rose.security.rest.mfa.provider.MfaProvider;
21 import io.github.rose.security.rest.mfa.provider.OtpBasedMfaProviderConfig;
22 import io.github.rose.security.util.SecurityUser;
23 import org.apache.commons.lang3.RandomStringUtils;
24 import org.springframework.cache.CacheManager;
25 import org.springframework.stereotype.Component;
26
27 import java.io.Serializable;
28 import java.util.concurrent.TimeUnit;
29
30 import static io.github.rose.security.CacheConstants.TWO_FA_VERIFICATION_CODE_CACHE;
31
32 @Component
33 public abstract class OtpBasedMfaProvider<C extends OtpBasedMfaProviderConfig, A extends OtpBasedMfaConfig>
34 implements MfaProvider<C, A> {
35
36 private final CacheManager cacheManager;
37
38 public OtpBasedMfaProvider(CacheManager cacheManager) {
39 this.cacheManager = cacheManager;
40 }
41
42 @Override
43 public final void prepareVerificationCode(SecurityUser user, C providerConfig, A twoFaConfig) {
44 String verificationCode = RandomStringUtils.randomNumeric(6);
45 sendVerificationCode(user, verificationCode, providerConfig, twoFaConfig);
46 cacheManager
47 .getCache(TWO_FA_VERIFICATION_CODE_CACHE)
48 .put(
49 TWO_FA_VERIFICATION_CODE_CACHE + ":" + user.getUsername(),
50 JsonUtils.toBytes(new Otp(System.currentTimeMillis(), verificationCode, twoFaConfig)));
51 }
52
53 protected abstract void sendVerificationCode(
54 SecurityUser user, String verificationCode, C providerConfig, A accountConfig);
55
56 @Override
57 public final boolean checkVerificationCode(SecurityUser user, String code, C providerConfig, A twoFaConfig) {
58 String correctVerificationCode = cacheManager
59 .getCache(TWO_FA_VERIFICATION_CODE_CACHE)
60 .get(TWO_FA_VERIFICATION_CODE_CACHE + ":" + user.getUsername())
61 .toString();
62 Otp otp = JsonUtils.fromJson(correctVerificationCode, Otp.class);
63 if (correctVerificationCode != null) {
64 if (System.currentTimeMillis() - otp.getTimestamp()
65 > TimeUnit.SECONDS.toMillis(providerConfig.getVerificationCodeExpireTime())) {
66 cacheManager
67 .getCache(TWO_FA_VERIFICATION_CODE_CACHE)
68 .evict(TWO_FA_VERIFICATION_CODE_CACHE + ":" + user.getUsername());
69 return false;
70 }
71 if (code.equals(otp.getValue()) && twoFaConfig.equals(otp.getTwoFaConfig())) {
72 cacheManager
73 .getCache(TWO_FA_VERIFICATION_CODE_CACHE)
74 .evict(TWO_FA_VERIFICATION_CODE_CACHE + ":" + user.getUsername());
75 return true;
76 }
77 }
78 return false;
79 }
80
81 public static class Otp implements Serializable {
82
83 private final long timestamp;
84
85 private final String value;
86
87 private final OtpBasedMfaConfig twoFaConfig;
88
89 public Otp(long timestamp, String value, OtpBasedMfaConfig twoFaConfig) {
90 this.timestamp = timestamp;
91 this.value = value;
92 this.twoFaConfig = twoFaConfig;
93 }
94
95 public long getTimestamp() {
96 return timestamp;
97 }
98
99 public String getValue() {
100 return value;
101 }
102
103 public OtpBasedMfaConfig getTwoFaConfig() {
104 return twoFaConfig;
105 }
106 }
107 }