mobile login

This commit is contained in:
MaxKey 2021-05-17 11:34:39 +08:00
parent ff3d97d51f
commit b378f9fa2f
27 changed files with 447 additions and 309 deletions

View File

@ -47,12 +47,19 @@ public abstract class AbstractAuthenticationProvider {
private static final Logger _logger = private static final Logger _logger =
LoggerFactory.getLogger(AbstractAuthenticationProvider.class); 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 ApplicationConfig applicationConfig;
protected AbstractAuthenticationRealm authenticationRealm; protected AbstractAuthenticationRealm authenticationRealm;
protected AbstractOtpAuthn tfaOtpAuthn; protected AbstractOtpAuthn tfaOtpAuthn;
protected AbstractOtpAuthn smsOtpAuthn;
protected AbstractRemeberMeService remeberMeService; protected AbstractRemeberMeService remeberMeService;
protected OnlineTicketServices onlineTicketServices; protected OnlineTicketServices onlineTicketServices;
@ -176,8 +183,10 @@ public abstract class AbstractAuthenticationProvider {
protected void authTypeValid(String authType) { protected void authTypeValid(String authType) {
_logger.debug("Login AuthN Type " + authType); _logger.debug("Login AuthN Type " + authType);
if (authType != null && ( if (authType != null && (
authType.equalsIgnoreCase("basic") authType.equalsIgnoreCase(AuthType.NORMAL)
|| authType.equalsIgnoreCase("tfa")) || authType.equalsIgnoreCase(AuthType.TFA)
|| authType.equalsIgnoreCase(AuthType.MOBILE)
)
) { ) {
return; return;
} }
@ -195,7 +204,8 @@ public abstract class AbstractAuthenticationProvider {
*/ */
protected void captchaValid(String captcha, String authType) { protected void captchaValid(String captcha, String authType) {
// for basic // for basic
if (applicationConfig.getLoginConfig().isCaptcha() && authType.equalsIgnoreCase("basic")) { if (applicationConfig.getLoginConfig().isCaptcha()
&& authType.equalsIgnoreCase(AuthType.NORMAL)) {
_logger.info("captcha : " _logger.info("captcha : "
+ WebContext.getSession().getAttribute( + WebContext.getSession().getAttribute(
WebConstants.KAPTCHA_SESSION_KEY).toString()); WebConstants.KAPTCHA_SESSION_KEY).toString());
@ -218,7 +228,8 @@ public abstract class AbstractAuthenticationProvider {
*/ */
protected void tftcaptchaValid(String otpCaptcha, String authType, UserInfo userInfo) { protected void tftcaptchaValid(String otpCaptcha, String authType, UserInfo userInfo) {
// for one time password 2 factor // 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(); UserInfo validUserInfo = new UserInfo();
validUserInfo.setUsername(userInfo.getUsername()); validUserInfo.setUsername(userInfo.getUsername());
validUserInfo.setSharedSecret(userInfo.getSharedSecret()); validUserInfo.setSharedSecret(userInfo.getSharedSecret());
@ -232,6 +243,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 * login user by j_username and j_cname first query user by j_cname if first
* step userinfo is null,query user from system. * step userinfo is null,query user from system.
@ -329,6 +362,7 @@ public abstract class AbstractAuthenticationProvider {
this.onlineTicketServices = onlineTicketServices; this.onlineTicketServices = onlineTicketServices;
} }
public void setSmsOtpAuthn(AbstractOtpAuthn smsOtpAuthn) {
this.smsOtpAuthn = smsOtpAuthn;
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@ import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.maxkey.constants.ConstantsProperties; import org.maxkey.constants.ConstantsProperties;
@ -82,19 +83,30 @@ public class ApplicationAutoConfiguration implements InitializingBean {
new ClassPathResource(ConstantsProperties.classPathResource( new ClassPathResource(ConstantsProperties.classPathResource(
ConstantsProperties.applicationPropertySource)); ConstantsProperties.applicationPropertySource));
PropertySourcesPlaceholderConfigurer configurer = PropertySourcesPlaceholderConfigurer configurer =
new PropertySourcesPlaceholderConfigurer(); new PropertySourcesPlaceholderConfigurer();
configurer.setLocations(classPathApplicationPropertySource); configurer.setLocations(classPathApplicationPropertySource);
/*configurer.setLocations(
classPathResource1,
classPathResource2
);*/
configurer.setIgnoreUnresolvablePlaceholders(true); configurer.setIgnoreUnresolvablePlaceholders(true);
_logger.debug("PropertySourcesPlaceholderConfigurer init"); _logger.debug("PropertySourcesPlaceholderConfigurer init");
return configurer; 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") @Bean(name = "passwordReciprocal")
public PasswordReciprocal passwordReciprocal() { public PasswordReciprocal passwordReciprocal() {
return new PasswordReciprocal(); return new PasswordReciprocal();

View File

@ -67,7 +67,7 @@ public class LoginService {
/** /**
* 1 (USERNAME) 2 (USERNAME | MOBILE) 3 (USERNAME | MOBILE | EMAIL) * 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(){ public LoginService(){

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@ -19,6 +19,8 @@ package org.maxkey.web.endpoint;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.regex.Pattern;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -79,6 +81,13 @@ public class LoginEndpoint {
@Qualifier("tfaOtpAuthn") @Qualifier("tfaOtpAuthn")
protected AbstractOtpAuthn 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 * init login
* @return * @return
@ -154,14 +163,12 @@ public class LoginEndpoint {
return authnType; return authnType;
} }
@RequestMapping("/login/otp/{username}") @RequestMapping("/login/sendsms/{mobile}")
@ResponseBody @ResponseBody
public String produceOtp(@PathVariable("username") String username) { public String produceOtp(@PathVariable("mobile") String mobile) {
UserInfo userInfo = new UserInfo(); UserInfo queryUserInfo=userInfoService.queryUserInfoByEmailMobile(mobile);
userInfo.setUsername(username);
UserInfo queryUserInfo=userInfoService.loadByUsername(username);//(userInfo);
if(queryUserInfo!=null) { if(queryUserInfo!=null) {
tfaOtpAuthn.produce(queryUserInfo); smsOtpAuthn.produce(queryUserInfo);
return "ok"; return "ok";
} }

View File

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

View File

@ -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.obtain.valid.unit=\u79d2
login.text.login.twofactor.validTime=\u5269\u4f59\u65f6\u95f4 login.text.login.twofactor.validTime=\u5269\u4f59\u65f6\u95f4
login.text.login.twofactor.validTime.unit=\u79d2 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.twofactor=\u5b89\u5168\u8ba4\u8bc1
login.text.login.normal=\u57fa\u672c\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.username=\u7528\u6237\u540d
login.text.mobile=\u624B\u673A\u53F7\u7801
login.text.password=\u5bc6&nbsp;&nbsp;&nbsp;&nbsp;\u7801 login.text.password=\u5bc6&nbsp;&nbsp;&nbsp;&nbsp;\u7801
login.text.captcha=\u9a8c\u8bc1\u7801 login.text.captcha=\u9a8c\u8bc1\u7801
login.text.smscode=\u77ED\u4FE1\u9a8c\u8bc1\u7801
login.text.remeberme=\u8bb0\u4f4f\u767b\u5f55 login.text.remeberme=\u8bb0\u4f4f\u767b\u5f55
login.text.forgotpassword=\u5fd8\u8bb0\u5bc6\u7801 login.text.forgotpassword=\u5fd8\u8bb0\u5bc6\u7801
login.button.login=\u767b\u5f55 login.button.login=\u767b\u5f55

View File

@ -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.obtain.valid.unit=seconds
login.text.login.twofactor.validTime=Remaining login.text.login.twofactor.validTime=Remaining
login.text.login.twofactor.validTime.unit=seconds 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.twofactor=Two-Factors
login.text.login.normal=Normal Login login.text.login.normal=Normal Login
login.text.login.mobile=Mobile Login
login.text.username=Username login.text.username=Username
login.text.mobile=Phone Number
login.text.password=Password login.text.password=Password
login.text.captcha=CAPTCHA login.text.captcha=CAPTCHA
login.text.smscode=Message Code
login.text.remeberme=RemeberMe login.text.remeberme=RemeberMe
login.text.forgotpassword=Forgot Password login.text.forgotpassword=Forgot Password
login.button.login=Login login.button.login=Login

View File

@ -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.obtain.valid.unit=\u79d2
login.text.login.twofactor.validTime=\u5269\u4f59\u65f6\u95f4 login.text.login.twofactor.validTime=\u5269\u4f59\u65f6\u95f4
login.text.login.twofactor.validTime.unit=\u79d2 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.twofactor=\u5b89\u5168\u8ba4\u8bc1
login.text.login.normal=\u57fa\u672c\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.username=\u7528\u6237\u540d
login.text.mobile=\u624B\u673A\u53F7\u7801
login.text.password=\u5bc6&nbsp;&nbsp;&nbsp;&nbsp;\u7801 login.text.password=\u5bc6&nbsp;&nbsp;&nbsp;&nbsp;\u7801
login.text.captcha=\u9a8c\u8bc1\u7801 login.text.captcha=\u9a8c\u8bc1\u7801
login.text.smscode=\u77ED\u4FE1\u9a8c\u8bc1\u7801
login.text.remeberme=\u8bb0\u4f4f\u767b\u5f55 login.text.remeberme=\u8bb0\u4f4f\u767b\u5f55
login.text.forgotpassword=\u5fd8\u8bb0\u5bc6\u7801 login.text.forgotpassword=\u5fd8\u8bb0\u5bc6\u7801
login.button.login=\u767b\u5f55 login.button.login=\u767b\u5f55

View File

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

View File

@ -39,12 +39,12 @@ $(function(){
$(".switch_tab .switch_tab_class").removeClass("switch_tab_current"); $(".switch_tab .switch_tab_class").removeClass("switch_tab_current");
$(this).addClass("switch_tab_current"); $(this).addClass("switch_tab_current");
$(".switch_tab li").each(function(){ $(".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"){ if (typeof(switchTab) == "function"){
switchTab($(this).attr("value"));//user define after switch Tab switchTab($(this).attr("id"));//user define after switch Tab
} }
}); });
//document forward //document forward

File diff suppressed because one or more lines are too long

View File

@ -31,12 +31,12 @@
var captchaCountTimer; var captchaCountTimer;
var captchaCount=60; var captchaCount=60;
function getCaptchaCount(){ 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--; captchaCount--;
if(captchaCount==0){ 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; captchaCount=60;
clearInterval(captchaCountTimer); 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"/>"); $("#tfa_j_otp_captcha_button").val("<@locale code="login.text.login.twofactor.validTime"/>("+timeBaseCount+")<@locale code="login.text.login.twofactor.validTime.unit"/>");
}; };
</#if> </#if>
var currentSwitchTab="div_commonLogin"; var currentSwitchTab="normalLogin";
<#--submit form--> <#--submit form-->
function doLoginSubmit(){ function doLoginSubmit(){
if(currentSwitchTab=="div_commonLogin"){ $.cookie("username", $("#"+currentSwitchTab+"Form input[name=username]").val(), { expires: 7 });
$.cookie("username", $("#loginForm input[name=j_username]").val(), { expires: 7 }); $("#"+currentSwitchTab+"SubmitButton").click();
$.cookie("switch_form", 1, { expires: 7 }); $.cookie("switch_tab", currentSwitchTab, { expires: 7 });
$("#loginSubmitButton").click();
}else{
$.cookie("username", $("#tfaLoginForm input[name=j_username]").val(), { expires: 7 });
$.cookie("switch_form", 2, { expires: 7 });
$("#tfaLoginSubmitButton").click();
}
}; };
<#--switch LoginForm && tfaLoginForm--> <#--switch Login Form-->
function switchTab(id){ function switchTab(id){
if($("#"+id+" input[name=j_username]").val()==""){ if($("#"+id+"Form input[name=username]").val()==""){
$("#"+id+" input[name=j_username]").focus(); $("#"+id+"Form input[name=username]").focus();
}else{ }else{
$("#"+id+" input[name=j_password]").focus(); $("#"+id+"Form input[name=password]").focus();
} }
currentSwitchTab=id; currentSwitchTab=id;
} }
@ -130,39 +124,30 @@
</#if> </#if>
<#--submit loginForme--> <#--submit loginForme-->
$("#loginSubmit").on("click",function(){ $(".doLoginSubmit").on("click",function(){
doLoginSubmit();
});
<#--submit tfaLoginForme-->
$("#tfa_loginSubmit").on("click",function(){
doLoginSubmit(); doLoginSubmit();
}); });
<#--read username cookie for login e--> <#--read username cookie for login e-->
if($.cookie("username")!=undefined&&$.cookie("username")!=""){ if($.cookie("username")!=undefined&&$.cookie("username")!=""){
$("input[name=j_username]").val($.cookie("username")==undefined?"":$.cookie("username")); var switch_tab=$.cookie("switch_tab")==undefined?"normalLogin":$.cookie("switch_tab");
$("input[name=j_password]").val(""); $("#"+switch_tab).click();
var switch_tab=$.cookie("switch_tab")==undefined?1:$.cookie("switch_tab"); $("#"+switch_tab+"Form input[name=username]").val($.cookie("username")==undefined?"":$.cookie("username"));
if(switch_tab==2){ $("#div_"+switch_tab+" input[name=password]").focus();
switchTab("switch_tfaLogin");
}else{ }else{
$("#div_commonLogin input[name=j_password]").focus(); $("#div_normalLogin input[name=username]").focus();
}
}else{
$("#div_commonLogin input[name=j_username]").focus();
} }
<#--resend captchae--> <#--resend captchae-->
$("#tfa_j_otp_captcha_button").on("click",function(){ $("#mobile_j_otp_captcha_button").on("click",function(){
if(captchaCount<60){ if(captchaCount<60){
return; return;
} }
var loginName=$("#tfa_j_username").val(); var loginName=$("#mobile_j_username").val();
if(loginName==""){ if(loginName==""){
return; return;
} }
$.get("<@base />/login/otp/"+loginName,function(data,status){ $.get("<@base />/login/sendsms/"+loginName,function(data,status){
alert("Data: " + data + "\nStatus: " + status); //alert("Data: " + data + "\nStatus: " + status);
}); });
<#--todo:send captcha--> <#--todo:send captcha-->
@ -190,158 +175,36 @@
<tr> <tr>
<td> <td>
<ul id="switch_tab" class="switch_tab"> <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);"> <li id="normalLogin" class="switch_tab_class switch_tab_current">
<@locale code="login.text.login.normal"/></a></li> <a href="javascript:void(0);">
<li id="switch_tfaLogin" value="div_tfaLogin" class="switch_tab_class"><a href="javascript:void(0);"> <@locale code="login.text.login.normal"/>
<@locale code="login.text.login.twofactor"/></a></li> </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> </ul>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<div id="div_commonLogin" > <div id="div_normalLogin" >
<form id="loginForm" name="loginForm" action="<@base />/logon.do" method="post" class="needs-validation" novalidate> <#include "loginnormal.ftl">
<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>
<div id="div_tfaLogin" > <div id="div_tfaLogin" >
<form id="tfaLoginForm" name="tfaLoginForm" action="<@base />/logon.do" method="post" class="needs-validation" novalidate> <#include "logintfa.ftl">
<input type="hidden" name="authType" value="tfa"/> </div>
<table class="login_form_table"> <div id="div_mobileLogin" >
<tr class="loginErrorMessage" <#if ''==loginErrorMessage>style="display:none;"</#if>> <#include "loginmobile.ftl">
<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>
</td> </td>
</tr> </tr>

View File

@ -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>

View File

@ -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>

View File

@ -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>