Browse Source

mobile login

MaxKey 4 years ago
parent
commit
b378f9fa2f
27 changed files with 445 additions and 309 deletions
  1. 40 6
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/AbstractAuthenticationProvider.java
  2. 8 3
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/RealmAuthenticationProvider.java
  3. 3 1
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/autoconfigure/AuthenticationAutoConfiguration.java
  4. 6 7
      maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/SmsOtpAuthn.java
  5. 4 4
      maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/sms/SmsOtpAuthnAliyun.java
  6. 5 5
      maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/sms/SmsOtpAuthnTencentCloud.java
  7. 7 5
      maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/sms/SmsOtpAuthnYunxin.java
  8. 4 6
      maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/sms/SmsOtpAuthnYunxinCheckSumBuilder.java
  9. 8 22
      maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/autoconfigure/SocialSignOnAutoConfiguration.java
  10. 17 5
      maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java
  11. 1 1
      maxkey-core/src/main/java/org/maxkey/persistence/db/LoginService.java
  12. 0 0
      maxkey-web-manage/src/main/resources/static/jquery/popper.min.js.map
  13. 1 1
      maxkey-web-manage/src/main/resources/templates/views/login.ftl
  14. 20 33
      maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java
  15. 8 8
      maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/ForgotPasswordContorller.java
  16. 13 6
      maxkey-web-maxkey/src/main/java/org/maxkey/web/endpoint/LoginEndpoint.java
  17. 2 2
      maxkey-web-maxkey/src/main/resources/application-https.properties
  18. 11 0
      maxkey-web-maxkey/src/main/resources/messages/message.properties
  19. 11 0
      maxkey-web-maxkey/src/main/resources/messages/message_en.properties
  20. 11 0
      maxkey-web-maxkey/src/main/resources/messages/message_zh_CN.properties
  21. 6 7
      maxkey-web-maxkey/src/main/resources/static/css/base.css
  22. 4 4
      maxkey-web-maxkey/src/main/resources/static/jquery/platform.common.js
  23. 0 0
      maxkey-web-maxkey/src/main/resources/static/jquery/popper.min.js.map
  24. 46 183
      maxkey-web-maxkey/src/main/resources/templates/views/login.ftl
  25. 57 0
      maxkey-web-maxkey/src/main/resources/templates/views/loginmobile.ftl
  26. 69 0
      maxkey-web-maxkey/src/main/resources/templates/views/loginnormal.ftl
  27. 83 0
      maxkey-web-maxkey/src/main/resources/templates/views/logintfa.ftl

+ 40 - 6
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/AbstractAuthenticationProvider.java

@@ -47,11 +47,18 @@ public abstract class AbstractAuthenticationProvider {
     private static final Logger _logger = 
             LoggerFactory.getLogger(AbstractAuthenticationProvider.class);
 
+    public class AuthType{
+    	public final static String NORMAL 	= "normal";
+    	public final static String TFA 		= "tfa";
+    	public final static String MOBILE 	= "mobile";
+    }
     protected ApplicationConfig applicationConfig;
 
     protected AbstractAuthenticationRealm authenticationRealm;
 
     protected AbstractOtpAuthn tfaOtpAuthn;
+    
+    protected AbstractOtpAuthn smsOtpAuthn;
 
     protected AbstractRemeberMeService remeberMeService;
     
@@ -176,8 +183,10 @@ public abstract class AbstractAuthenticationProvider {
     protected void authTypeValid(String authType) {
         _logger.debug("Login AuthN Type  " + authType);
         if (authType != null && (
-                authType.equalsIgnoreCase("basic") 
-                || authType.equalsIgnoreCase("tfa"))
+                authType.equalsIgnoreCase(AuthType.NORMAL) 
+                || authType.equalsIgnoreCase(AuthType.TFA)
+                || authType.equalsIgnoreCase(AuthType.MOBILE)
+        		)
             ) {
             return;
         }
@@ -195,7 +204,8 @@ public abstract class AbstractAuthenticationProvider {
      */
     protected void captchaValid(String captcha, String authType) {
         // for basic
-        if (applicationConfig.getLoginConfig().isCaptcha() && authType.equalsIgnoreCase("basic")) {
+        if (applicationConfig.getLoginConfig().isCaptcha() 
+        		&& authType.equalsIgnoreCase(AuthType.NORMAL)) {
             _logger.info("captcha : "
                     + WebContext.getSession().getAttribute(
                             WebConstants.KAPTCHA_SESSION_KEY).toString());
@@ -218,7 +228,8 @@ public abstract class AbstractAuthenticationProvider {
      */
     protected void tftcaptchaValid(String otpCaptcha, String authType, UserInfo userInfo) {
         // for one time password 2 factor
-        if (applicationConfig.getLoginConfig().isMfa() && authType.equalsIgnoreCase("tfa")) {
+        if (applicationConfig.getLoginConfig().isMfa() 
+        		&& authType.equalsIgnoreCase(AuthType.TFA)) {
             UserInfo validUserInfo = new UserInfo();
             validUserInfo.setUsername(userInfo.getUsername());
             validUserInfo.setSharedSecret(userInfo.getSharedSecret());
@@ -231,6 +242,28 @@ public abstract class AbstractAuthenticationProvider {
             }
         }
     }
+    
+    /**
+     * mobile validate.
+     * 
+     * @param otpCaptcha String
+     * @param authType   String
+     * @param userInfo   UserInfo
+     */
+    protected void mobilecaptchaValid(String password, String authType, UserInfo userInfo) {
+        // for mobile password
+        if (applicationConfig.getLoginConfig().isMfa() 
+        		&& authType.equalsIgnoreCase(AuthType.MOBILE)) {
+            UserInfo validUserInfo = new UserInfo();
+            validUserInfo.setUsername(userInfo.getUsername());
+            validUserInfo.setId(userInfo.getId());
+            if (password == null || !smsOtpAuthn.validate(validUserInfo, password)) {
+                String message = WebContext.getI18nValue("login.error.captcha");
+                _logger.debug("login captcha valid error.");
+                throw new BadCredentialsException(message);
+            }
+        }
+    }
 
     /**
      * login user by j_username and j_cname first query user by j_cname if first
@@ -328,7 +361,8 @@ public abstract class AbstractAuthenticationProvider {
     public void setOnlineTicketServices(OnlineTicketServices onlineTicketServices) {
         this.onlineTicketServices = onlineTicketServices;
     }
-    
-    
 
+	public void setSmsOtpAuthn(AbstractOtpAuthn smsOtpAuthn) {
+		this.smsOtpAuthn = smsOtpAuthn;
+	}
 }

+ 8 - 3
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/RealmAuthenticationProvider.java

@@ -62,11 +62,13 @@ public class RealmAuthenticationProvider extends AbstractAuthenticationProvider
     		AbstractAuthenticationRealm authenticationRealm,
     		ApplicationConfig applicationConfig,
     	    AbstractOtpAuthn tfaOtpAuthn,
+    	    AbstractOtpAuthn smsOtpAuthn,
     	    AbstractRemeberMeService remeberMeService,
     	    OnlineTicketServices onlineTicketServices) {
 		this.authenticationRealm = authenticationRealm;
 		this.applicationConfig = applicationConfig;
 		this.tfaOtpAuthn = tfaOtpAuthn;
+		this.smsOtpAuthn = smsOtpAuthn;
 		this.remeberMeService =  remeberMeService;
 		this.onlineTicketServices = onlineTicketServices;
 	}
@@ -96,9 +98,12 @@ public class RealmAuthenticationProvider extends AbstractAuthenticationProvider
 
         tftcaptchaValid(loginCredential.getOtpCaptcha(),loginCredential.getAuthType(),userInfo);
 
-        authenticationRealm.getPasswordPolicyValidator().passwordPolicyValid(userInfo);
-
-        authenticationRealm.passwordMatches(userInfo, loginCredential.getPassword());
+        if(loginCredential.getAuthType().equalsIgnoreCase(AuthType.MOBILE)) {
+        	mobilecaptchaValid(loginCredential.getPassword(),loginCredential.getAuthType(),userInfo);
+        }else {
+        	authenticationRealm.getPasswordPolicyValidator().passwordPolicyValid(userInfo);
+        	authenticationRealm.passwordMatches(userInfo, loginCredential.getPassword());
+        }
         
         UsernamePasswordAuthenticationToken authenticationToken = setOnline(loginCredential,userInfo);
         //RemeberMe Config check then set  RemeberMe cookies

+ 3 - 1
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/autoconfigure/AuthenticationAutoConfiguration.java

@@ -51,7 +51,6 @@ import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
-
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
 import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
@@ -77,14 +76,17 @@ public class AuthenticationAutoConfiguration  implements InitializingBean {
     		AbstractAuthenticationRealm authenticationRealm,
     		ApplicationConfig applicationConfig,
     	    AbstractOtpAuthn tfaOtpAuthn,
+    	    AbstractOtpAuthn smsOtpAuthn,
     	    AbstractRemeberMeService remeberMeService,
     	    OnlineTicketServices onlineTicketServices
     		) {
+       
     	_logger.debug("init authenticationProvider .");
         return new RealmAuthenticationProvider(
         		authenticationRealm,
         		applicationConfig,
         		tfaOtpAuthn,
+        		smsOtpAuthn,
         		remeberMeService,
         		onlineTicketServices
         		);

+ 6 - 7
maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/SmsOtpAuthn.java

@@ -47,13 +47,12 @@ public class SmsOtpAuthn extends AbstractOtpAuthn {
         return true;
     }
     
-    protected void loadProperties() throws IOException {
-        Resource resource = new ClassPathResource(
-                ConstantsProperties.classPathResource(
-                        ConstantsProperties.classPathResource(
-                                ConstantsProperties.applicationPropertySource)));
-        properties = new Properties();
-        properties.load(resource.getInputStream());
+    public void setProperties(Properties properties) {
+		this.properties = properties;
+	}
+
+	protected void loadProperties() throws IOException {
+
     }
     
     public void initPropertys() {

+ 4 - 4
maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/sms/SmsOtpAuthnAliyun.java

@@ -138,10 +138,10 @@ public class SmsOtpAuthnAliyun extends SmsOtpAuthn {
             e.printStackTrace();
         }
         
-        this.accessKeyId = this.properties.getProperty("config.otp.sms.aliyun.accesskeyid");
-        this.accessSecret = this.properties.getProperty("config.otp.sms.aliyun.accesssecret");
-        this.templateCode = this.properties.getProperty("config.otp.sms.aliyun.templatecode");
-        this.signName = this.properties.getProperty("config.otp.sms.aliyun.signname");
+        this.accessKeyId = this.properties.getProperty("maxkey.otp.sms.aliyun.accesskeyid");
+        this.accessSecret = this.properties.getProperty("maxkey.otp.sms.aliyun.accesssecret");
+        this.templateCode = this.properties.getProperty("maxkey.otp.sms.aliyun.templatecode");
+        this.signName = this.properties.getProperty("maxkey.otp.sms.aliyun.signname");
     }
     
 }

+ 5 - 5
maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/sms/SmsOtpAuthnTencentCloud.java

@@ -181,11 +181,11 @@ public class SmsOtpAuthnTencentCloud extends SmsOtpAuthn {
             e.printStackTrace();
         }
         
-        this.secretId = this.properties.getProperty("config.otp.sms.tencentcloud.secretid");
-        this.secretKey = this.properties.getProperty("config.otp.sms.tencentcloud.secretkey");
-        this.smsSdkAppid = this.properties.getProperty("config.otp.sms.tencentcloud.smssdkappid");
-        this.templateId = this.properties.getProperty("config.otp.sms.tencentcloud.templateid");
-        this.sign = this.properties.getProperty("config.otp.sms.tencentcloud.sign");
+        this.secretId = this.properties.getProperty("maxkey.otp.sms.tencentcloud.secretid");
+        this.secretKey = this.properties.getProperty("maxkey.otp.sms.tencentcloud.secretkey");
+        this.smsSdkAppid = this.properties.getProperty("maxkey.otp.sms.tencentcloud.smssdkappid");
+        this.templateId = this.properties.getProperty("maxkey.otp.sms.tencentcloud.templateid");
+        this.sign = this.properties.getProperty("maxkey.otp.sms.tencentcloud.sign");
     }
     
 }

+ 7 - 5
maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/sms/SmsOtpAuthnYunxin.java

@@ -78,7 +78,7 @@ public class SmsOtpAuthnYunxin extends SmsOtpAuthn {
                     ).randomGenerate();
                 String checkSum = SmsOtpAuthnYunxinCheckSumBuilder
                         .getCheckSum(appSecret, nonce, curTime);
-        
+                logger.debug("AppKey " +appKey+" ,Nonce "+nonce+", CurTime "+curTime+" ,checkSum "+checkSum);
                 // 设置请求的header
                 httpPost.addHeader("AppKey", appKey);
                 httpPost.addHeader("Nonce", nonce);
@@ -118,9 +118,11 @@ public class SmsOtpAuthnYunxin extends SmsOtpAuthn {
                 YunxinSms  yunxinSms = 
                         JsonUtils.gson2Object(responseString,YunxinSms.class);
                 logger.debug("responseEntity code " + yunxinSms.getObj());
+                nonce = yunxinSms.getObj() == null ?nonce:yunxinSms.getObj();
+                logger.debug("nonce " + nonce);
                 this.optTokenStore.store(
                                         userInfo, 
-                                        yunxinSms.getObj(), 
+                                        nonce, 
                                         userInfo.getMobile(), 
                                         OtpTypes.SMS);
                 return true;
@@ -210,9 +212,9 @@ public class SmsOtpAuthnYunxin extends SmsOtpAuthn {
             e.printStackTrace();
         }
         
-        this.appKey = this.properties.getProperty("config.otp.sms.yunxin.appkey");
-        this.appSecret = this.properties.getProperty("config.otp.sms.yunxin.appsecret");
-        this.templateId = this.properties.getProperty("config.otp.sms.yunxin.templateid");
+        this.appKey = this.properties.getProperty("maxkey.otp.sms.yunxin.appkey");
+        this.appSecret = this.properties.getProperty("maxkey.otp.sms.yunxin.appsecret");
+        this.templateId = this.properties.getProperty("maxkey.otp.sms.yunxin.templateid");
     }
     
     /**

+ 4 - 6
maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/impl/sms/SmsOtpAuthnYunxinCheckSumBuilder.java

@@ -35,14 +35,14 @@ public class SmsOtpAuthnYunxinCheckSumBuilder {
             return null;
         }
         try {
-            MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
+            MessageDigest messageDigest
+                    = MessageDigest.getInstance(algorithm);
             messageDigest.update(value.getBytes());
             return getFormattedText(messageDigest.digest());
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
-
     private static String getFormattedText(byte[] bytes) {
         int len = bytes.length;
         StringBuilder buf = new StringBuilder(len * 2);
@@ -52,8 +52,6 @@ public class SmsOtpAuthnYunxinCheckSumBuilder {
         }
         return buf.toString();
     }
-
-    private static final char[] HEX_DIGITS = { 
-            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
-            'a', 'b', 'c', 'd','e', 'f' };
+    private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
+            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
 }

+ 8 - 22
maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/autoconfigure/SocialSignOnAutoConfiguration.java

@@ -21,7 +21,6 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
-
 import org.maxkey.authn.support.socialsignon.service.JdbcSocialsAssociateService;
 import org.maxkey.authn.support.socialsignon.service.SocialSignOnProvider;
 import org.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService;
@@ -29,14 +28,11 @@ import org.maxkey.constants.ConstantsProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
 import org.springframework.jdbc.core.JdbcTemplate;
 
 @Configuration
@@ -50,27 +46,17 @@ public class SocialSignOnAutoConfiguration implements InitializingBean {
     @Bean(name = "socialSignOnProviderService")
     @ConditionalOnClass(SocialSignOnProvider.class)
     public SocialSignOnProviderService socialSignOnProviderService(
-    		@Value("${spring.profiles.active}")String profilesActive) throws IOException {
+    		Properties applicationProperty) throws IOException {
         SocialSignOnProviderService socialSignOnProviderService = new SocialSignOnProviderService();
-        
-        _logger.trace("spring.profiles.active " + profilesActive);
-        
-        Resource resource = new ClassPathResource(
-                    ConstantsProperties.classPathResource(
-                    		ConstantsProperties.classPathResource(
-                    				ConstantsProperties.applicationPropertySource,
-                    				profilesActive)));
-        
-        Properties properties = new Properties();
-        properties.load(resource.getInputStream());
-        String [] providerList =properties.get("maxkey.login.socialsignon.providers").toString().split(",");
+   
+        String [] providerList =applicationProperty.get("maxkey.login.socialsignon.providers").toString().split(",");
         List<SocialSignOnProvider> socialSignOnProviderList = new ArrayList<SocialSignOnProvider>();
         for(String provider : providerList) {
-            String providerName = properties.getProperty("maxkey.socialsignon."+provider+".provider.name");
-            String icon=properties.getProperty("maxkey.socialsignon."+provider+".icon");
-            String clientId=properties.getProperty("maxkey.socialsignon."+provider+".client.id");
-            String clientSecret=properties.getProperty("maxkey.socialsignon."+provider+".client.secret");
-            String sortOrder = properties.getProperty("maxkey.socialsignon."+provider+".sortorder");
+            String providerName = applicationProperty.getProperty("maxkey.socialsignon."+provider+".provider.name");
+            String icon=applicationProperty.getProperty("maxkey.socialsignon."+provider+".icon");
+            String clientId=applicationProperty.getProperty("maxkey.socialsignon."+provider+".client.id");
+            String clientSecret=applicationProperty.getProperty("maxkey.socialsignon."+provider+".client.secret");
+            String sortOrder = applicationProperty.getProperty("maxkey.socialsignon."+provider+".sortorder");
             SocialSignOnProvider socialSignOnProvider = new SocialSignOnProvider();
             socialSignOnProvider.setProvider(provider);
             socialSignOnProvider.setProviderName(providerName);

+ 17 - 5
maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java

@@ -21,6 +21,7 @@ import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Properties;
 
 import javax.sql.DataSource;
 import org.maxkey.constants.ConstantsProperties;
@@ -82,19 +83,30 @@ public class ApplicationAutoConfiguration  implements InitializingBean {
                 new ClassPathResource(ConstantsProperties.classPathResource(
                         ConstantsProperties.applicationPropertySource));
 
-
         PropertySourcesPlaceholderConfigurer configurer = 
                 new PropertySourcesPlaceholderConfigurer();
         configurer.setLocations(classPathApplicationPropertySource);
-        /*configurer.setLocations(
-                classPathResource1,
-                classPathResource2
-        );*/
         configurer.setIgnoreUnresolvablePlaceholders(true);
         _logger.debug("PropertySourcesPlaceholderConfigurer init");
+        
         return configurer;
     }
     
+    @Bean (name = "applicationProperty")
+    public Properties applicationProperty(
+    		@Value("${spring.profiles.active:}")String profilesActive) throws IOException {
+    	 Resource resource = new ClassPathResource(
+                 ConstantsProperties.classPathResource(
+                 		ConstantsProperties.classPathResource(
+                 				ConstantsProperties.applicationPropertySource,
+                 				profilesActive)));
+     
+    	 Properties properties = new Properties();
+    	 properties.load(resource.getInputStream());
+    	 return properties;
+    }
+    
+    
     @Bean(name = "passwordReciprocal")
     public PasswordReciprocal passwordReciprocal() {
         return new PasswordReciprocal();

+ 1 - 1
maxkey-core/src/main/java/org/maxkey/persistence/db/LoginService.java

@@ -67,7 +67,7 @@ public class LoginService {
     /**
      * 1 (USERNAME)  2 (USERNAME | MOBILE) 3 (USERNAME | MOBILE | EMAIL)
      */
-    public  static  int LOGIN_ATTRIBUTE_TYPE = 1;
+    public  static  int LOGIN_ATTRIBUTE_TYPE = 2;
     
     public LoginService(){
         

File diff suppressed because it is too large
+ 0 - 0
maxkey-web-manage/src/main/resources/static/jquery/popper.min.js.map


+ 1 - 1
maxkey-web-manage/src/main/resources/templates/views/login.ftl

@@ -48,7 +48,7 @@
 								</div>
 								</#if>
 								<div class="form-group text-center m-t-20">
-									<input type="hidden" name="authType" value="basic" /> 
+									<input type="hidden" name="authType" value="normal" /> 
 									<input type='hidden' id="sessionid" name="sessionId" value="${sessionid}" />
 									<button id="loginSubmit" class="button btn-primary btn btn-common btn-block" type="submit">
 										<@locale code="login.button.login" />

+ 20 - 33
maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java

@@ -19,6 +19,8 @@ package org.maxkey;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Properties;
+
 import org.maxkey.authn.realm.jdbc.JdbcAuthenticationRealm;
 import org.maxkey.authn.realm.ldap.LdapAuthenticationRealm;
 import org.maxkey.authn.realm.ldap.LdapServer;
@@ -164,7 +166,13 @@ public class MaxKeyConfig  implements InitializingBean {
     			@Value("${maxkey.support.ldap.basedn}")String baseDN,
     			@Value("${maxkey.support.ldap.domain}")String domain,
     			@Value("${maxkey.support.ldap.product:openldap}")String product) {
-    	
+    	AbstractAuthenticationRealm ldapAuthenticationRealm = 
+    			ldapAuthenticationRealm(
+					ldapSupport,ldapJit,
+					providerUrl,principal,credentials,
+					filter,baseDN,domain,product,
+					jdbcTemplate
+				);
         JdbcAuthenticationRealm authenticationRealm = new JdbcAuthenticationRealm(
         		passwordEncoder,
         		passwordPolicyValidator,
@@ -172,18 +180,13 @@ public class MaxKeyConfig  implements InitializingBean {
         		loginHistoryService,
         		remeberMeService,
         		jdbcTemplate,
-        		ldapAuthenticationRealm(
-        				ldapSupport,ldapJit,
-        				providerUrl,principal,credentials,
-        				filter,baseDN,domain,product,
-        				jdbcTemplate),
-        		ldapSupport);
+        		ldapAuthenticationRealm,
+        		ldapSupport
+        	);
         
         return authenticationRealm;
     }
     
-    
-    
 	@Bean(name = "timeBasedOtpAuthn")
     public TimeBasedOtpAuthn timeBasedOtpAuthn() {
 	    TimeBasedOtpAuthn tfaOtpAuthn = new TimeBasedOtpAuthn();
@@ -191,32 +194,14 @@ public class MaxKeyConfig  implements InitializingBean {
         return tfaOtpAuthn;
     }
     
-    //default tfaOtpAuthn
     @Bean(name = "tfaOtpAuthn")
     public AbstractOtpAuthn tfaOptAuthn(
             @Value("${maxkey.login.mfa.type}")String mfaType,
             @Value("${maxkey.server.persistence}") int persistence,
-            MailOtpAuthn tfaMailOtpAuthn,
             RedisConnectionFactory redisConnFactory) {    
-        
-        AbstractOtpAuthn tfaOtpAuthn  = null;
-        if(mfaType.equalsIgnoreCase("SmsOtpAuthnAliyun")) {
-        	tfaOtpAuthn = new SmsOtpAuthnAliyun();
-            _logger.debug("SmsOtpAuthnAliyun inited.");
-        }else if(mfaType.equalsIgnoreCase("SmsOtpAuthnTencentCloud")) {
-        	tfaOtpAuthn = new SmsOtpAuthnTencentCloud();
-            _logger.debug("SmsOtpAuthnTencentCloud inited.");
-        }else if(mfaType.equalsIgnoreCase("SmsOtpAuthnYunxin")) {
-        	tfaOtpAuthn = new SmsOtpAuthnYunxin();
-            _logger.debug("SmsOtpAuthnYunxin inited.");
-        }else if(mfaType.equalsIgnoreCase("MailOtpAuthn")) {
-        	tfaOtpAuthn = tfaMailOtpAuthn;
-            _logger.debug("MailOtpAuthn inited.");
-        }else {
-        	tfaOtpAuthn = new TimeBasedOtpAuthn();
-            _logger.debug("TimeBasedOtpAuthn inited.");
-        }
-        
+        AbstractOtpAuthn tfaOtpAuthn  = new TimeBasedOtpAuthn();
+        _logger.debug("TimeBasedOtpAuthn inited.");
+
         if (persistence == ConstantsPersistence.REDIS) {
             RedisOtpTokenStore redisOptTokenStore = new RedisOtpTokenStore(redisConnFactory);
             tfaOtpAuthn.setOptTokenStore(redisOptTokenStore);
@@ -226,7 +211,7 @@ public class MaxKeyConfig  implements InitializingBean {
         return tfaOtpAuthn;
     }
     
-    @Bean(name = "tfaMailOtpAuthn")
+    @Bean(name = "mailOtpAuthn")
     public MailOtpAuthn mailOtpAuthn(
             @Value("${spring.mail.properties.mailotp.message.subject}")
             String messageSubject,
@@ -236,14 +221,15 @@ public class MaxKeyConfig  implements InitializingBean {
         MailOtpAuthn mailOtpAuthn = new MailOtpAuthn();
         mailOtpAuthn.setSubject(messageSubject);
         mailOtpAuthn.setMessageTemplate(messageTemplate);
-        _logger.debug("tfaMailOtpAuthn inited.");
+        _logger.debug("MailOtpAuthn inited.");
         return mailOtpAuthn;
     }
     
-    @Bean(name = "tfaMobileOtpAuthn")
+    @Bean(name = "smsOtpAuthn")
     public SmsOtpAuthn smsOtpAuthn(
             @Value("${maxkey.otp.sms}")String optSmsProvider,
             @Value("${maxkey.server.persistence}") int persistence,
+            Properties applicationProperty,
             RedisConnectionFactory redisConnFactory) {
         SmsOtpAuthn smsOtpAuthn = null;
         if(optSmsProvider.equalsIgnoreCase("SmsOtpAuthnAliyun")) {
@@ -257,6 +243,7 @@ public class MaxKeyConfig  implements InitializingBean {
             RedisOtpTokenStore redisOptTokenStore = new RedisOtpTokenStore(redisConnFactory);
             smsOtpAuthn.setOptTokenStore(redisOptTokenStore);
         }
+        smsOtpAuthn.setProperties(applicationProperty);
         smsOtpAuthn.initPropertys();
         
         _logger.debug("SmsOtpAuthn inited.");

+ 8 - 8
maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/ForgotPasswordContorller.java

@@ -62,12 +62,12 @@ public class ForgotPasswordContorller {
     private UserInfoService userInfoService;
 
     @Autowired
-    @Qualifier("tfaMailOtpAuthn")
-    protected AbstractOtpAuthn tfaMailOtpAuthn;
+    @Qualifier("mailOtpAuthn")
+    protected AbstractOtpAuthn mailOtpAuthn;
     
     @Autowired
-    @Qualifier("tfaMobileOtpAuthn")
-    protected AbstractOtpAuthn tfaMobileOtpAuthn;
+    @Qualifier("smsOtpAuthn")
+    protected AbstractOtpAuthn smsOtpAuthn;
     
 
     @RequestMapping(value = { "/forward" })
@@ -89,10 +89,10 @@ public class ForgotPasswordContorller {
             
             Matcher matcher = emailRegex.matcher(emailMobile);
             if (matcher.matches() && null != userInfo) {
-                tfaMailOtpAuthn.produce(userInfo);
+            	mailOtpAuthn.produce(userInfo);
                 forgotType = ForgotType.EMAIL;
             }else if (null != userInfo) {
-                tfaMobileOtpAuthn.produce(userInfo);
+            	smsOtpAuthn.produce(userInfo);
                 forgotType = ForgotType.MOBILE;
             }
            
@@ -126,8 +126,8 @@ public class ForgotPasswordContorller {
             userInfo.setUsername(username);
             userInfo.setPassword(password);
             userInfo.setDecipherable(password);
-            if ((forgotType == ForgotType.EMAIL && tfaMailOtpAuthn.validate(userInfo, captcha)) ||
-                    (forgotType == ForgotType.MOBILE && tfaMobileOtpAuthn.validate(userInfo, captcha))
+            if ((forgotType == ForgotType.EMAIL && mailOtpAuthn.validate(userInfo, captcha)) ||
+                    (forgotType == ForgotType.MOBILE && smsOtpAuthn.validate(userInfo, captcha))
                 ) {
                 userInfoService.changePassword(userInfo);
                 modelAndView.addObject("passwordResetResult", PasswordResetResult.SUCCESS);

+ 13 - 6
maxkey-web-maxkey/src/main/java/org/maxkey/web/endpoint/LoginEndpoint.java

@@ -19,6 +19,8 @@ package org.maxkey.web.endpoint;
 
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.regex.Pattern;
+
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -79,6 +81,13 @@ public class LoginEndpoint {
     @Qualifier("tfaOtpAuthn")
     protected AbstractOtpAuthn tfaOtpAuthn;
 	
+	@Autowired
+    @Qualifier("smsOtpAuthn")
+    protected AbstractOtpAuthn smsOtpAuthn;
+	
+	Pattern mobileRegex = Pattern.compile(
+	            "^(13[4,5,6,7,8,9]|15[0,8,9,1,7]|188|187)\\\\d{8}$");
+	
 	/**
 	 * init login
 	 * @return
@@ -154,14 +163,12 @@ public class LoginEndpoint {
  		return authnType;
  	}
  	
- 	@RequestMapping("/login/otp/{username}")
+ 	@RequestMapping("/login/sendsms/{mobile}")
     @ResponseBody
-    public String produceOtp(@PathVariable("username") String username) {
-        UserInfo userInfo = new UserInfo();
-        userInfo.setUsername(username);
-        UserInfo queryUserInfo=userInfoService.loadByUsername(username);//(userInfo);
+    public String produceOtp(@PathVariable("mobile") String mobile) {
+        UserInfo queryUserInfo=userInfoService.queryUserInfoByEmailMobile(mobile);
         if(queryUserInfo!=null) {
-        	tfaOtpAuthn.produce(queryUserInfo);
+        	smsOtpAuthn.produce(queryUserInfo);
             return "ok";
         }
         

+ 2 - 2
maxkey-web-maxkey/src/main/resources/application-https.properties

@@ -206,9 +206,9 @@ maxkey.otp.sms.aliyun.accesssecret=05d5485357bc
 maxkey.otp.sms.aliyun.templatecode=14860095
 maxkey.otp.sms.aliyun.signname=maxkey
 #yunxin
-maxkey.otp.sms.yunxin.appkey=94395d754eb55693043f5d6a2b772ef4
+maxkey.otp.sms.yunxin.appkey=94395d754eb55693043f5d6a2b772ef3
 maxkey.otp.sms.yunxin.appsecret=05d5485357bc
-maxkey.otp.sms.yunxin.templateid=14860095
+maxkey.otp.sms.yunxin.templateid=14860099
 #tencentcloud
 maxkey.otp.sms.tencentcloud.secretid=94395d754eb55693043f5d6a2b772ef4
 maxkey.otp.sms.tencentcloud.secretkey=05d5485357bc

+ 11 - 0
maxkey-web-maxkey/src/main/resources/messages/message.properties

@@ -42,11 +42,22 @@ login.text.login.twofactor.obtain=\u83b7\u53d6\u52a8\u6001\u9a8c\u8bc1\u7801
 login.text.login.twofactor.obtain.valid.unit=\u79d2
 login.text.login.twofactor.validTime=\u5269\u4f59\u65f6\u95f4
 login.text.login.twofactor.validTime.unit=\u79d2
+
+login.text.login.mobile.obtain.valid=\u91cd\u65b0\u83b7\u53d6
+login.text.login.mobile.obtain=\u53D1\u9001\u9a8c\u8bc1\u7801
+login.text.login.mobile.obtain.valid.unit=\u79d2
+login.text.login.mobile.validTime=\u5269\u4f59\u65f6\u95f4
+login.text.login.mobile.validTime.unit=\u79d2
+
 login.text.login.twofactor=\u5b89\u5168\u8ba4\u8bc1
 login.text.login.normal=\u57fa\u672c\u8ba4\u8bc1
+login.text.login.mobile=\u624B\u673A\u767B\u5F55
+
 login.text.username=\u7528\u6237\u540d
+login.text.mobile=\u624B\u673A\u53F7\u7801
 login.text.password=\u5bc6&nbsp;&nbsp;&nbsp;&nbsp;\u7801
 login.text.captcha=\u9a8c\u8bc1\u7801
+login.text.smscode=\u77ED\u4FE1\u9a8c\u8bc1\u7801
 login.text.remeberme=\u8bb0\u4f4f\u767b\u5f55
 login.text.forgotpassword=\u5fd8\u8bb0\u5bc6\u7801
 login.button.login=\u767b\u5f55

+ 11 - 0
maxkey-web-maxkey/src/main/resources/messages/message_en.properties

@@ -42,11 +42,22 @@ login.text.login.twofactor.obtain=Get dynamic verification code
 login.text.login.twofactor.obtain.valid.unit=seconds
 login.text.login.twofactor.validTime=Remaining
 login.text.login.twofactor.validTime.unit=seconds
+
+login.text.login.mobile.obtain.valid=Resend
+login.text.login.mobile.obtain=Send code
+login.text.login.mobile.obtain.valid.unit=seconds
+login.text.login.mobile.validTime=Remaining
+login.text.login.mobile.validTime.unit=seconds
+
 login.text.login.twofactor=Two-Factors
 login.text.login.normal=Normal Login
+login.text.login.mobile=Mobile Login
+
 login.text.username=Username
+login.text.mobile=Phone Number
 login.text.password=Password
 login.text.captcha=CAPTCHA
+login.text.smscode=Message Code
 login.text.remeberme=RemeberMe
 login.text.forgotpassword=Forgot Password
 login.button.login=Login

+ 11 - 0
maxkey-web-maxkey/src/main/resources/messages/message_zh_CN.properties

@@ -42,11 +42,22 @@ login.text.login.twofactor.obtain=\u83b7\u53d6\u52a8\u6001\u9a8c\u8bc1\u7801
 login.text.login.twofactor.obtain.valid.unit=\u79d2
 login.text.login.twofactor.validTime=\u5269\u4f59\u65f6\u95f4
 login.text.login.twofactor.validTime.unit=\u79d2
+
+login.text.login.mobile.obtain.valid=\u91cd\u65b0\u83b7\u53d6
+login.text.login.mobile.obtain=\u53D1\u9001\u9a8c\u8bc1\u7801
+login.text.login.mobile.obtain.valid.unit=\u79d2
+login.text.login.mobile.validTime=\u5269\u4f59\u65f6\u95f4
+login.text.login.mobile.validTime.unit=\u79d2
+
 login.text.login.twofactor=\u5b89\u5168\u8ba4\u8bc1
 login.text.login.normal=\u57fa\u672c\u8ba4\u8bc1
+login.text.login.mobile=\u624B\u673A\u767B\u5F55
+
 login.text.username=\u7528\u6237\u540d
+login.text.mobile=\u624B\u673A\u53F7\u7801
 login.text.password=\u5bc6&nbsp;&nbsp;&nbsp;&nbsp;\u7801
 login.text.captcha=\u9a8c\u8bc1\u7801
+login.text.smscode=\u77ED\u4FE1\u9a8c\u8bc1\u7801
 login.text.remeberme=\u8bb0\u4f4f\u767b\u5f55
 login.text.forgotpassword=\u5fd8\u8bb0\u5bc6\u7801
 login.button.login=\u767b\u5f55

+ 6 - 7
maxkey-web-maxkey/src/main/resources/static/css/base.css

@@ -310,15 +310,14 @@ body{
 	font-weight: bold;
 }
 
-#tfa_j_otp_captcha{
-	width	:120px;
-	/*width	:230px;*/
+#tfa_j_otp_captcha,#mobile_j_password{
+	width	:110px;
 	font-size: 14px;
 	font-weight: bold;	
 }
 
-#tfa_j_otp_captcha_button{
-	width	:130px;
+#tfa_j_otp_captcha_button,#mobile_j_otp_captcha_button{
+	width	:120px;
 	height: 34px;
 	font-size: 14px;
 	font-weight: bold;	
@@ -330,11 +329,11 @@ body{
 	vertical-align: top;
 }
 
-#switch_commonLogin,#switch_tfaLogin{
+#normalLogin,#tfaLogin,#mobileLogin{
 	width	:49%;
 }
 
-#div_tfaLogin{
+#div_tfaLogin , #div_mobileLogin{
 	display: none;
 }
 

+ 4 - 4
maxkey-web-maxkey/src/main/resources/static/jquery/platform.common.js

@@ -35,16 +35,16 @@ $(function(){
 		if($(".switch_tab_current").attr("id")==(this.id)){
 			return;
 		}
-		
+
 		$(".switch_tab .switch_tab_class").removeClass("switch_tab_current");
 		$(this).addClass("switch_tab_current");
 		$(".switch_tab li").each(function(){
-		    $("#"+$(this).attr("value")).hide();
+		    $("#div_"+$(this).attr("id")).hide();
 		});
 		
-		$("#"+$(this).attr("value")).show();
+		$("#div_"+$(this).attr("id")).show();
 		if (typeof(switchTab) == "function"){
-			switchTab($(this).attr("value"));//user define after switch Tab
+			switchTab($(this).attr("id"));//user define after switch Tab
 		}
 	});
 	//document forward

File diff suppressed because it is too large
+ 0 - 0
maxkey-web-maxkey/src/main/resources/static/jquery/popper.min.js.map


+ 46 - 183
maxkey-web-maxkey/src/main/resources/templates/views/login.ftl

@@ -31,12 +31,12 @@
 	var captchaCountTimer;
 	var captchaCount=60;
 	function getCaptchaCount(){
-		$("#tfa_j_otp_captcha_button").val("<@locale code="login.text.login.twofactor.obtain.valid"/>("+captchaCount+")<@locale code="login.text.login.twofactor.obtain.valid.unit"/>");
+		$("#mobile_j_otp_captcha_button").val("<@locale code="login.text.login.mobile.obtain.valid"/>("+captchaCount+")<@locale code="login.text.login.mobile.obtain.valid.unit"/>");
 		
 		
 		captchaCount--;
 		if(captchaCount==0){
-			$("#tfa_j_otp_captcha_button").val("<@locale code="login.text.login.twofactor.obtain"/>");
+			$("#mobile_j_otp_captcha_button").val("<@locale code="login.text.login.mobile.obtain"/>");
 			captchaCount=60;
 			clearInterval(captchaCountTimer);
 		}
@@ -93,26 +93,20 @@
 		$("#tfa_j_otp_captcha_button").val("<@locale code="login.text.login.twofactor.validTime"/>("+timeBaseCount+")<@locale code="login.text.login.twofactor.validTime.unit"/>");
 	};
 	</#if>
-	var currentSwitchTab="div_commonLogin";
+	var currentSwitchTab="normalLogin";
 	<#--submit form-->		
 	function doLoginSubmit(){
-		if(currentSwitchTab=="div_commonLogin"){
-			$.cookie("username", $("#loginForm input[name=j_username]").val(), { expires: 7 });
-			$.cookie("switch_form", 1, { expires: 7 });
-			$("#loginSubmitButton").click();
-		}else{
-			$.cookie("username", $("#tfaLoginForm input[name=j_username]").val(), { expires: 7 });
-			$.cookie("switch_form", 2, { expires: 7 });
-			$("#tfaLoginSubmitButton").click();
-		}
+		$.cookie("username", $("#"+currentSwitchTab+"Form input[name=username]").val(), { expires: 7 });
+		$("#"+currentSwitchTab+"SubmitButton").click();
+		$.cookie("switch_tab", currentSwitchTab, { expires: 7 });
 	};
 	
-	<#--switch LoginForm && tfaLoginForm-->
+	<#--switch Login Form-->
 	function switchTab(id){
-		if($("#"+id+" input[name=j_username]").val()==""){
-			$("#"+id+" input[name=j_username]").focus();
+		if($("#"+id+"Form  input[name=username]").val()==""){
+			$("#"+id+"Form input[name=username]").focus();
 		}else{
-			$("#"+id+" input[name=j_password]").focus();
+			$("#"+id+"Form  input[name=password]").focus();
 		}
 		currentSwitchTab=id;
 	}
@@ -130,39 +124,30 @@
 		</#if>
 	
 		<#--submit loginForme-->
-		$("#loginSubmit").on("click",function(){
-				doLoginSubmit();
-		});
-		<#--submit tfaLoginForme-->	
-		$("#tfa_loginSubmit").on("click",function(){
+		$(".doLoginSubmit").on("click",function(){
 				doLoginSubmit();
 		});
 		
 		<#--read username cookie for login e-->		
 		if($.cookie("username")!=undefined&&$.cookie("username")!=""){
-			$("input[name=j_username]").val($.cookie("username")==undefined?"":$.cookie("username"));
-			$("input[name=j_password]").val("");
-			var switch_tab=$.cookie("switch_tab")==undefined?1:$.cookie("switch_tab");
-			if(switch_tab==2){
-				switchTab("switch_tfaLogin");
-			}else{
-				$("#div_commonLogin input[name=j_password]").focus();
-			}
-				
+			var switch_tab=$.cookie("switch_tab")==undefined?"normalLogin":$.cookie("switch_tab");
+			$("#"+switch_tab).click();
+			$("#"+switch_tab+"Form input[name=username]").val($.cookie("username")==undefined?"":$.cookie("username"));
+			$("#div_"+switch_tab+" input[name=password]").focus();
 		}else{
-			$("#div_commonLogin input[name=j_username]").focus();
+			$("#div_normalLogin input[name=username]").focus();
 		}
 		<#--resend  captchae-->	
-		$("#tfa_j_otp_captcha_button").on("click",function(){	
+		$("#mobile_j_otp_captcha_button").on("click",function(){	
 			if(captchaCount<60){
 				return;
 			}
-			var loginName=$("#tfa_j_username").val();
+			var loginName=$("#mobile_j_username").val();
 			if(loginName==""){
 				return;
 			}
-			$.get("<@base />/login/otp/"+loginName,function(data,status){
-	    		alert("Data: " + data + "\nStatus: " + status);
+			$.get("<@base />/login/sendsms/"+loginName,function(data,status){
+	    		//alert("Data: " + data + "\nStatus: " + status);
 	  		});
 			
 			<#--todo:send captcha-->
@@ -190,159 +175,37 @@
 					<tr>
 						<td>
 							<ul id="switch_tab" class="switch_tab">
-								<li id="switch_commonLogin" value="div_commonLogin" class="switch_tab_class switch_tab_current"><a href="javascript:void(0);">
-									<@locale code="login.text.login.normal"/></a></li>
-								<li id="switch_tfaLogin"  value="div_tfaLogin"  class="switch_tab_class"><a href="javascript:void(0);">
-									<@locale code="login.text.login.twofactor"/></a></li>
+								<li id="normalLogin" class="switch_tab_class switch_tab_current">
+									<a href="javascript:void(0);">
+										<@locale code="login.text.login.normal"/>
+									</a>
+								</li>
+								<!--
+								<li id="tfaLogin"  class="switch_tab_class">
+									<a href="javascript:void(0);">
+									<@locale code="login.text.login.twofactor"/>
+									</a>
+								</li>-->
+								<!---->
+								<li id="mobileLogin"   class="switch_tab_class">
+									<a href="javascript:void(0);">
+									<@locale code="login.text.login.mobile"/>
+									</a>
+								</li>
 							</ul>
 						</td>
 					</tr>
 					<tr>
 						<td>
-							<div id="div_commonLogin" >
-								<form id="loginForm" name="loginForm" action="<@base />/logon.do" method="post" class="needs-validation" novalidate>
-									<input type="hidden" name="authType" value="basic"/>
-									<table  class="table login_form_table">
-										<tr  class="loginErrorMessage"  <#if ''==loginErrorMessage>style="display:none;"</#if>>
-											<td  colspan="2" style="color:red;">
-												${loginErrorMessage!}
-											</td>
-										</tr>
-										<tr>
-											<td><@locale code="login.text.username"/>:</td>
-											<td>
-											 	<div  class="wrapper">
-		                                        	<i class="fa fa-user"></i>
-													<input required="" class="form-control" type='text' id='j_username'  name='username' value="admin" tabindex="1"/>
-												</div >
-											</td>
-										</tr>
-										<tr>
-											<td><@locale code="login.text.password"/>:</td>
-											<td>
-												<div  class="wrapper">
-		                                        	<i class="fa fa-key fa-2" style="color: #FFD700;"></i>
-													<input required="" class="form-control"  type='password' id='j_password'  name='password' value="maxkey"  tabindex="2"/>
-												</div >
-											</td>
-										</tr>
-										<#if true==isCaptcha> 
-										<tr>
-											<td><@locale code="login.text.captcha"/>:</td>
-											<td>
-												<div  class="wrapper">
-		                                        	<i class="fa fa-lock fa-2"></i>
-													<input required="" class="form-control "  type='text' id="j_captcha" name="captcha"  tabindex="3"  value="" style="float: left;"/><img id="j_captchaimg" class="captcha-image" src="<@base/>/captcha"/>
-												</div >
-											</td>
-											
-										</tr>
-										</#if>
-										<#if true==isRemeberMe>
-										<tr>
-											<td colspan="2">
-												<table  style="width:100%">
-													<tr>
-														<td style="width:50%">
-															<span class="form_checkbox_label">
-																<input type='checkbox' id="remeberMe" name="remeberMe"  class="checkbox"   tabindex="4"  value="remeberMe" />
-																<@locale code="login.text.remeberme"/>
-															</span>
-														</td>
-														<td style="width:50%"><a href="<@base />/forgotpassword/forward"><@locale code="login.text.forgotpassword"/></a></td>
-													</tr>
-												</table>
-											</td>								
-										</tr>
-										</#if>
-										<tr   style="display:none">
-											<td>sessionid:</td>
-											<td><input  class="form-control"  type='text' id="j_sessionid" name="sessionId" value="${sessionid}" /></td>
-											
-										</tr>
-										<tr >
-											<td colspan="2">
-											 <input type="submit" id="loginSubmitButton" style="display: none;" />
-											 <input id="loginSubmit" type="button"  tabindex="5"  style="width: 100%;" class="button btn btn-lg btn-primary btn-block"  value="<@locale code="login.button.login"/>"/></td>
-											
-										</tr>
-									</table>
-									<div class="clear"></div>
-								    </form>
-								</div>
-								<div id="div_tfaLogin" >
-								<form id="tfaLoginForm" name="tfaLoginForm" action="<@base />/logon.do" method="post"  class="needs-validation" novalidate>
-									<input type="hidden" name="authType" value="tfa"/>
-									<table  class="login_form_table">
-										<tr class="loginErrorMessage" <#if ''==loginErrorMessage>style="display:none;"</#if>>
-											<td  colspan="2" style="color:red;">
-												${loginErrorMessage!}
-											</td>
-										</tr>
-										<tr>
-											<td><@locale code="login.text.username"/>:</td>
-											<td><input required="" class="form-control"  type='text' id='tfa_j_username'  name='username' value="" tabindex="1"/></td>
-										</tr>
-										<tr> 
-											<td><@locale code="login.text.password"/>:</td>
-											<td><input required="" class="form-control"  type='password' id='tfa_j_password'  name='password' value=""  tabindex="2" /></td>
-										</tr>
-										<#if true==isMfa >
-										<tr>
-											<td><@locale code="login.text.captcha"/>:</td>
-											<td>
-												<input required="" class="form-control"  type='text' id="tfa_j_otp_captcha" name="otpCaptcha"  tabindex="3"  value=""   style="float: left;"/>
-												<input class="form-control"  id="tfa_j_otp_captcha_button" type="button"  tabindex="5" class="button"  value="获取动态验证码"/>
-												
-											</td>
-										</tr>
-										<#if "TOPT"==otpType >
-										<tr>
-											<td><@locale code="login.text.currenttime"/>:</td>
-											<td>
-												<input  class="form-control"  readonly type='text' id="currentTime" name="currentTime"  tabindex="3"  value="" />
-											</td>
-										</tr>
-										</#if>
-										<tr>
-											<td></td>
-											<td>
-												<div id="currentTime"></div>
-											</td>
-										</tr>
-										</#if>
-										<#if true==isRemeberMe>
-										<tr> 
-											<td colspan="2">
-												<table  style="width:100%">
-													<tr>
-														<td style="width:50%">
-															<span class="form_checkbox_label">
-																<input type='checkbox' id="tfa_remeberMe" name="remeberMe"  class="checkbox"   tabindex="4"  value="remeberMe" />
-																<@locale code="login.text.remeberme"/>
-															</span>
-														</td>
-														<td style="width:50%"><a href="<@base />/forgotpassword/forward"><@locale code="login.text.forgotpassword"/></a></td>
-													</tr>
-												</table>
-											</td>								
-										</tr>
-										</#if>
-										<tr   style="display:none">
-											<td>sessionid:</td>
-											<td><input class="form-control"  type='text' id="tfa_sessionid" name="sessionId" value="${sessionid}" /></td>
-											
-										</tr>
-										<tr >
-											<td colspan="2">
-											 <input type="submit" id="tfaLoginSubmitButton" style="display: none;" />
-											<input id="tfa_loginSubmit" type="button" style="width: 100%;" tabindex="5" class="button btn btn-lg btn-primary btn-block"  value="<@locale code="login.button.login"/>"/></td>
-											
-										</tr>
-									</table>
-									<div class="clear"></div>
-								    </form>
-								</div>
+							<div id="div_normalLogin" >
+								<#include "loginnormal.ftl">
+							</div>
+							<div id="div_tfaLogin" >
+								<#include "logintfa.ftl">
+							</div>
+							<div id="div_mobileLogin" >
+								<#include "loginmobile.ftl">
+							</div>
 						</td>
 					</tr>
 					<tr>

+ 57 - 0
maxkey-web-maxkey/src/main/resources/templates/views/loginmobile.ftl

@@ -0,0 +1,57 @@
+<form id="mobileLoginForm" name="mobileLoginForm" action="<@base />/logon.do" method="post"  class="needs-validation" novalidate>
+	<input type="hidden" name="authType" value="mobile"/>
+	<table  class="login_form_table">
+		<tr class="loginErrorMessage" <#if ''==loginErrorMessage>style="display:none;"</#if>>
+			<td  colspan="2" style="color:red;">
+				${loginErrorMessage!}
+			</td>
+		</tr>
+		<tr>
+			<td><@locale code="login.text.mobile"/>:</td>
+			<td>
+				<div  class="wrapper">
+                	<i class="fa fa-mobile"></i>
+					<input required="" class="form-control"  type='text' id='mobile_j_username'  name='username' value="" tabindex="1"/>
+				</div>
+			</td>
+		</tr>
+		<tr> 
+			<td><@locale code="login.text.smscode"/>:</td>
+			<td>
+				<div  class="wrapper">
+                	<i class="fa fa-lock fa-2"></i>
+					<input required="" class="form-control"  type='password' id='mobile_j_password'  name='password' value=""  tabindex="2"  style="float: left;"/>
+					<input class="form-control"  id="mobile_j_otp_captcha_button" type="button"  tabindex="5" class="button"  value="<@locale code="login.text.login.mobile.obtain"/>"/>
+				</div>
+			</td>
+		</tr>
+		<#if true==isRemeberMe>
+		<tr> 
+			<td colspan="2">
+				<table  style="width:100%">
+					<tr>
+						<td style="width:50%">
+							<span class="form_checkbox_label">
+								<input type='checkbox' id="mobile_remeberMe" name="remeberMe"  class="checkbox"   tabindex="4"  value="remeberMe" />
+								<@locale code="login.text.remeberme"/>
+							</span>
+						</td>
+						<td style="width:50%"><a href="<@base />/forgotpassword/forward"><@locale code="login.text.forgotpassword"/></a></td>
+					</tr>
+				</table>
+			</td>								
+		</tr>
+		</#if>
+		<tr   style="display:none">
+			<td>sessionid:</td>
+			<td><input class="form-control"  type='text' id="mobile_sessionid" name="sessionId" value="${sessionid}" /></td>
+			
+		</tr>
+		<tr >
+			<td colspan="2">
+			 <input type="submit" id="mobileLoginSubmitButton" style="display: none;" />
+			<input  id="mobileLoginSubmit" type="button" style="width: 100%;" tabindex="5" class="doLoginSubmit button btn btn-lg btn-primary btn-block"  value="<@locale code="login.button.login"/>"/></td>
+		</tr>
+	</table>
+	<div class="clear"></div>
+    </form>

+ 69 - 0
maxkey-web-maxkey/src/main/resources/templates/views/loginnormal.ftl

@@ -0,0 +1,69 @@
+<form id="normalLoginForm" name="normalLoginForm" action="<@base />/logon.do" method="post" class="needs-validation" novalidate>
+	<input type="hidden" name="authType" value="normal"/>
+	<table  class="table login_form_table">
+		<tr  class="loginErrorMessage"  <#if ''==loginErrorMessage>style="display:none;"</#if>>
+			<td  colspan="2" style="color:red;">
+				${loginErrorMessage!}
+			</td>
+		</tr>
+		<tr>
+			<td><@locale code="login.text.username"/>:</td>
+			<td>
+			 	<div  class="wrapper">
+                	<i class="fa fa-user"></i>
+					<input required="" class="form-control" type='text' id='j_username'  name='username' value="admin" tabindex="1"/>
+				</div >
+			</td>
+		</tr>
+		<tr>
+			<td><@locale code="login.text.password"/>:</td>
+			<td>
+				<div  class="wrapper">
+                	<i class="fa fa-key fa-2" style="color: #FFD700;"></i>
+					<input required="" class="form-control"  type='password' id='j_password'  name='password' value="maxkey"  tabindex="2"/>
+				</div >
+			</td>
+		</tr>
+		<#if true==isCaptcha> 
+		<tr>
+			<td><@locale code="login.text.captcha"/>:</td>
+			<td>
+				<div  class="wrapper">
+                	<i class="fa fa-lock fa-2"></i>
+					<input required="" class="form-control "  type='text' id="j_captcha" name="captcha"  tabindex="3"  value="" style="float: left;"/><img id="j_captchaimg" class="captcha-image" src="<@base/>/captcha"/>
+				</div >
+			</td>
+			
+		</tr>
+		</#if>
+		<#if true==isRemeberMe>
+		<tr>
+			<td colspan="2">
+				<table  style="width:100%">
+					<tr>
+						<td style="width:50%">
+							<span class="form_checkbox_label">
+								<input type='checkbox' id="remeberMe" name="remeberMe"  class="checkbox"   tabindex="4"  value="remeberMe" />
+								<@locale code="login.text.remeberme"/>
+							</span>
+						</td>
+						<td style="width:50%"><a href="<@base />/forgotpassword/forward"><@locale code="login.text.forgotpassword"/></a></td>
+					</tr>
+				</table>
+			</td>								
+		</tr>
+		</#if>
+		<tr   style="display:none">
+			<td>sessionid:</td>
+			<td><input  class="form-control"  type='text' id="j_sessionid" name="sessionId" value="${sessionid}" /></td>
+			
+		</tr>
+		<tr >
+			<td colspan="2">
+			 <input type="submit" id="normalLoginSubmitButton" style="display: none;" />
+			 <input  id="normalLoginSubmit" type="button"  tabindex="5"  style="width: 100%;" class="doLoginSubmit button btn btn-lg btn-primary btn-block"  value="<@locale code="login.button.login"/>"/></td>
+			
+		</tr>
+	</table>
+	<div class="clear"></div>
+    </form>

+ 83 - 0
maxkey-web-maxkey/src/main/resources/templates/views/logintfa.ftl

@@ -0,0 +1,83 @@
+<form id="tfaLoginForm" name="tfaLoginForm" action="<@base />/logon.do" method="post"  class="needs-validation" novalidate>
+	<input type="hidden" name="authType" value="tfa"/>
+	<table  class="login_form_table">
+		<tr class="loginErrorMessage" <#if ''==loginErrorMessage>style="display:none;"</#if>>
+			<td  colspan="2" style="color:red;">
+				${loginErrorMessage!}
+			</td>
+		</tr>
+		<tr>
+			<td><@locale code="login.text.username"/>:</td>
+			<td>
+				<div  class="wrapper">
+					<i class="fa fa-user"></i>
+					<input required="" class="form-control"  type='text' id='tfa_j_username'  name='username' value="" tabindex="1"/>
+				</div>
+			</td>
+		</tr>
+		<tr> 
+			<td><@locale code="login.text.password"/>:</td>
+			<td>
+				<div  class="wrapper">
+					<i class="fa fa-key fa-2" style="color: #FFD700;"></i>
+					<input required="" class="form-control"  type='password' id='tfa_j_password'  name='password' value=""  tabindex="2" />
+				</div>
+			</td>
+		</tr>
+		<#if true==isMfa >
+		<tr>
+			<td><@locale code="login.text.captcha"/>:</td>
+			<td>
+				<div  class="wrapper">
+					<i class="fa fa-lock fa-2"></i>
+					<input required="" class="form-control"  type='text' id="tfa_j_otp_captcha" name="otpCaptcha"  tabindex="3"  value=""   style="float: left;"/>
+					<input class="form-control"  id="tfa_j_otp_captcha_button" type="button"  tabindex="5" class="button"  value="<@locale code="login.text.login.twofactor.obtain"/>"/>
+				</div>
+			</td>
+		</tr>
+		<#if "TOPT"==otpType >
+		<tr>
+			<td><@locale code="login.text.currenttime"/>:</td>
+			<td>
+				<input  class="form-control"  readonly type='text' id="currentTime" name="currentTime"  tabindex="3"  value="" />
+			</td>
+		</tr>
+		</#if>
+		<tr>
+			<td></td>
+			<td>
+				<div id="currentTime"></div>
+			</td>
+		</tr>
+		</#if>
+		<#if true==isRemeberMe>
+		<tr> 
+			<td colspan="2">
+				<table  style="width:100%">
+					<tr>
+						<td style="width:50%">
+							<span class="form_checkbox_label">
+								<input type='checkbox' id="tfa_remeberMe" name="remeberMe"  class="checkbox"   tabindex="4"  value="remeberMe" />
+								<@locale code="login.text.remeberme"/>
+							</span>
+						</td>
+						<td style="width:50%"><a href="<@base />/forgotpassword/forward"><@locale code="login.text.forgotpassword"/></a></td>
+					</tr>
+				</table>
+			</td>								
+		</tr>
+		</#if>
+		<tr   style="display:none">
+			<td>sessionid:</td>
+			<td><input class="form-control"  type='text' id="tfa_sessionid" name="sessionId" value="${sessionid}" /></td>
+			
+		</tr>
+		<tr >
+			<td colspan="2">
+			 <input type="submit" id="tfaLoginSubmitButton" style="display: none;" />
+			<input   id="tfaLoginSubmit" type="button" style="width: 100%;" tabindex="5" class="doLoginSubmit button btn btn-lg btn-primary btn-block"  value="<@locale code="login.button.login"/>"/></td>
+			
+		</tr>
+	</table>
+	<div class="clear"></div>
+    </form>

Some files were not shown because too many files changed in this diff