split maxkey-authentication-sms
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.maxkey.autoconfigure;
|
||||
|
||||
import org.maxkey.constants.ConstsPersistence;
|
||||
import org.maxkey.password.onetimepwd.token.RedisOtpTokenStore;
|
||||
import org.maxkey.password.sms.SmsOtpAuthnService;
|
||||
import org.maxkey.persistence.redis.RedisConnectionFactory;
|
||||
import org.maxkey.persistence.service.EmailSendersService;
|
||||
import org.maxkey.persistence.service.SmsProviderService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
|
||||
@AutoConfiguration
|
||||
public class SmsAutoConfiguration implements InitializingBean {
|
||||
private static final Logger _logger =
|
||||
LoggerFactory.getLogger(SmsAutoConfiguration.class);
|
||||
|
||||
|
||||
@Bean(name = "smsOtpAuthnService")
|
||||
public SmsOtpAuthnService smsOtpAuthnService(
|
||||
@Value("${maxkey.server.persistence}") int persistence,
|
||||
SmsProviderService smsProviderService,
|
||||
EmailSendersService emailSendersService,
|
||||
RedisConnectionFactory redisConnFactory) {
|
||||
SmsOtpAuthnService smsOtpAuthnService =
|
||||
new SmsOtpAuthnService(smsProviderService,emailSendersService);
|
||||
|
||||
if (persistence == ConstsPersistence.REDIS) {
|
||||
RedisOtpTokenStore redisOptTokenStore = new RedisOtpTokenStore(redisConnFactory);
|
||||
smsOtpAuthnService.setRedisOptTokenStore(redisOptTokenStore);
|
||||
}
|
||||
|
||||
_logger.debug("SmsOtpAuthnService {} inited." ,
|
||||
persistence == ConstsPersistence.REDIS ? "Redis" : "InMemory");
|
||||
return smsOtpAuthnService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.maxkey.password.sms;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.maxkey.entity.UserInfo;
|
||||
import org.maxkey.password.onetimepwd.AbstractOtpAuthn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
public class SmsOtpAuthn extends AbstractOtpAuthn {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SmsOtpAuthn.class);
|
||||
|
||||
protected StandardEnvironment properties;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean produce(UserInfo userInfo) {
|
||||
String token = this.genToken(userInfo);
|
||||
// You must add send sms code here
|
||||
logger.debug("send sms code" + token);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(UserInfo userInfo, String token) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setProperties(StandardEnvironment properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
protected void loadProperties() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
public void initPropertys() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.maxkey.password.sms;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.maxkey.configuration.EmailConfig;
|
||||
import org.maxkey.constants.ConstsBoolean;
|
||||
import org.maxkey.crypto.password.PasswordReciprocal;
|
||||
import org.maxkey.entity.EmailSenders;
|
||||
import org.maxkey.entity.SmsProvider;
|
||||
import org.maxkey.password.onetimepwd.AbstractOtpAuthn;
|
||||
import org.maxkey.password.onetimepwd.impl.MailOtpAuthn;
|
||||
import org.maxkey.password.onetimepwd.token.RedisOtpTokenStore;
|
||||
import org.maxkey.password.sms.impl.SmsOtpAuthnAliyun;
|
||||
import org.maxkey.password.sms.impl.SmsOtpAuthnTencentCloud;
|
||||
import org.maxkey.password.sms.impl.SmsOtpAuthnYunxin;
|
||||
import org.maxkey.persistence.service.EmailSendersService;
|
||||
import org.maxkey.persistence.service.SmsProviderService;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
||||
public class SmsOtpAuthnService {
|
||||
|
||||
protected static final Cache<String, AbstractOtpAuthn> smsAuthnStore =
|
||||
Caffeine.newBuilder()
|
||||
.expireAfterWrite(60, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
SmsProviderService smsProviderService;
|
||||
|
||||
EmailSendersService emailSendersService;
|
||||
|
||||
RedisOtpTokenStore redisOptTokenStore;
|
||||
|
||||
public SmsOtpAuthnService(SmsProviderService smsProviderService, EmailSendersService emailSendersService) {
|
||||
this.smsProviderService = smsProviderService;
|
||||
this.emailSendersService = emailSendersService;
|
||||
}
|
||||
|
||||
public SmsOtpAuthnService(SmsProviderService smsProviderService,EmailSendersService emailSendersService,RedisOtpTokenStore redisOptTokenStore) {
|
||||
this.smsProviderService = smsProviderService;
|
||||
this.emailSendersService = emailSendersService;
|
||||
this.redisOptTokenStore = redisOptTokenStore;
|
||||
}
|
||||
|
||||
public AbstractOtpAuthn getByInstId(String instId) {
|
||||
AbstractOtpAuthn otpAuthn = smsAuthnStore.getIfPresent(instId);
|
||||
if(otpAuthn == null) {
|
||||
SmsProvider smsProvider =
|
||||
smsProviderService.findOne("where instid = ? ", new Object[]{instId}, new int[]{Types.VARCHAR});
|
||||
if(smsProvider != null ) {
|
||||
|
||||
if(smsProvider.getProvider().equalsIgnoreCase("aliyun")) {
|
||||
SmsOtpAuthnAliyun aliyun = new SmsOtpAuthnAliyun(
|
||||
smsProvider.getAppKey(),
|
||||
smsProvider.getAppSecret(),
|
||||
smsProvider.getTemplateId(),
|
||||
smsProvider.getSignName()
|
||||
);
|
||||
if(redisOptTokenStore != null) {
|
||||
aliyun.setOptTokenStore(redisOptTokenStore);
|
||||
}
|
||||
otpAuthn = aliyun;
|
||||
}else if(smsProvider.getProvider().equalsIgnoreCase("tencentcloud")) {
|
||||
SmsOtpAuthnTencentCloud tencentCloud = new SmsOtpAuthnTencentCloud(
|
||||
smsProvider.getAppKey(),
|
||||
smsProvider.getAppSecret(),
|
||||
smsProvider.getSmsSdkAppId(),
|
||||
smsProvider.getTemplateId(),
|
||||
smsProvider.getSignName()
|
||||
);
|
||||
if(redisOptTokenStore != null) {
|
||||
tencentCloud.setOptTokenStore(redisOptTokenStore);
|
||||
}
|
||||
otpAuthn = tencentCloud;
|
||||
}else if(smsProvider.getProvider().equalsIgnoreCase("neteasesms")) {
|
||||
SmsOtpAuthnYunxin yunxin = new SmsOtpAuthnYunxin(
|
||||
smsProvider.getAppKey(),
|
||||
smsProvider.getAppSecret(),
|
||||
smsProvider.getTemplateId()
|
||||
);
|
||||
if(redisOptTokenStore != null) {
|
||||
yunxin.setOptTokenStore(redisOptTokenStore);
|
||||
}
|
||||
otpAuthn = yunxin;
|
||||
}else if(smsProvider.getProvider().equalsIgnoreCase("email")) {
|
||||
EmailSenders emailSender =
|
||||
emailSendersService.findOne("where instid = ? ", new Object[]{instId}, new int[]{Types.VARCHAR});
|
||||
|
||||
String credentials = PasswordReciprocal.getInstance().decoder(emailSender.getCredentials());
|
||||
EmailConfig emailConfig =
|
||||
new EmailConfig(
|
||||
emailSender.getAccount(),
|
||||
credentials,
|
||||
emailSender.getSmtpHost(),
|
||||
emailSender.getPort(),
|
||||
ConstsBoolean.isTrue(emailSender.getSslSwitch()),
|
||||
emailSender.getSender());
|
||||
MailOtpAuthn mailOtpAuthn = new MailOtpAuthn(emailConfig);
|
||||
if(redisOptTokenStore != null) {
|
||||
mailOtpAuthn.setOptTokenStore(redisOptTokenStore);
|
||||
}
|
||||
otpAuthn = mailOtpAuthn;
|
||||
}
|
||||
|
||||
smsAuthnStore.put(instId, otpAuthn);
|
||||
}
|
||||
}
|
||||
return otpAuthn;
|
||||
}
|
||||
|
||||
public void setRedisOptTokenStore(RedisOtpTokenStore redisOptTokenStore) {
|
||||
this.redisOptTokenStore = redisOptTokenStore;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.maxkey.password.sms.impl;
|
||||
|
||||
import com.aliyuncs.CommonRequest;
|
||||
import com.aliyuncs.CommonResponse;
|
||||
import com.aliyuncs.DefaultAcsClient;
|
||||
import com.aliyuncs.IAcsClient;
|
||||
import com.aliyuncs.http.MethodType;
|
||||
import com.aliyuncs.profile.DefaultProfile;
|
||||
|
||||
import org.maxkey.entity.UserInfo;
|
||||
import org.maxkey.password.sms.SmsOtpAuthn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 阿里云短信验证.
|
||||
* @author shimingxy
|
||||
*
|
||||
*/
|
||||
public class SmsOtpAuthnAliyun extends SmsOtpAuthn {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SmsOtpAuthnAliyun.class);
|
||||
|
||||
public SmsOtpAuthnAliyun() {
|
||||
otpType = OtpTypes.SMS;
|
||||
}
|
||||
|
||||
public SmsOtpAuthnAliyun(String accessKeyId, String accessSecret, String templateCode, String signName) {
|
||||
otpType = OtpTypes.SMS;
|
||||
this.accessKeyId = accessKeyId;
|
||||
this.accessSecret = accessSecret;
|
||||
this.templateCode = templateCode;
|
||||
this.signName = signName;
|
||||
}
|
||||
|
||||
//请替换你在管理后台应用下申请的accessKeyId
|
||||
private String accessKeyId = "94395d754eb55693043f5d6a2b772ef3";
|
||||
//请替换你在管理后台应用下申请的accessSecret
|
||||
private String accessSecret = "05d5485357bc";
|
||||
// 短信模板ID
|
||||
private String templateCode = "SMS_187590021";
|
||||
|
||||
private String signName = "MaxKey";
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean produce(UserInfo userInfo) {
|
||||
// 手机号
|
||||
String mobile = userInfo.getMobile();
|
||||
if (mobile != null && !mobile.equals("")) {
|
||||
try {
|
||||
DefaultProfile profile = DefaultProfile.getProfile(
|
||||
"cn-hangzhou", accessKeyId, accessSecret);
|
||||
IAcsClient client = new DefaultAcsClient(profile);
|
||||
|
||||
String token = this.genToken(userInfo);
|
||||
CommonRequest request = new CommonRequest();
|
||||
request.setSysMethod(MethodType.POST);
|
||||
request.setSysDomain("dysmsapi.aliyuncs.com");
|
||||
request.setSysVersion("2017-05-25");
|
||||
request.setSysAction("SendSms");
|
||||
request.putQueryParameter("RegionId", "cn-hangzhou");
|
||||
request.putQueryParameter("PhoneNumbers", mobile);
|
||||
request.putQueryParameter("SignName", signName);
|
||||
request.putQueryParameter("TemplateCode", templateCode);
|
||||
request.putQueryParameter("TemplateParam", "{\"code\":\"" + token + "\"}");
|
||||
CommonResponse response = client.getCommonResponse(request);
|
||||
logger.debug("responseString " + response.getData());
|
||||
//成功返回
|
||||
if (response.getData().indexOf("OK") > -1) {
|
||||
this.optTokenStore.store(
|
||||
userInfo,
|
||||
token,
|
||||
userInfo.getMobile(),
|
||||
OtpTypes.SMS);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(" produce code error ", e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(UserInfo userInfo, String token) {
|
||||
return this.optTokenStore.validate(userInfo, token, OtpTypes.SMS, interval);
|
||||
}
|
||||
|
||||
public String getAccessKeyId() {
|
||||
return accessKeyId;
|
||||
}
|
||||
|
||||
public void setAccessKeyId(String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
}
|
||||
|
||||
public String getAccessSecret() {
|
||||
return accessSecret;
|
||||
}
|
||||
|
||||
public void setAccessSecret(String accessSecret) {
|
||||
this.accessSecret = accessSecret;
|
||||
}
|
||||
|
||||
public String getTemplateCode() {
|
||||
return templateCode;
|
||||
}
|
||||
|
||||
public void setTemplateCode(String templateCode) {
|
||||
this.templateCode = templateCode;
|
||||
}
|
||||
|
||||
public String getSignName() {
|
||||
return signName;
|
||||
}
|
||||
|
||||
public void setSignName(String signName) {
|
||||
this.signName = signName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.maxkey.password.sms.impl;
|
||||
|
||||
import com.tencentcloudapi.common.Credential;
|
||||
import com.tencentcloudapi.common.profile.ClientProfile;
|
||||
import com.tencentcloudapi.common.profile.HttpProfile;
|
||||
import com.tencentcloudapi.sms.v20190711.SmsClient;
|
||||
import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
|
||||
import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
|
||||
|
||||
import org.maxkey.entity.UserInfo;
|
||||
import org.maxkey.password.sms.SmsOtpAuthn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* 腾讯云短信验证.
|
||||
* @author shimingxy
|
||||
*
|
||||
*/
|
||||
public class SmsOtpAuthnTencentCloud extends SmsOtpAuthn {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SmsOtpAuthnTencentCloud.class);
|
||||
|
||||
//
|
||||
String secretId;
|
||||
//
|
||||
String secretKey;
|
||||
//短信SDKAPPID
|
||||
String smsSdkAppid;
|
||||
//短信模板
|
||||
String templateId;
|
||||
//签名
|
||||
String sign;
|
||||
|
||||
public SmsOtpAuthnTencentCloud() {
|
||||
otpType = OtpTypes.SMS;
|
||||
}
|
||||
|
||||
public SmsOtpAuthnTencentCloud(String secretId, String secretKey, String smsSdkAppid, String templateId,
|
||||
String sign) {
|
||||
otpType = OtpTypes.SMS;
|
||||
this.secretId = secretId;
|
||||
this.secretKey = secretKey;
|
||||
this.smsSdkAppid = smsSdkAppid;
|
||||
this.templateId = templateId;
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean produce(UserInfo userInfo) {
|
||||
// 手机号
|
||||
String mobile = userInfo.getMobile();
|
||||
if (mobile != null && !mobile.equals("")) {
|
||||
try {
|
||||
Credential cred = new Credential(secretId, secretKey);
|
||||
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.setEndpoint("sms.tencentcloudapi.com");
|
||||
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
|
||||
SmsClient client = new SmsClient(cred, "ap-beijing", clientProfile);
|
||||
String token = this.genToken(userInfo);
|
||||
String params = "{\"PhoneNumberSet\":[\"" + mobile + "\"],"
|
||||
+ "\"TemplateID\":\"" + templateId + "\",\"Sign\":\"" + sign + "\","
|
||||
+ "\"TemplateParamSet\":[\"" + token + "\",\"" + this.interval + "\"],"
|
||||
+ "\"SmsSdkAppid\":\"" + smsSdkAppid + "\"}";
|
||||
|
||||
SendSmsRequest req = SendSmsRequest.fromJsonString(params, SendSmsRequest.class);
|
||||
|
||||
SendSmsResponse resp = client.SendSms(req);
|
||||
|
||||
logger.debug("responseString " + SendSmsRequest.toJsonString(resp));
|
||||
if (resp.getSendStatusSet()[0].getCode().equalsIgnoreCase("Ok")) {
|
||||
this.optTokenStore.store(
|
||||
userInfo,
|
||||
token,
|
||||
userInfo.getMobile(),
|
||||
OtpTypes.SMS);
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error(" produce code error ", e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(UserInfo userInfo, String token) {
|
||||
return this.optTokenStore.validate(userInfo, token, OtpTypes.SMS, interval);
|
||||
}
|
||||
|
||||
public String getSecretId() {
|
||||
return secretId;
|
||||
}
|
||||
|
||||
public void setSecretId(String secretId) {
|
||||
this.secretId = secretId;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public String getSmsSdkAppid() {
|
||||
return smsSdkAppid;
|
||||
}
|
||||
|
||||
public void setSmsSdkAppid(String smsSdkAppid) {
|
||||
this.smsSdkAppid = smsSdkAppid;
|
||||
}
|
||||
|
||||
public String getTemplateId() {
|
||||
return templateId;
|
||||
}
|
||||
|
||||
public void setTemplateId(String templateId) {
|
||||
this.templateId = templateId;
|
||||
}
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.maxkey.password.sms.impl;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.maxkey.entity.UserInfo;
|
||||
import org.maxkey.password.sms.SmsOtpAuthn;
|
||||
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() {
|
||||
otpType = OtpTypes.SMS;
|
||||
}
|
||||
|
||||
public SmsOtpAuthnYunxin(String appKey, String appSecret, String templateId) {
|
||||
otpType = OtpTypes.SMS;
|
||||
this.appKey = appKey;
|
||||
this.appSecret = appSecret;
|
||||
this.templateId = templateId;
|
||||
}
|
||||
|
||||
//发送验证码的请求路径URL
|
||||
private static final String
|
||||
SERVER_URL = "https://api.netease.im/sms/sendcode.action";
|
||||
//网易云信分配的账号,请替换你在管理后台应用下申请的Appkey
|
||||
private String
|
||||
appKey = "94395d754eb55693043f5d6a2b772ef3";
|
||||
//网易云信分配的密钥,请替换你在管理后台应用下申请的appSecret
|
||||
private String appSecret = "05d5485357bc";
|
||||
// 短信模板ID
|
||||
private String templateId = "14860099";
|
||||
|
||||
|
||||
@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);
|
||||
logger.debug("AppKey " +appKey+" ,Nonce "+nonce+", CurTime "+curTime+" ,checkSum "+checkSum);
|
||||
// 设置请求的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<NameValuePair> nvps = new ArrayList<NameValuePair>();
|
||||
/*
|
||||
* 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.gsonStringToObject(responseString,YunxinSms.class);
|
||||
logger.debug("responseEntity code " + yunxinSms.getObj());
|
||||
nonce = yunxinSms.getObj() == null ?nonce:yunxinSms.getObj();
|
||||
logger.debug("nonce " + nonce);
|
||||
this.optTokenStore.store(
|
||||
userInfo,
|
||||
nonce,
|
||||
userInfo.getMobile(),
|
||||
OtpTypes.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, OtpTypes.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() {
|
||||
}
|
||||
|
||||
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 {
|
||||
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.gsonStringToObject(mapJson,YunxinSms.class);
|
||||
System.out.println("code " + yunxinSms.getObj());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SmsOtpAuthnYunxinCheckSumBuilder {
|
||||
// 计算并获取CheckSum
|
||||
public static String getCheckSum(String appSecret, String nonce, String curTime) {
|
||||
return encode("sha1", appSecret + nonce + curTime);
|
||||
}
|
||||
|
||||
// 计算并获取md5值
|
||||
public static String getMD5(String requestBody) {
|
||||
return encode("md5", requestBody);
|
||||
}
|
||||
|
||||
private static String encode(String algorithm, String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
MessageDigest messageDigest
|
||||
= MessageDigest.getInstance(algorithm);
|
||||
messageDigest.update(value.getBytes());
|
||||
return getFormattedText(messageDigest.digest());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
private static String getFormattedText(byte[] bytes) {
|
||||
int len = bytes.length;
|
||||
StringBuilder buf = new StringBuilder(len * 2);
|
||||
for (int j = 0; j < len; j++) {
|
||||
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
|
||||
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
}
|
||||
Reference in New Issue
Block a user