From 710299b78fa10d93f682273effae6cb49bcce9b4 Mon Sep 17 00:00:00 2001 From: shimingxy Date: Mon, 13 Apr 2020 01:24:35 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E6=98=93=E4=BA=91=E4=BF=A1=E7=9A=84?= =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crypto/password/opt/AbstractOptAuthn.java | 38 +- .../crypto/password/opt/OneTimePassword.java | 11 +- .../crypto/password/opt/impl/CapOtpAuthn.java | 6 + .../opt/impl/CounterBasedOtpAuthn.java | 4 + .../password/opt/impl/HotpOtpAuthn.java | 4 + .../password/opt/impl/MailOtpAuthn.java | 4 + .../password/opt/impl/MobileOtpAuthn.java | 6 + .../crypto/password/opt/impl/RsaOtpAuthn.java | 6 + .../password/opt/impl/TimeBasedOtpAuthn.java | 1 - .../password/opt/impl/package-info.java | 1 - .../impl/sms/netease/SmsOtpAuthnYunxin.java | 222 +++-- .../opt/token/AbstractOptTokenStore.java | 7 +- .../opt/token/InMemoryOptTokenStore.java | 51 +- .../password/opt/token/JdbcOptTokenStore.java | 13 +- .../opt/token/RedisOptTokenStore.java | 43 +- .../main/java/org/maxkey/util/JsonUtils.java | 183 ++-- .../java/org/maxkey/util/StringUtils.java | 904 +++++++++--------- .../maxkey/web/endpoint/LoginEndpoint.java | 25 + .../config/applicationConfig.properties | 19 +- .../main/resources/spring/maxkey-security.xml | 6 +- .../main/resources/templates/views/login.ftl | 24 +- 21 files changed, 921 insertions(+), 657 deletions(-) delete mode 100644 maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/package-info.java diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/AbstractOptAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/AbstractOptAuthn.java index 45f14083..7ae46a28 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/AbstractOptAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/AbstractOptAuthn.java @@ -1,5 +1,7 @@ package org.maxkey.crypto.password.opt; +import org.maxkey.crypto.password.opt.token.AbstractOptTokenStore; +import org.maxkey.crypto.password.opt.token.InMemoryOptTokenStore; import org.maxkey.domain.UserInfo; import org.maxkey.util.StringGenerator; import org.slf4j.Logger; @@ -13,29 +15,35 @@ import org.slf4j.LoggerFactory; public abstract class AbstractOptAuthn { private static final Logger logger = LoggerFactory.getLogger(AbstractOptAuthn.class); + protected AbstractOptTokenStore optTokenStore = new InMemoryOptTokenStore(); + + //验证码有效間隔 protected int interval = 30; - + + // 验证码长度,范围4~10,默认为6 protected int digits = 6; protected String crypto = "HmacSHA1"; StringGenerator stringGenerator; + + protected String optType = OptTypes.TIMEBASED_OPT; public static final class OptTypes { // 手机 - public static int MOBILE = 2; + public static String MOBILE = "MOBILE"; // 短信 - public static int SMS = 3; + public static String SMS = "SMS"; // 邮箱 - public static int EMAIL = 4; + public static String EMAIL = "EMAIL"; + //TIMEBASED_OPT + public static String TIMEBASED_OPT = "TOPT"; + // HmacOTP + public static String HOTP_OPT = "HOTP"; - public static int TIMEBASED_OPT = 5; - - public static int COUNTERBASED_OPT = 6; - - public static int HOTP_OPT = 7; - - public static int RSA_OPT = 8; + public static String RSA_OPT = "RSA"; + + public static String CAP_OPT = "CAP"; } @@ -109,5 +117,13 @@ public abstract class AbstractOptAuthn { this.crypto = crypto; } + public String getOptType() { + return optType; + } + + public void setOptType(String optType) { + this.optType = optType; + } + } diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/OneTimePassword.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/OneTimePassword.java index d45c5a40..12d6e58a 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/OneTimePassword.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/OneTimePassword.java @@ -1,8 +1,11 @@ package org.maxkey.crypto.password.opt; -public class OneTimePassword { +import java.io.Serializable; + +public class OneTimePassword implements Serializable { + private static final long serialVersionUID = -1637133296702014021L; private String id; - private int type; + private String type; private String token; private String username; private String receiver; @@ -16,11 +19,11 @@ public class OneTimePassword { this.id = id; } - public int getType() { + public String getType() { return type; } - public void setType(int type) { + public void setType(String type) { this.type = type; } diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/CapOtpAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/CapOtpAuthn.java index 6d66b44e..22d8add0 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/CapOtpAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/CapOtpAuthn.java @@ -15,6 +15,12 @@ import org.maxkey.domain.UserInfo; */ public class CapOtpAuthn extends AbstractOptAuthn { + + + public CapOtpAuthn() { + optType = OptTypes.CAP_OPT; + } + @Override public boolean produce(UserInfo userInfo) { // TODO Auto-generated method stub diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/CounterBasedOtpAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/CounterBasedOtpAuthn.java index 9083f492..de7ab0a0 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/CounterBasedOtpAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/CounterBasedOtpAuthn.java @@ -12,6 +12,10 @@ public class CounterBasedOtpAuthn extends AbstractOptAuthn { private static final Logger _logger = LoggerFactory.getLogger(CounterBasedOtpAuthn.class); + public CounterBasedOtpAuthn() { + optType = OptTypes.HOTP_OPT; + } + @Override public boolean produce(UserInfo userInfo) { return true; diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/HotpOtpAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/HotpOtpAuthn.java index f223465a..507f41e8 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/HotpOtpAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/HotpOtpAuthn.java @@ -15,6 +15,10 @@ public class HotpOtpAuthn extends AbstractOptAuthn { boolean addChecksum; int truncation = -1; + public HotpOtpAuthn() { + optType = OptTypes.HOTP_OPT; + } + @Override public boolean produce(UserInfo userInfo) { return true; diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MailOtpAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MailOtpAuthn.java index f146cf29..6f9c2919 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MailOtpAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MailOtpAuthn.java @@ -13,6 +13,10 @@ public class MailOtpAuthn extends AbstractOptAuthn { private static final Logger _logger = LoggerFactory.getLogger(MailOtpAuthn.class); EmailConfig emailConfig; + public MailOtpAuthn() { + optType = OptTypes.EMAIL; + } + @Override public boolean produce(UserInfo userInfo) { try { diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MobileOtpAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MobileOtpAuthn.java index 6356d30c..b665c7ca 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MobileOtpAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MobileOtpAuthn.java @@ -5,6 +5,12 @@ import org.maxkey.domain.UserInfo; public class MobileOtpAuthn extends AbstractOptAuthn { + + + public MobileOtpAuthn() { + optType = OptTypes.SMS; + } + @Override public boolean produce(UserInfo userInfo) { // TODO Auto-generated method stub diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/RsaOtpAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/RsaOtpAuthn.java index 2b63ebf3..e9bb4ba3 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/RsaOtpAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/RsaOtpAuthn.java @@ -15,6 +15,12 @@ import org.maxkey.domain.UserInfo; */ public class RsaOtpAuthn extends AbstractOptAuthn { + + + public RsaOtpAuthn() { + optType = OptTypes.RSA_OPT; + } + @Override public boolean produce(UserInfo userInfo) { // TODO Auto-generated method stub diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/TimeBasedOtpAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/TimeBasedOtpAuthn.java index fdf116ab..afc77cc4 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/TimeBasedOtpAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/TimeBasedOtpAuthn.java @@ -22,7 +22,6 @@ public class TimeBasedOtpAuthn extends AbstractOptAuthn { @Override public boolean produce(UserInfo userInfo) { - // TODO Auto-generated method stub return true; } diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/package-info.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/package-info.java deleted file mode 100644 index 08c08cc0..00000000 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package org.maxkey.crypto.password.opt.impl; \ No newline at end of file diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/sms/netease/SmsOtpAuthnYunxin.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/sms/netease/SmsOtpAuthnYunxin.java index e2a08e79..6a171688 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/sms/netease/SmsOtpAuthnYunxin.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/sms/netease/SmsOtpAuthnYunxin.java @@ -12,90 +12,192 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.maxkey.crypto.password.opt.impl.SmsOtpAuthn; +import org.maxkey.domain.UserInfo; +import org.maxkey.util.JsonUtils; +import org.maxkey.util.StringGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * 网易云信的短信验证. * @author shimingxy * */ - public class SmsOtpAuthnYunxin extends SmsOtpAuthn { + private static final Logger logger = LoggerFactory.getLogger(SmsOtpAuthnYunxin.class); + public SmsOtpAuthnYunxin() { - + optType = OptTypes.SMS; } //发送验证码的请求路径URL private static final String SERVER_URL = "https://api.netease.im/sms/sendcode.action"; //网易云信分配的账号,请替换你在管理后台应用下申请的Appkey - private static final String - APP_KEY = "94395d754eb55693043f5d6a2b772ef3"; + private String + appKey = "94395d754eb55693043f5d6a2b772ef3"; //网易云信分配的密钥,请替换你在管理后台应用下申请的appSecret - private static final String APP_SECRET = "05d5485357bc"; - // 随机数 - private static final String NONCE = "123456"; + private String appSecret = "05d5485357bc"; // 短信模板ID - private static final String TEMPLATEID = "14850150"; - // 手机号 - private static final String MOBILE = "15618726256"; - // 验证码长度,范围4~10,默认为4 - private static final String CODELEN = "6"; + private String templateId = "14860099"; - /** - * . - * @param args String - * @throws Exception e - */ - public static void sendSms(String[] args) throws Exception { + @Override + public boolean produce(UserInfo userInfo) { + HttpPost httpPost = null; + // 手机号 + String mobile = userInfo.getMobile(); + if (mobile != null && !mobile.equals("")) { + try { + httpPost = new HttpPost(SERVER_URL); + String curTime = String.valueOf((new Date()).getTime() / 1000L); + /* + * 参考计算CheckSum的java代码,在上述文档的参数列表中,有CheckSum的计算文档示例 + */ + // 随机数 + String nonce = new StringGenerator( + StringGenerator.DEFAULT_CODE_NUMBER, + 6 + ).randomGenerate(); + String checkSum = SmsOtpAuthnYunxinCheckSumBuilder + .getCheckSum(appSecret, nonce, curTime); - HttpPost httpPost = new HttpPost(SERVER_URL); - String curTime = String.valueOf((new Date()).getTime() / 1000L); - /* - * 参考计算CheckSum的java代码,在上述文档的参数列表中,有CheckSum的计算文档示例 - */ - String checkSum = SmsOtpAuthnYunxinCheckSumBuilder - .getCheckSum(APP_SECRET, NONCE, curTime); - - // 设置请求的header - httpPost.addHeader("AppKey", APP_KEY); - httpPost.addHeader("Nonce", NONCE); - httpPost.addHeader("CurTime", curTime); - httpPost.addHeader("CheckSum", checkSum); - httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); - - // 设置请求的的参数,requestBody参数 - List nvps = new ArrayList(); - /* - * 1.如果是模板短信,请注意参数mobile是有s的,详细参数配置请参考“发送模板短信文档” - * 2.参数格式是jsonArray的格式,例如 "['13888888888','13666666666']" - * 3.params是根据你模板里面有几个参数,那里面的参数也是jsonArray格式 - */ - //https://api.netease.im/sms/sendcode.action - nvps.add(new BasicNameValuePair("templateid", TEMPLATEID)); - nvps.add(new BasicNameValuePair("mobile", MOBILE)); - nvps.add(new BasicNameValuePair("codeLen", CODELEN)); - //authCode 用户自定义验证码 - //nvps.add(new BasicNameValuePair("authCode", "")); - //https://api.netease.im/sms/verifycode.action - //nvps.add(new BasicNameValuePair("code", "123456")); + // 设置请求的header + httpPost.addHeader("AppKey", appKey); + httpPost.addHeader("Nonce", nonce); + httpPost.addHeader("CurTime", curTime); + httpPost.addHeader("CheckSum", checkSum); + httpPost.addHeader("Content-Type", + "application/x-www-form-urlencoded;charset=utf-8"); + // 设置请求的的参数,requestBody参数 + List nvps = new ArrayList(); + /* + * 1.如果是模板短信,请注意参数mobile是有s的,详细参数配置请参考“发送模板短信文档” + * 2.参数格式是jsonArray的格式,例如 "['13888888888','13666666666']" + * 3.params是根据你模板里面有几个参数,那里面的参数也是jsonArray格式 + */ + //https://api.netease.im/sms/sendcode.action + nvps.add(new BasicNameValuePair("templateid", templateId)); + nvps.add(new BasicNameValuePair("mobile", userInfo.getMobile())); + nvps.add(new BasicNameValuePair("codeLen", digits + "")); + //authCode 用户自定义验证码 + //nvps.add(new BasicNameValuePair("authCode", "")); + //https://api.netease.im/sms/verifycode.action + //nvps.add(new BasicNameValuePair("code", "123456")); + + httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8")); + HttpClient httpClient = HttpClientBuilder.create().build(); + // 执行请求 + HttpResponse response = httpClient.execute(httpPost); + /* + * 1.打印执行结果,打印结果一般会200、315、403、404、413、414、500 + * 2.具体的code有问题的可以参考官网的Code状态表 + */ + //{"code":200,"msg":"1","obj":"740673"} + String responseString = EntityUtils.toString(response.getEntity(), "utf-8"); + //String responseString = "{\"code\":200,\"msg\":\"1\",\"obj\":\"740673\"}"; + logger.debug("responseString " + responseString); + YunxinSms yunxinSms = + JsonUtils.gson2Object(responseString,YunxinSms.class); + logger.debug("responseEntity code " + yunxinSms.getObj()); + this.optTokenStore.store( + userInfo, + yunxinSms.getObj(), + userInfo.getMobile(), + OptTypes.SMS); + return true; + } catch (Exception e) { + logger.error(" produce code error ", e); + } finally { + if (httpPost != null) { + httpPost.releaseConnection(); + } + } + } + return false; + } + + @Override + public boolean validate(UserInfo userInfo, String token) { + return this.optTokenStore.validate(userInfo, token, OptTypes.SMS, interval); + } + + + + public String getAppKey() { + return appKey; + } + + public void setAppKey(String appKey) { + this.appKey = appKey; + } + + public String getAppSecret() { + return appSecret; + } + + public void setAppSecret(String appSecret) { + this.appSecret = appSecret; + } + + public String getTemplateId() { + return templateId; + } + + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + + + + public class YunxinSms { + int code; + String msg; + String obj; + public YunxinSms() { + } - httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8")); - HttpClient httpClient = HttpClientBuilder.create().build(); - // 执行请求 - HttpResponse response = httpClient.execute(httpPost); - /* - * 1.打印执行结果,打印结果一般会200、315、403、404、413、414、500 - * 2.具体的code有问题的可以参考官网的Code状态表 - */ - System.out.println(EntityUtils.toString(response.getEntity(), "utf-8")); - //{"code":200,"msg":"1","obj":"740673"} - + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getObj() { + return obj; + } + + public void setObj(String obj) { + this.obj = obj; + } + } + /** + * main. + * @param args String + * @throws Exception throws + */ public static void main(String[] args) throws Exception { - sendSms(null); + String nonce = new StringGenerator( + StringGenerator.DEFAULT_CODE_NUMBER, + 6 + ).randomGenerate(); + System.out.println(nonce); + String mapJson = "{\"code\":200,\"msg\":\"1\",\"obj\":\"740673\"}"; + YunxinSms yunxinSms = JsonUtils.gson2Object(mapJson,YunxinSms.class); + System.out.println("code " + yunxinSms.getObj()); } } diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/AbstractOptTokenStore.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/AbstractOptTokenStore.java index 933e519e..4c380865 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/AbstractOptTokenStore.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/AbstractOptTokenStore.java @@ -1,5 +1,10 @@ package org.maxkey.crypto.password.opt.token; -public class AbstractOptTokenStore { +import org.maxkey.domain.UserInfo; +public abstract class AbstractOptTokenStore { + + public abstract void store(UserInfo userInfo, String token, String receiver, String type); + + public abstract boolean validate(UserInfo userInfo, String token, String type,int interval); } diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/InMemoryOptTokenStore.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/InMemoryOptTokenStore.java index ab131c0d..e8fccfe0 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/InMemoryOptTokenStore.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/InMemoryOptTokenStore.java @@ -1,19 +1,62 @@ package org.maxkey.crypto.password.opt.token; -import java.time.Duration; import org.ehcache.UserManagedCache; import org.ehcache.config.builders.ExpiryPolicyBuilder; import org.ehcache.config.builders.UserManagedCacheBuilder; +import org.joda.time.DateTime; +import org.joda.time.Duration; +import org.joda.time.format.DateTimeFormat; import org.maxkey.constants.ConstantsTimeInterval; import org.maxkey.crypto.password.opt.OneTimePassword; +import org.maxkey.domain.UserInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class InMemoryOptTokenStore { - protected static final UserManagedCache remeberMeStore = +public class InMemoryOptTokenStore extends AbstractOptTokenStore { + private static final Logger logger = LoggerFactory.getLogger(InMemoryOptTokenStore.class); + + protected static final UserManagedCache optTokenStore = UserManagedCacheBuilder.newUserManagedCacheBuilder(String.class, OneTimePassword.class) .withExpiry( ExpiryPolicyBuilder.timeToLiveExpiration( - Duration.ofMinutes(ConstantsTimeInterval.TWO_WEEK) + java.time.Duration.ofMinutes(ConstantsTimeInterval.ONE_MINUTE * 5) ) ) .build(true); + + @Override + public void store(UserInfo userInfo, String token, String receiver, String type) { + DateTime currentDateTime = new DateTime(); + OneTimePassword otp = new OneTimePassword(); + otp.setId(userInfo.getUsername() + "_" + type + "_" + token); + otp.setType(type); + otp.setUsername(userInfo.getUsername()); + otp.setToken(token); + otp.setReceiver(receiver); + otp.setCreateTime(currentDateTime.toString("yyyy-MM-dd HH:mm:ss")); + optTokenStore.put(otp.getId(), otp); + + } + + @Override + public boolean validate(UserInfo userInfo, String token, String type, int interval) { + OneTimePassword otp = optTokenStore.get(userInfo.getUsername() + "_" + type + "_" + token); + if (otp != null) { + DateTime currentdateTime = new DateTime(); + DateTime oneCreateTime = DateTime.parse(otp.getCreateTime(), + DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")); + Duration duration = new Duration(oneCreateTime, currentdateTime); + int intDuration = Integer.parseInt(duration.getStandardSeconds() + ""); + logger.debug("validate duration " + intDuration); + logger.debug("validate result " + (intDuration <= interval)); + if (intDuration <= interval) { + return true; + } + } + return false; + } + + public InMemoryOptTokenStore() { + + } } diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/JdbcOptTokenStore.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/JdbcOptTokenStore.java index df75fcd6..db643300 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/JdbcOptTokenStore.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/JdbcOptTokenStore.java @@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; -public class JdbcOptTokenStore { +public class JdbcOptTokenStore extends AbstractOptTokenStore { private static final Logger logger = LoggerFactory.getLogger(JdbcOptTokenStore.class); private static final String DEFAULT_DEFAULT_INSERT_STATEMENT = @@ -38,7 +38,10 @@ public class JdbcOptTokenStore { } - protected void store(UserInfo userInfo, String token, String receiver, int type) { + /** + *store. + */ + public void store(UserInfo userInfo, String token, String receiver, String type) { jdbcTemplate.update(DEFAULT_DEFAULT_INSERT_STATEMENT, new Object[] { java.util.UUID.randomUUID(), @@ -48,7 +51,7 @@ public class JdbcOptTokenStore { receiver, new Date() }, - new int[] { Types.VARCHAR, Types.INTEGER, + new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,Types.TIMESTAMP } @@ -62,7 +65,7 @@ public class JdbcOptTokenStore { * @param type int * @return */ - public boolean validate(UserInfo userInfo, String token, int type,int interval) { + public boolean validate(UserInfo userInfo, String token, String type,int interval) { OneTimePassword oneTimePassword = jdbcTemplate.queryForObject( DEFAULT_DEFAULT_SELECT_STATEMENT, new OneTimePasswordRowMapper(), userInfo.getUsername(), token, type); @@ -97,7 +100,7 @@ public class JdbcOptTokenStore { public OneTimePassword mapRow(ResultSet rs, int rowNum) throws SQLException { OneTimePassword oneTimePassword = new OneTimePassword(); oneTimePassword.setId(rs.getString("ID")); - oneTimePassword.setType(rs.getInt("OPTTYPE")); + oneTimePassword.setType(rs.getString("OPTTYPE")); oneTimePassword.setUsername(rs.getString("USERNAME")); oneTimePassword.setToken(rs.getString("TOKEN")); oneTimePassword.setUsername(rs.getString("USERNAME")); diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/RedisOptTokenStore.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/RedisOptTokenStore.java index 67942add..56cb4cfb 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/RedisOptTokenStore.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/token/RedisOptTokenStore.java @@ -1,5 +1,46 @@ package org.maxkey.crypto.password.opt.token; -public class RedisOptTokenStore { +import org.joda.time.DateTime; +import org.maxkey.constants.ConstantsTimeInterval; +import org.maxkey.crypto.password.opt.OneTimePassword; +import org.maxkey.domain.UserInfo; +import org.maxkey.persistence.redis.RedisConnection; +import org.maxkey.persistence.redis.RedisConnectionFactory; + +public class RedisOptTokenStore extends AbstractOptTokenStore { + + protected int validitySeconds = ConstantsTimeInterval.ONE_MINUTE * 5; + + RedisConnectionFactory connectionFactory; + + public static String PREFIX = "REDIS_OTP_SERVICE_"; + + @Override + public void store(UserInfo userInfo, String token, String receiver, String type) { + DateTime currentDateTime = new DateTime(); + OneTimePassword otp = new OneTimePassword(); + otp.setId(userInfo.getUsername() + "_" + type + "_" + token); + otp.setType(type); + otp.setUsername(userInfo.getUsername()); + otp.setToken(token); + otp.setReceiver(receiver); + otp.setCreateTime(currentDateTime.toString("yyyy-MM-dd HH:mm:ss")); + RedisConnection conn = connectionFactory.getConnection(); + conn.setexObject(PREFIX + otp.getId(), validitySeconds, otp); + conn.close(); + } + + @Override + public boolean validate(UserInfo userInfo, String token, String type, int interval) { + RedisConnection conn = connectionFactory.getConnection(); + OneTimePassword otp = (OneTimePassword)conn.getObject( + PREFIX + userInfo.getUsername() + "_" + type + "_" + token); + conn.delete(PREFIX + userInfo.getUsername() + "_" + type + "_" + token); + conn.close(); + if (otp != null) { + return true; + } + return false; + } } diff --git a/maxkey-core/src/main/java/org/maxkey/util/JsonUtils.java b/maxkey-core/src/main/java/org/maxkey/util/JsonUtils.java index 18b96fd8..94ff59fe 100644 --- a/maxkey-core/src/main/java/org/maxkey/util/JsonUtils.java +++ b/maxkey-core/src/main/java/org/maxkey/util/JsonUtils.java @@ -1,110 +1,101 @@ package org.maxkey.util; -import java.io.IOException; - import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; +import java.io.IOException; public class JsonUtils { - /** - * Transform json string to java bean object - * @param json - * @param bean - * @return Object - */ - public static Object json2Object(String json,Object bean){ - try { - bean=(new ObjectMapper()).readValue(json, bean.getClass()); - } catch (JsonParseException e) { - e.printStackTrace(); - } catch (JsonMappingException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - }finally{ - - } - return bean; - } - - /** - * Transform json string to java bean object - * @param json - * @param Class - * @return Object - */ - public static T json2Object(String json,Class cls){ - T bean =null; - try { - bean=(new ObjectMapper()).readValue(json, cls); - } catch (JsonParseException e) { - e.printStackTrace(); - } catch (JsonMappingException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - }finally{ - - } - return bean; - } - - /** - * Transform java bean object to json string - * @param bean - * @return string - */ - public static String object2Json(Object bean){ - String json=""; - try { - json= (new ObjectMapper()).writeValueAsString(bean); - } catch (JsonGenerationException e) { - e.printStackTrace(); - } catch (JsonMappingException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - }finally{ - - } - return json; - } - + /** + * Transform json string to java bean object. + * + * @param json String + * @param bean Object + * @return Object + */ + public static Object json2Object(String json, Object bean) { + try { + bean = (new ObjectMapper()).readValue(json, bean.getClass()); + } catch (JsonParseException e) { + e.printStackTrace(); + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return bean; + } - - - /** - * Transform json string to java bean object use Gson - * @param - * @param json - * @param Class - * @return Object - */ + /** + * Transform json string to java bean object. + * + * @param json String + * @param cls Class + * @return Object + */ + public static T json2Object(String json, Class cls) { + T bean = null; + try { + bean = (new ObjectMapper()).readValue(json, cls); + } catch (JsonParseException e) { + e.printStackTrace(); + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return bean; + } - public static T gson2Object(String json,Class cls){ - T newBean = (new Gson()).fromJson(json, cls); - return newBean; - } - - - /** - * Transform java bean object to json string use Gson - * @param bean - * @return string - */ - public static String gson2Json(Object bean){ - String json=""; - // convert java object to JSON format, - // and returned as JSON formatted string - json = (new Gson()).toJson(bean); - - return json; - } - + /** + * Transform java bean object to json string. + * + * @param bean Object + * @return string + */ + public static String object2Json(Object bean) { + String json = ""; + try { + json = (new ObjectMapper()).writeValueAsString(bean); + } catch (JsonGenerationException e) { + e.printStackTrace(); + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return json; + } + + /** + * Transform json string to java bean object use Gson. + * + * @param Class + * @param json String + * @return Object + */ + + public static T gson2Object(String json, Class cls) { + T newBean = (new Gson()).fromJson(json, cls); + return newBean; + } + + /** + * Transform java bean object to json string use Gson. + * + * @param bean Object + * @return string + */ + public static String gson2Json(Object bean) { + String json = ""; + // convert java object to JSON format, + // and returned as JSON formatted string + json = (new Gson()).toJson(bean); + + return json; + } - } diff --git a/maxkey-core/src/main/java/org/maxkey/util/StringUtils.java b/maxkey-core/src/main/java/org/maxkey/util/StringUtils.java index b9d5a1d4..bc9cabd5 100644 --- a/maxkey-core/src/main/java/org/maxkey/util/StringUtils.java +++ b/maxkey-core/src/main/java/org/maxkey/util/StringUtils.java @@ -10,464 +10,456 @@ import java.util.Map; import java.util.regex.Pattern; public final class StringUtils { - - /** - * avoid null, and return value trim. - * @param value string value. - * @return the trim of value. - */ - public static String avoidNull(String value) { - return (value == null) ? "" : value.trim(); - } - - public static boolean isNull(String value) { - return value == null; - } - /** - * @param value string - * value - * @return value - */ - public static boolean isNullOrBlank(String value) { - return value == null || "".equals(value.trim()); - } + /** + * avoid null, and return value trim. + * + * @param value string value. + * @return the trim of value. + */ + public static String avoidNull(String value) { + return (value == null) ? "" : value.trim(); + } - public static boolean isNotEmpty(String value) { - return !isNullOrBlank(value); - } - - public static boolean isNotNullAndEquals(String value,String equalString) { - return !isNullOrBlank(value)&&value.equals(equalString); - } - - public static boolean isNotNullAndEqualsIgnoreCase(String value,String equalString) { - return !isNullOrBlank(value)&&value.equalsIgnoreCase(equalString); - } - - /* - * 获取指定UTF-8模式字节长度的字符串 - */ - public static String limitLength(String strValue, int bytelen){ - - //中文汉字占用三个字节 - int strlen = bytelen/3; - int real_bytelen = strlen*3; - - //如果为NULL或�?空,则直接返�? - if(null==strValue || "".equalsIgnoreCase(strValue)){ - return ""; - } - - try{ - byte[] utf8_bytes = strValue.getBytes("UTF-8"); - if(utf8_bytes.length<=bytelen) return strValue; - - byte[] cutoff_bytes = new byte[real_bytelen]; - System.arraycopy(utf8_bytes, 0, cutoff_bytes, 0, real_bytelen); - - String strResult = new String(cutoff_bytes, "UTF-8"); - - return strResult; - - }catch(Exception e){ - if(strValue.length() string2List(String string, String split) { - String[] strs = {}; - if (string != null && !string.equals("")) { - strs = string.split(split); - } - ArrayList resultList = new ArrayList(0); - for (int i = 0; i < strs.length; i++) { - if (strs[i] != null&& !strs[i].equals("")) { - resultList.add(strs[i]); - } - } - resultList.trimToSize(); - return resultList; - } + /** + * @param value string value + * @return value + */ + public static boolean isNullOrBlank(String value) { + return value == null || "".equals(value.trim()); + } - public static String list2String(List list, String split) { - String string = ""; - if (list == null) - return string; - for (int i = 0; i < list.size(); i++) { - if (list.get(i) != null && !list.get(i).equals("")) { - string += list.get(i) + split; - } - } - if (string.length() > 0) { - string = string.substring(0, string.length() - 1); - } - return string; - } - - - public static int parse2Integer(String string) { - Integer value = 0; - try { - value = Integer.parseInt(string); - } catch(Exception e){ - throw new RuntimeException("parse "+string+" to Integer error."); - } - return value; - } - - - /** - * 处理如id=name形式的字符串 - * @param proValue - * @param key - * @param value - * @return - */ - public static Map> processStr(String proValue,String key,String value) { - Map> map = new HashMap>(); - List idList = new ArrayList(); - List nameList = new ArrayList(); - if(StringUtils.isNotEmpty(proValue)) { - List list = StringUtils.string2List(proValue, ","); - for(String str : list) { - idList.add(str.split("\\,")[0]); - nameList.add(str.split("\\,")[1]); - } - } - map.put(key, idList); - map.put(value, nameList); - return map; - } - - /** - * 获得集合对象中某�?��性字段的值,中间用split隔开 - * @param list 集合对象 - * @param propName 属�?名称 - * @param split 分隔�? - * @return - */ - @SuppressWarnings("rawtypes") - public static String convertPropVal(List list,String propName,String split) { - String retVal = ""; - for(Object obj : list) { - if(StringUtils.isNotEmpty(retVal)) { - retVal += split; - } - retVal += BeanUtil.getValue(obj, propName); - } - return retVal; - } - - /** - * 将对象类型的集合转换为String类型的集�? - * @param list - * @param propName - * @return - */ - @SuppressWarnings("rawtypes") - public static List convertList(List list, String propName) { - List strList = new ArrayList(); - for(Object obj : list) { - strList.add(BeanUtil.getValue(obj, propName)); - } - return strList; - } - - /** - * 将对象类型的集合转换为String类型的Map集合 - * @param list - * @param propNames - * @return - */ - @SuppressWarnings("rawtypes") - public static Map> convertListToMap(List list, String ... propNames) { - Map> map = new HashMap>(); - for(String name : propNames) { - List strList = new ArrayList(); - for(Object obj : list) { - strList.add(BeanUtil.getValue(obj, name)); - } - map.put(name, strList); - } - return map; - } - - public static String convertWeekStr(String str,String split) { - String retVal = ""; - try { - if(StringUtils.isNotEmpty(str)) { - String []array = str.split("\\"+split); - for(int i = 0; i < array.length; i++) { - if(StringUtils.isNotEmpty(retVal)) { - retVal += split; - } - retVal += (Integer.parseInt(array[i]) + 1); - } - } - } catch(NumberFormatException e) { - e.printStackTrace(); - } - return retVal; - } - - - public static List convertToSingleList(List list){ - return convertToSingleList(list,","); - } - - public static List convertToSingleList(List list,String reg){ - List result = new ArrayList(); - if(list!=null && list.size()>0){ - for(String str:list){ - String[] arrStr = str.split(reg); - for(int i=0;i 128) { - try { - if(first){ - pinyin += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0].charAt(0); - }else{ - pinyin += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0]; - } - } catch (BadHanyuPinyinOutputFormatCombination e) { - e.printStackTrace(); - } - }else{ - pinyin += nameChar[i]; - } - } - return pinyin; - } - */ - public static Map aduserName2Map(String aduserName) { - if(isNullOrBlank(aduserName)) { - return null; - } - Map map = new HashMap(); - int index = 0; - if((index = aduserName.indexOf("\\")) > 0) { - map.put("domain", aduserName.substring(0,index)); - map.put("userName", aduserName.substring(index + 1, aduserName.length())); - } else if((index = aduserName.indexOf("@")) > 0) { - map.put("userName", aduserName.substring(0, index)); - map.put("domain", aduserName.substring(index + 1)); - } else { - map.put("userName", aduserName); - } - return map; - } - - - /** - * 处理AD域中的用户名,将域名去掉 - * @param str - * @return - */ - public static String takeoffDomain(String aduserName) { - Map map = aduserName2Map(aduserName); - if(BeanUtil.isNotNull(map)) { - return map.get("userName"); - } - return null; - } - - public static String getAdDomin(String aduserName) { - Map map = aduserName2Map(aduserName); - if(BeanUtil.isNotNull(map)) { - return map.get("domain"); - } - return null; - } + public static boolean isNotEmpty(String value) { + return !isNullOrBlank(value); + } + + public static boolean isNotNullAndEquals(String value, String equalString) { + return !isNullOrBlank(value) && value.equals(equalString); + } + + public static boolean isNotNullAndEqualsIgnoreCase(String value, String equalString) { + return !isNullOrBlank(value) && value.equalsIgnoreCase(equalString); + } + + /* + * 获取指定UTF-8模式字节长度的字符串 + */ + public static String limitLength(String strValue, int bytelen) { + + // 中文汉字占用三个字节 + int strlen = bytelen / 3; + int real_bytelen = strlen * 3; + + // 如果为NULL或�?空,则直接返�? + if (null == strValue || "".equalsIgnoreCase(strValue)) { + return ""; + } + + try { + byte[] utf8_bytes = strValue.getBytes("UTF-8"); + if (utf8_bytes.length <= bytelen) + return strValue; + + byte[] cutoff_bytes = new byte[real_bytelen]; + System.arraycopy(utf8_bytes, 0, cutoff_bytes, 0, real_bytelen); + + String strResult = new String(cutoff_bytes, "UTF-8"); + + return strResult; + + } catch (Exception e) { + if (strValue.length() < strlen) + return strValue; + return strValue.substring(0, strlen); + } + + } + + /** + * 对url进行编码�? + * + * @param ori_url 要编码的url + * @return 返回url + */ + public static String urlEncode(String url) { + try { + String tempstr = URLEncoder.encode(url, "UTF-8"); + return tempstr.replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return url; + } + } + + /** + * 编码url + * + * @param ori_url + * @return + */ + public static String urlDecode(String url) { + try { + + String tempstr = URLDecoder.decode(url.replaceAll("%20", "\\+"), "UTF-8"); + return tempstr; + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return url; + } + } + + /** + * �?��字符串是否包含特殊字�? + * + * @param str + * @return + */ + public static Boolean specialWord(String string) { + String regEx = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#�?…�?&*()—�?+|{}【�?‘;:�?“�?。,、?]"; + return Pattern.compile(regEx).matcher(string).find(); + } + + /** + * @param str + * @return + */ + public static Boolean startWithUpper(String string) { + return Pattern.compile("^[A-Z]").matcher(string).find(); + } + + /** + * @param str + * @return + */ + public static Boolean startWithLower(String string) { + return Pattern.compile("^[a-z]").matcher(string).find(); + } + + /** + * @param str + * @return + */ + public static Boolean startWithNumber(String string) { + return Pattern.compile("^[0-9]").matcher(string).find(); + } + + /** + * @param str + * @return + */ + public static Boolean containsLower(String string) { + return Pattern.compile("[a-z]").matcher(string).find(); + } + + /** + * @param str + * @return + */ + public static Boolean containsUpper(String string) { + return Pattern.compile("[A-Z]").matcher(string).find(); + } + + /** + * @param str + * @return + */ + public static Boolean containsChinese(String string) { + String regEx = "[\u2E80-\u9FFF]+$"; + return Pattern.compile(regEx).matcher(string).find(); + } + + /** + * 密码不包含全部或部分的用户账户名 �?��str2中是否包含str中全部或部分的数�? + * + * @param str + * @param str2 + * @return + */ + public static Boolean containsPartOrAll(String string, String string2) { + if (isNotEmpty(string) && isNotEmpty(string2)) { + return Pattern.compile("[" + string + "]").matcher(string2).find(); + } + return false; + } + + public static boolean containsSpace(String string) { + return string.lastIndexOf(" ") != -1; + } + + public static Boolean isNumber(String string) { + return Pattern.compile("[0-9]").matcher(string).find(); + } + + /** + * 返回字符串中包含的大写字母的 + * + * @param str + * @return + */ + public static int countUpper(String string) { + int count = 0; + for (int i = 0; i < string.toCharArray().length; i++) { + if (containsUpper(String.valueOf(string.charAt(i)))) { + count++; + } + } + return count; + } + + /** + * @param str + * @return + */ + public static int countLower(String string) { + int count = 0; + for (int i = 0; i < string.toCharArray().length; i++) { + if (containsLower(String.valueOf(string.charAt(i)))) { + count++; + } + } + return count; + } + + public static int countNumber(String string) { + int count = 0; + for (int i = 0; i < string.toCharArray().length; i++) { + if (isNumber(String.valueOf(string.charAt(i)))) { + count++; + } + } + return count; + } + + /** + * @param str + * @return + */ + public static int countSpecialWord(String string) { + int count = 0; + for (int i = 0; i < string.toCharArray().length; i++) { + if (specialWord(String.valueOf(string.charAt(i)))) { + count++; + } + } + return count; + } + + public static List string2List(String string, String split) { + String[] strs = {}; + if (string != null && !string.equals("")) { + strs = string.split(split); + } + ArrayList resultList = new ArrayList(0); + for (int i = 0; i < strs.length; i++) { + if (strs[i] != null && !strs[i].equals("")) { + resultList.add(strs[i]); + } + } + resultList.trimToSize(); + return resultList; + } + + public static String list2String(List list, String split) { + String string = ""; + if (list == null) + return string; + for (int i = 0; i < list.size(); i++) { + if (list.get(i) != null && !list.get(i).equals("")) { + string += list.get(i) + split; + } + } + if (string.length() > 0) { + string = string.substring(0, string.length() - 1); + } + return string; + } + + public static int parse2Integer(String string) { + Integer value = 0; + try { + value = Integer.parseInt(string); + } catch (Exception e) { + throw new RuntimeException("parse " + string + " to Integer error."); + } + return value; + } + + /** + * 处理如id=name形式的字符串 + * + * @param proValue + * @param key + * @param value + * @return + */ + public static Map> processStr(String proValue, String key, String value) { + Map> map = new HashMap>(); + List idList = new ArrayList(); + List nameList = new ArrayList(); + if (StringUtils.isNotEmpty(proValue)) { + List list = StringUtils.string2List(proValue, ","); + for (String str : list) { + idList.add(str.split("\\,")[0]); + nameList.add(str.split("\\,")[1]); + } + } + map.put(key, idList); + map.put(value, nameList); + return map; + } + + /** + * 获得集合对象中某�?��性字段的值,中间用split隔开 + * + * @param list 集合对象 + * @param propName 属�?名称 + * @param split 分隔�? + * @return + */ + @SuppressWarnings("rawtypes") + public static String convertPropVal(List list, String propName, String split) { + String retVal = ""; + for (Object obj : list) { + if (StringUtils.isNotEmpty(retVal)) { + retVal += split; + } + retVal += BeanUtil.getValue(obj, propName); + } + return retVal; + } + + /** + * 将对象类型的集合转换为String类型的集�? + * + * @param list + * @param propName + * @return + */ + @SuppressWarnings("rawtypes") + public static List convertList(List list, String propName) { + List strList = new ArrayList(); + for (Object obj : list) { + strList.add(BeanUtil.getValue(obj, propName)); + } + return strList; + } + + /** + * 将对象类型的集合转换为String类型的Map集合 + * + * @param list + * @param propNames + * @return + */ + @SuppressWarnings("rawtypes") + public static Map> convertListToMap(List list, String... propNames) { + Map> map = new HashMap>(); + for (String name : propNames) { + List strList = new ArrayList(); + for (Object obj : list) { + strList.add(BeanUtil.getValue(obj, name)); + } + map.put(name, strList); + } + return map; + } + + public static String convertWeekStr(String str, String split) { + String retVal = ""; + try { + if (StringUtils.isNotEmpty(str)) { + String[] array = str.split("\\" + split); + for (int i = 0; i < array.length; i++) { + if (StringUtils.isNotEmpty(retVal)) { + retVal += split; + } + retVal += (Integer.parseInt(array[i]) + 1); + } + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + return retVal; + } + + public static List convertToSingleList(List list) { + return convertToSingleList(list, ","); + } + + public static List convertToSingleList(List list, String reg) { + List result = new ArrayList(); + if (list != null && list.size() > 0) { + for (String str : list) { + String[] arrStr = str.split(reg); + for (int i = 0; i < arrStr.length; i++) { + result.add(arrStr[i]); + } + } + } + return result; + } + + /** + * 汉字转换位汉语拼音 + * + * @param hanYu Chinese + * @param first true is Convert first,else all + * @return 拼音 + */ + /* + * public static String hanYu2Pinyin(String hanYu,boolean first){ String pinyin + * = ""; char[] nameChar = hanYu.toCharArray(); HanyuPinyinOutputFormat + * defaultFormat = new HanyuPinyinOutputFormat(); + * defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE); + * defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); for (int i = 0; + * i < nameChar.length; i++) { if (nameChar[i] > 128) { try { if(first){ pinyin + * += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], + * defaultFormat)[0].charAt(0); }else{ pinyin += + * PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0]; } } + * catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } + * }else{ pinyin += nameChar[i]; } } return pinyin; } + */ + public static Map aduserName2Map(String aduserName) { + if (isNullOrBlank(aduserName)) { + return null; + } + Map map = new HashMap(); + int index = 0; + if ((index = aduserName.indexOf("\\")) > 0) { + map.put("domain", aduserName.substring(0, index)); + map.put("userName", aduserName.substring(index + 1, aduserName.length())); + } else if ((index = aduserName.indexOf("@")) > 0) { + map.put("userName", aduserName.substring(0, index)); + map.put("domain", aduserName.substring(index + 1)); + } else { + map.put("userName", aduserName); + } + return map; + } + + /** + * 处理AD域中的用户名,将域名去掉 + * + * @param str + * @return + */ + public static String takeoffDomain(String aduserName) { + Map map = aduserName2Map(aduserName); + if (BeanUtil.isNotNull(map)) { + return map.get("userName"); + } + return null; + } + + public static String getAdDomin(String aduserName) { + Map map = aduserName2Map(aduserName); + if (BeanUtil.isNotNull(map)) { + return map.get("domain"); + } + return null; + } } diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/web/endpoint/LoginEndpoint.java b/maxkey-web-maxkey/src/main/java/org/maxkey/web/endpoint/LoginEndpoint.java index a3c97a94..b2923a34 100644 --- a/maxkey-web-maxkey/src/main/java/org/maxkey/web/endpoint/LoginEndpoint.java +++ b/maxkey-web-maxkey/src/main/java/org/maxkey/web/endpoint/LoginEndpoint.java @@ -12,6 +12,7 @@ import org.maxkey.authn.support.rememberme.AbstractRemeberMeService; import org.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService; import org.maxkey.authn.support.wsfederation.WsFederationConstants; import org.maxkey.config.ApplicationConfig; +import org.maxkey.crypto.password.opt.AbstractOptAuthn; import org.maxkey.dao.service.UserInfoService; import org.maxkey.domain.UserInfo; import org.maxkey.util.StringUtils; @@ -69,6 +70,11 @@ public class LoginEndpoint { @Autowired @Qualifier("authenticationProvider") RealmAuthenticationProvider authenticationProvider ; + + @Autowired + @Qualifier("tfaOptAuthn") + protected AbstractOptAuthn tfaOptAuthn; + /* @Autowired @Qualifier("jwtLoginService") @@ -124,6 +130,11 @@ public class LoginEndpoint { modelAndView.addObject("isRemeberMe", applicationConfig.getLoginConfig().isRemeberMe()); modelAndView.addObject("isKerberos", applicationConfig.getLoginConfig().isKerberos()); modelAndView.addObject("isOneTimePwd", applicationConfig.getLoginConfig().isOneTimePwd()); + if(applicationConfig.getLoginConfig().isOneTimePwd()) { + modelAndView.addObject("optType", tfaOptAuthn.getOptType()); + modelAndView.addObject("optInterval", tfaOptAuthn.getInterval()); + } + if( applicationConfig.getLoginConfig().isKerberos()){ modelAndView.addObject("userDomainUrlJson", kerberosService.buildKerberosProxys()); @@ -183,4 +194,18 @@ public class LoginEndpoint { return authnType; } + + @RequestMapping("/login/otp/{username}") + @ResponseBody + public String produceOtp(@PathVariable("username") String username) { + UserInfo userInfo = new UserInfo(); + userInfo.setUsername(username); + UserInfo queryUserInfo=userInfoService.loadByUsername(username);//(userInfo); + if(queryUserInfo!=null) { + tfaOptAuthn.produce(queryUserInfo); + return "ok"; + } + + return "fail"; + } } \ No newline at end of file diff --git a/maxkey-web-maxkey/src/main/resources/config/applicationConfig.properties b/maxkey-web-maxkey/src/main/resources/config/applicationConfig.properties index ad00fdc1..dc844e65 100644 --- a/maxkey-web-maxkey/src/main/resources/config/applicationConfig.properties +++ b/maxkey-web-maxkey/src/main/resources/config/applicationConfig.properties @@ -59,7 +59,14 @@ config.characterencoding.charset.to=UTF-8 config.app.issuer=CN=ConSec,CN=COM,CN=SH ############################################################################ - +#IP +config.redis.hostname=127.0.0.1 +#port +config.redis.port=6379 +#password +config.redis.password=password +# +config.redis.timeout=10000 # config.redis.pool.maxtotal=1000 # @@ -68,16 +75,6 @@ config.redis.pool.maxidle=200 config.redis.pool.maxwaitmillis=1000 # config.redis.pool.testonborrow=true - -#IP -config.redis.hostname=127.0.0.1 -#port -config.redis.port=6379 -#password -config.redis.password=password - -config.redis.timeout=10000 - ############################################################################ # Login configuration #enable captcha diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-security.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-security.xml index c0529a43..73708137 100644 --- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-security.xml +++ b/maxkey-web-maxkey/src/main/resources/spring/maxkey-security.xml @@ -93,7 +93,11 @@ - + + diff --git a/maxkey-web-maxkey/src/main/resources/templates/views/login.ftl b/maxkey-web-maxkey/src/main/resources/templates/views/login.ftl index acf36ad7..10b764f4 100644 --- a/maxkey-web-maxkey/src/main/resources/templates/views/login.ftl +++ b/maxkey-web-maxkey/src/main/resources/templates/views/login.ftl @@ -54,6 +54,7 @@ function formatTime(){ strTime+=(seconds<10?"0"+seconds:seconds); } +<#if true==isOneTimePwd && "TOPT"==optType> function currentTime(){ seconds++; if(seconds>59){ @@ -74,17 +75,18 @@ function currentTime(){ $("#currentTime").val(strTime); } + <#--timeBase Token Interval default is 30s--> var timeBaseCount; function getTimeBaseCount(){ - if(seconds<30){ - timeBaseCount=30-seconds; + if(seconds<${optInterval}){ + timeBaseCount=${optInterval}-seconds; }else{ - timeBaseCount=30-(seconds-30); + timeBaseCount=${optInterval}-(seconds-${optInterval}); } $("#tfa_j_otp_captcha_button").val("<@locale code="login.text.login.twofactor.validTime"/>("+timeBaseCount+")<@locale code="login.text.login.twofactor.validTime.unit"/>"); }; - + var currentSwitchTab="div_commonLogin"; <#--submit form--> function doLoginSubmit(){ @@ -117,7 +119,9 @@ document.onkeydown=function(event){ }; $(function(){ + <#if true==isOneTimePwd && "TOPT"==optType> setInterval("currentTime()", 1000); + <#--on captcha image click ,new a captcha code--> <#if true==isCaptcha> $('#j_captchaimg').click(function () {// @@ -153,6 +157,14 @@ $(function(){ if(captchaCount<60){ return; } + var loginName=$("#tfa_j_username").val(); + if(loginName==""){ + return; + } + $.get("<@base />/login/otp/"+loginName,function(data,status){ + alert("Data: " + data + "\nStatus: " + status); + }); + <#--todo:send captcha--> captchaCountTimer=setInterval("getCaptchaCount()", 1000); }); @@ -245,13 +257,15 @@ $(function(){ <@locale code="login.text.password"/>: - <#if true==isOneTimePwd> + <#if true==isOneTimePwd > + <#if "TOPT"==optType > <@locale code="login.text.currenttime"/>: + <@locale code="login.text.captcha"/>: