Commit fb1e7076 authored by ma's avatar ma

新增找回密码功能,以及一些密码限制和频率限制

parent 8c7af349
...@@ -48,6 +48,13 @@ public class UserController { ...@@ -48,6 +48,13 @@ public class UserController {
private UserService userService; private UserService userService;
@Value("${rsa.private_key}") @Value("${rsa.private_key}")
private String PRIVATE_KRY; private String PRIVATE_KRY;
@Value("${other.error_count.change_pwd}")
private Integer changePwdMaxErrCount;
@Value("${spring.mail.to}")
private String account;
private static final String OPERATION_CHANGE = "change";
@InitBinder @InitBinder
public void initBinder(WebDataBinder binder) { public void initBinder(WebDataBinder binder) {
...@@ -127,6 +134,15 @@ public class UserController { ...@@ -127,6 +134,15 @@ public class UserController {
@PostMapping("update_pwd") @PostMapping("update_pwd")
@MyLog(title = "修改密码", businessType = BusinessType.UPDATE) @MyLog(title = "修改密码", businessType = BusinessType.UPDATE)
public BaseResult updatePwd(@RequestBody UserUpdatePwdVo userUpdatePwdVo) { public BaseResult updatePwd(@RequestBody UserUpdatePwdVo userUpdatePwdVo) {
String errCntTimeMap = UserUtils.getErrCntTimeMap(account + OPERATION_CHANGE);
if (!StringUtils.isEmpty(errCntTimeMap)) {
if (errCntTimeMap.equals(CommonUtil.getCurDateStr())) {
return BaseResult.failed().setMsgValue("今日尝试修改密码次数已达" + changePwdMaxErrCount + "次,请明日再试");
} else {
UserUtils.removeErrCntTimeMap(account + OPERATION_CHANGE);
UserUtils.removeErrCnt(account + OPERATION_CHANGE);
}
}
String oldPassWord = userUpdatePwdVo.getOldPassWord(); String oldPassWord = userUpdatePwdVo.getOldPassWord();
String newPassWord = userUpdatePwdVo.getNewPassWord(); String newPassWord = userUpdatePwdVo.getNewPassWord();
String userId = UserUtils.getLoginUserId(); String userId = UserUtils.getLoginUserId();
...@@ -138,7 +154,27 @@ public class UserController { ...@@ -138,7 +154,27 @@ public class UserController {
} }
oldPassWord = RSAUtil.getDecryptString(oldPassWord, PRIVATE_KRY); oldPassWord = RSAUtil.getDecryptString(oldPassWord, PRIVATE_KRY);
newPassWord = RSAUtil.getDecryptString(newPassWord, PRIVATE_KRY); newPassWord = RSAUtil.getDecryptString(newPassWord, PRIVATE_KRY);
Integer errCnt = UserUtils.getErrCnt(account + OPERATION_CHANGE);
if (errCnt == null) {
errCnt = 0;
}
if (oldPassWord.equals(user.getPassword())) { if (oldPassWord.equals(user.getPassword())) {
if (newPassWord.length() < 8) {
computeChangePwdErrCnt(errCnt);
return BaseResult.failed().setMsgValue("密码不得小于8位");
}
if (CommonUtil.verifyPasswordContainAccount(newPassWord, user.getUserName())) {
computeChangePwdErrCnt(errCnt);
return BaseResult.failed().setMsgValue("密码中不得包含用户名的完整字符串、大小写变位或形似变换的字符串");
}
if (CommonUtil.isKeyBoardContinuousChar(newPassWord)) {
computeChangePwdErrCnt(errCnt);
return BaseResult.failed().setMsgValue("密码不得包含键盘连续字符4个及以上");
}
if (!CommonUtil.checkPassword(newPassWord)) {
computeChangePwdErrCnt(errCnt);
return BaseResult.failed().setMsgValue("至少由大写字母、小写字母、数字与特殊符号等4类中3类混合");
}
user.setPassword(newPassWord); user.setPassword(newPassWord);
boolean b = userService.updateUser(user); boolean b = userService.updateUser(user);
if (b) { if (b) {
...@@ -147,10 +183,19 @@ public class UserController { ...@@ -147,10 +183,19 @@ public class UserController {
return BaseResult.failed().setMsgValue("密码修改失败"); return BaseResult.failed().setMsgValue("密码修改失败");
} }
} else { } else {
computeChangePwdErrCnt(errCnt);
return BaseResult.failed().setMsgValue("原密码出错"); return BaseResult.failed().setMsgValue("原密码出错");
} }
} }
private void computeChangePwdErrCnt(int errCnt) {
if (errCnt < changePwdMaxErrCount - 1) {
UserUtils.setErrCnt(account + OPERATION_CHANGE, errCnt + 1);
} else {
UserUtils.setErrCntTimeMap(account + OPERATION_CHANGE, CommonUtil.getCurDateStr());
}
}
/** /**
* 分页查询所有的user * 分页查询所有的user
...@@ -184,7 +229,7 @@ public class UserController { ...@@ -184,7 +229,7 @@ public class UserController {
List<UserVo> result = records.getResult(); List<UserVo> result = records.getResult();
String str = "uBtWZTiPMYkQLsp7rNly3RUIXKGqFbjnSg56H8ve49AC0mfO"; String str = "uBtWZTiPMYkQLsp7rNly3RUIXKGqFbjnSg56H8ve49AC0mfO";
for (UserVo u : result) { for (UserVo u : result) {
u.setPassword(DigestUtils.md5DigestAsHex((str+u.getPassword()).getBytes())); u.setPassword(DigestUtils.md5DigestAsHex((str + u.getPassword()).getBytes()));
} }
return new PageResult(200, "查找成功", pageNo, pages, total, result); return new PageResult(200, "查找成功", pageNo, pages, total, result);
} }
......
...@@ -25,6 +25,8 @@ public class JwtFilter implements Filter { ...@@ -25,6 +25,8 @@ public class JwtFilter implements Filter {
private static final String url4 = "/v2/api-docs"; private static final String url4 = "/v2/api-docs";
private static final String url7 = "/swagger-resources"; private static final String url7 = "/swagger-resources";
private static final String url8 = "/webjars/"; private static final String url8 = "/webjars/";
private static final String url9 = "/check_code";
private static final String url10 = "/reset_pwd";
@Override @Override
public void init(FilterConfig filterConfig) { public void init(FilterConfig filterConfig) {
...@@ -49,7 +51,7 @@ public class JwtFilter implements Filter { ...@@ -49,7 +51,7 @@ public class JwtFilter implements Filter {
boolean check = true; boolean check = true;
String uri = request.getRequestURI(); String uri = request.getRequestURI();
if (uri.contains(url1) || uri.contains(url2) || uri.contains(url3) || uri.contains(url4) || uri.contains(url7) || uri.contains(url8)) { if (uri.contains(url1) || uri.contains(url2) || uri.contains(url3) || uri.contains(url4) || uri.contains(url7) || uri.contains(url8) || uri.contains(url9) || uri.contains(url10)) {
if (uri.contains(url1)) { if (uri.contains(url1)) {
uri = XssUtil.checkXSS(uri); uri = XssUtil.checkXSS(uri);
UserUtils.setUri(uri); UserUtils.setUri(uri);
......
...@@ -13,9 +13,13 @@ public abstract class UserUtils { ...@@ -13,9 +13,13 @@ public abstract class UserUtils {
static Map<String, String> tokenMap = new HashMap<>(); static Map<String, String> tokenMap = new HashMap<>();
static Map<String, Date> tokenExpTimeMap = new HashMap<>(); static Map<String, Date> tokenExpTimeMap = new HashMap<>();
static Map<String, Integer> errCntMap = new HashMap<>(); static Map<String, Integer> errCntMap = new HashMap<>();
static Map<String, String> errCntTimeMap = new HashMap<>();
static Map<String, String> emailCodeMap = new HashMap<>(); static Map<String, String> emailCodeMap = new HashMap<>();
static Map<String, Date> emailCodeExpTimeMap = new HashMap<>(); static Map<String, Date> emailCodeExpTimeMap = new HashMap<>();
static Map<String, Date> countFreezeDateMap = new HashMap<>(); static Map<String, Date> countFreezeDateMap = new HashMap<>();
static Map<String, Integer> codeErrCntMap = new HashMap<>();
static Map<String, String> codeFreezeTimeMap = new HashMap<>();
/** /**
* 线程变量,存放user实体类信息,即使是静态的也与其他线程也是隔离的 * 线程变量,存放user实体类信息,即使是静态的也与其他线程也是隔离的
*/ */
...@@ -136,8 +140,43 @@ public abstract class UserUtils { ...@@ -136,8 +140,43 @@ public abstract class UserUtils {
return emailCodeExpTimeMap.get(email); return emailCodeExpTimeMap.get(email);
} }
public static void removeEmailCodeExpTime(String email) { public static void removeEmailCodeExpTime(String email) {
emailCodeExpTimeMap.remove(email); emailCodeExpTimeMap.remove(email);
} }
public static void setCodeErrCntMap(String account, int count) {
codeErrCntMap.put(account, count);
}
public static Integer getCodeErrCntMap(String account) {
return codeErrCntMap.get(account);
}
public static void removeCodeErrCntMap(String account) {
codeErrCntMap.remove(account);
}
public static void setCodeFreezeTimeMap(String account, String timeStr) {
codeFreezeTimeMap.put(account, timeStr);
}
public static String getCodeFreezeTimeMap(String account) {
return codeFreezeTimeMap.get(account);
}
public static void removeCodeFreezeTimeMap(String account) {
codeFreezeTimeMap.remove(account);
}
public static void setErrCntTimeMap(String account, String timeStr) {
errCntTimeMap.put(account, timeStr);
}
public static String getErrCntTimeMap(String account) {
return errCntTimeMap.get(account);
}
public static void removeErrCntTimeMap(String account) {
errCntTimeMap.remove(account);
}
} }
package iot.sixiang.license.model.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* Created by m33
* Date 2022/9/23 17:46
* Description
*/
@Data
public class CheckCodeDto {
@ApiModelProperty("验证码")
private String code;
}
package iot.sixiang.license.model.vo;
import lombok.Data;
/**
* Created by m33
* Date 2022/9/23 18:32
* Description
*/
@Data
public class UserResetPwdVo {
private String password;
}
...@@ -12,7 +12,7 @@ import iot.sixiang.license.model.vo.UserVo; ...@@ -12,7 +12,7 @@ import iot.sixiang.license.model.vo.UserVo;
* @author m33 * @author m33
* @since 2022-06-06 * @since 2022-06-06
*/ */
public interface UserService{ public interface UserService {
boolean deleteUser(int userIdVo); boolean deleteUser(int userIdVo);
...@@ -23,4 +23,6 @@ public interface UserService{ ...@@ -23,4 +23,6 @@ public interface UserService{
PageInfoModel<UserVo> getUserList(int pageNo, int pageSize, String userName, String company); PageInfoModel<UserVo> getUserList(int pageNo, int pageSize, String userName, String company);
User getUserById(int userId); User getUserById(int userId);
User getUserByName(String root);
} }
package iot.sixiang.license.service.impl; package iot.sixiang.license.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import iot.sixiang.license.consts.ResultCode; import iot.sixiang.license.consts.ResultCode;
import iot.sixiang.license.entity.User; import iot.sixiang.license.entity.User;
...@@ -34,11 +35,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us ...@@ -34,11 +35,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
UserMapper userMapper; UserMapper userMapper;
@Override @Override
public PageInfoModel<UserVo> getUserList(int pageNo, int pageSize,String userName, String company) { public PageInfoModel<UserVo> getUserList(int pageNo, int pageSize, String userName, String company) {
if(pageNo == 0 || pageSize == 0) { if (pageNo == 0 || pageSize == 0) {
throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(),ResultCode.VALIDATE_FAILED.getMsg()); throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(), ResultCode.VALIDATE_FAILED.getMsg());
} }
List<UserVo> records = userMapper.getUserList(userName,company); List<UserVo> records = userMapper.getUserList(userName, company);
records = records.stream().sorted(Comparator.comparing(UserVo::getCreateTime, Comparator.reverseOrder())).collect(Collectors.toList()); records = records.stream().sorted(Comparator.comparing(UserVo::getCreateTime, Comparator.reverseOrder())).collect(Collectors.toList());
List<UserVo> result = new ArrayList<>(); List<UserVo> result = new ArrayList<>();
int begin = (pageNo - 1) * pageSize; int begin = (pageNo - 1) * pageSize;
...@@ -56,10 +57,17 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us ...@@ -56,10 +57,17 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return userMapper.getUserById(userId); return userMapper.getUserById(userId);
} }
@Override
public User getUserByName(String root) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUserName, root).last("limit 1");
return userMapper.selectOne(wrapper);
}
@Override @Override
public boolean deleteUser(int userId) { public boolean deleteUser(int userId) {
if(userId == 0) { if (userId == 0) {
throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(),ResultCode.VALIDATE_FAILED.getMsg()); throw new IotLicenseException(ResultCode.VALIDATE_FAILED.getCode(), ResultCode.VALIDATE_FAILED.getMsg());
} }
return userMapper.deleteUser(userId); return userMapper.deleteUser(userId);
} }
...@@ -73,7 +81,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us ...@@ -73,7 +81,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
if (res != null) { if (res != null) {
throw new IotLicenseException(403, "用户名已存在"); throw new IotLicenseException(403, "用户名已存在");
} }
return userMapper.addUser(userName,company,password); return userMapper.addUser(userName, company, password);
} }
@Override @Override
......
...@@ -4,6 +4,7 @@ import iot.sixiang.license.consts.Consts; ...@@ -4,6 +4,7 @@ import iot.sixiang.license.consts.Consts;
import iot.sixiang.license.model.ResResult; import iot.sixiang.license.model.ResResult;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.util.TextUtils;
import org.springframework.boot.system.ApplicationHome; import org.springframework.boot.system.ApplicationHome;
import java.io.File; import java.io.File;
...@@ -14,9 +15,143 @@ import java.text.SimpleDateFormat; ...@@ -14,9 +15,143 @@ import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j @Slf4j
public class CommonUtil { public class CommonUtil {
/**
* 验证密码-是否包含用户名字符(密码应与用户名无相关性,密码中不得包含用户名的完整字符串、大小写变位或形似变换的字符串)
*/
public static boolean verifyPasswordContainAccount(String password, String account) {
boolean isContain = false;
if (!TextUtils.isEmpty(password) && !TextUtils.isEmpty(account)) {
password = password.toLowerCase();
account = account.toLowerCase();
if (password.contains(account)) {
return true;
}
String[] likes = {"a", "l", "o"};
String[] likeSign = {"@", "!", "0"};
String originalAccount = account + "";
for (int i = 0; i < likes.length; i++) {
String tempAccount = originalAccount.replace(likes[i], likeSign[i]);
account = account.replace(likes[i], likeSign[i]);
if (password.contains(tempAccount) || password.contains(account)) {
return true;
}
}
}
return isContain;
}
/**
* 键盘连续字符统计4个
*
* @param str
* @return
*/
public static boolean isKeyBoardContinuousChar(String str) {
boolean result = false;
char[][] c1 = {
{'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+'},
{'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '|'},
{'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ':', '"'},
{'z', 'x', 'c', 'v', 'b', 'n', 'm', '<', '>', '?'}
};
char[][] c2 = {
{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '='},
{'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '\\'},
{'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\''},
{'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/'}
};
for (char[][] c : new char[][][]{c1, c2}) {
//横向
for (char[] chars : c) {
for (int j = 0; j < chars.length - 3; j++) {
//创建连续字符
StringBuffer sb = new StringBuffer();
for (int k = j; k < j + 4; k++) {
sb.append(chars[k]);
}
String keyStr = sb.toString();
if (str.contains(keyStr)) {
return true;
}
}
}
//纵向
for (int i = 0; i < c[3].length; i++) {
//创建连续字符--每列只有4个
StringBuffer sb = new StringBuffer();
for (int j = 0; j < 4; j++) {
sb.append(c[j][i]);
}
String keyStr = sb.toString();
if (str.contains(keyStr)) {
return true;
}
}
}
return result;
}
public static void main(String[] args) {
boolean b = checkPassword("1234qwe123");
System.out.println(b);
}
/**
* 密码验证
* (至少由8位及以上大写字母、小写字母、数字与特殊符号等4类中3类混合)
*
* @param password
* @return
*/
public static boolean checkPassword(String password) {
boolean flag = false;
try {
int c = 0;
if (find("[a-z]+", password)) {
c++;
}
if (find("[A-Z]+", password)) {
c++;
}
if (find("[0-9]+", password)) {
c++;
}
if (find("\\W+|_", password) && !find("\\s+", password)) {//特殊符号
c++;
}
if (c >= 3) {
flag = true;
}
} catch (Exception e) {
flag = false;
}
return flag;
}
//通用匹配
public static boolean find(String regexStr, String input) {
boolean flag;
try {
Pattern regex = Pattern.compile(regexStr);
Matcher matcher = regex.matcher(input);
// 部分进行匹配
flag = matcher.find();
} catch (Exception e) {
flag = false;
}
return flag;
}
/** /**
* 获取随机验证码 * 获取随机验证码
*/ */
...@@ -45,8 +180,8 @@ public class CommonUtil { ...@@ -45,8 +180,8 @@ public class CommonUtil {
int maxNum = 36; int maxNum = 36;
int i = -1; int i = -1;
int count = 0; int count = 0;
char[] str = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', char[] str = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
StringBuilder pwd = new StringBuilder(""); StringBuilder pwd = new StringBuilder("");
SecureRandom secureRandom = null; SecureRandom secureRandom = null;
try { try {
...@@ -67,7 +202,6 @@ public class CommonUtil { ...@@ -67,7 +202,6 @@ public class CommonUtil {
} }
public static String getSystemTime() { public static String getSystemTime() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 设置日期格式 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 设置日期格式
String time = df.format(new Date()); String time = df.format(new Date());
...@@ -100,7 +234,7 @@ public class CommonUtil { ...@@ -100,7 +234,7 @@ public class CommonUtil {
} }
public static String getServerParentDirectory() { public static String getServerParentDirectory() {
return new File(new ApplicationHome(Consts.class).getSource().getParentFile().getPath()).getParent()+ File.separator + "lib"; return new File(new ApplicationHome(Consts.class).getSource().getParentFile().getPath()).getParent() + File.separator + "lib";
} }
/** /**
...@@ -147,6 +281,13 @@ public class CommonUtil { ...@@ -147,6 +281,13 @@ public class CommonUtil {
return res; return res;
} }
public static String getCurDateStr() {
Date date = new Date();
String pattern = "yyyy-MM-dd";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
return simpleDateFormat.format(date);
}
// 用于测试存储型xss // 用于测试存储型xss
public static Object reverseData(Object obj, Class clazz) { public static Object reverseData(Object obj, Class clazz) {
......
...@@ -32,12 +32,18 @@ server: ...@@ -32,12 +32,18 @@ server:
cros: cros:
# 需要设置访问白名单 # 需要设置访问白名单
cros_allowed_origins: http://192.168.1.88:8080, http://localhost:8868, http://localhost:8080 cros_allowed_origins: http://192.168.1.88:8080, http://192.168.1.88:8081, http://localhost:8868, http://localhost:8080, http://192.168.1.54:8080
cros_allowed_method: GET,POST cros_allowed_method: GET,POST
other: other:
md5: md5:
salt: PI7dBYlEfeP8IZ6vogqFL1U5pVnyCuNAGja3lsREx4M9r0SX salt: PI7dBYlEfeP8IZ6vogqFL1U5pVnyCuNAGja3lsREx4M9r0SX
error_count:
change_pwd: 5 # 修改密码的最大错误次数
forget_pwd: 5 # 忘记密码的最大错误次数
check_code: 5 # 校验验证码的最大错误次数
code_exp_time: 3 # 验证码失效时间,单位:分钟
rsa: rsa:
public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA18W2H1hO98dUWf1PNKNWTWmxCyvvy0NOR7iSvp76J0LdzyMJxs8WHVAmRfSGOb9SvpDZhBVx11bhTBqkl1qMzJWzn+F2ZtTCH2nXZcJHwSfLuGqin5FRBYW1WrFkqwg+R80aOuRSrbo0k1bZg3JPkkCxISHieEZPjSV5a4r7+Xopj0a9Dnh3rh4nDmH2p/wvotkx1oMKdhFglYcAITlk9ucEUf+CDuSdTAFFeKg9+fPqwKqWZRJZPQXqV3pGZ1/JS7gPnBFGZojW44eJufkBeiW3pbBvm/cKOkTnb8o4oltYUJsirYSQCCG+sDtxUAuGxuDCv8p+r8dWE1z5+xKclQIDAQAB public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA18W2H1hO98dUWf1PNKNWTWmxCyvvy0NOR7iSvp76J0LdzyMJxs8WHVAmRfSGOb9SvpDZhBVx11bhTBqkl1qMzJWzn+F2ZtTCH2nXZcJHwSfLuGqin5FRBYW1WrFkqwg+R80aOuRSrbo0k1bZg3JPkkCxISHieEZPjSV5a4r7+Xopj0a9Dnh3rh4nDmH2p/wvotkx1oMKdhFglYcAITlk9ucEUf+CDuSdTAFFeKg9+fPqwKqWZRJZPQXqV3pGZ1/JS7gPnBFGZojW44eJufkBeiW3pbBvm/cKOkTnb8o4oltYUJsirYSQCCG+sDtxUAuGxuDCv8p+r8dWE1z5+xKclQIDAQAB
private_key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDXxbYfWE73x1RZ/U80o1ZNabELK+/LQ05HuJK+nvonQt3PIwnGzxYdUCZF9IY5v1K+kNmEFXHXVuFMGqSXWozMlbOf4XZm1MIfaddlwkfBJ8u4aqKfkVEFhbVasWSrCD5HzRo65FKtujSTVtmDck+SQLEhIeJ4Rk+NJXlrivv5eimPRr0OeHeuHicOYfan/C+i2THWgwp2EWCVhwAhOWT25wRR/4IO5J1MAUV4qD358+rAqpZlElk9BepXekZnX8lLuA+cEUZmiNbjh4m5+QF6JbelsG+b9wo6ROdvyjiiW1hQmyKthJAIIb6wO3FQC4bG4MK/yn6vx1YTXPn7EpyVAgMBAAECggEAR7YQ+kfqLtVThnj2mwLyCtZmndTjZEWhPZrtQmcpsmS5vT7i3+0xZ1qc7cD/3y9j+6u+bvSFmlDondd4/kh85P2X7joLlM9/GNufV9WC7YIhZdAi7i9ooxI2HMc6MtGRiWF0J0B87folwRYrQlF6epv/goh1cQ3FIJ7kxMYzSk0gF0JcmOZn3KH8tMt3t/GK+uVUIycuEQQKsaTTq45nIM7oqhlAwD/M+IO2pGFkLXJ23FzFACI10qBdpn+xL2xFGRO6EE8EAeDslT4OvvN3/vtnSnRNn8CJqfoEG40XO0xrZzH1noI35iPWX1WrN7qFLAhl2oLhu1ZSIA1tz0+I/QKBgQD3X/islXfVmV2XeuHvaf4qcMAdrgLQwtmLlHhFxfFURPh6au8uatDgUA8HWcRACmhtTruytlFRGKKFwZxuQE/LOZh67Uts3GTs5eHN8xvZTL+en+n7B/cCRYrrg3+yAM/k0eezIlk65iW700o+icEHxkwTXhhVBmIROBzpXsVCwwKBgQDfS6kVhgZxLMQePbXUQ1NBr2KbfhXLzceINhsoyWLa1rIk8+9HLSxw8q0zma12Jqkd22OAgkbeLDy+niYPi3pUrWAm8O59Ot1aaOarxMTvEv7+eS+urKId57sli/hQTWsS+xghA4+VfW2+EY++pYyZIp+j+1/Q2ciXWVJy4iv9xwKBgFzW8+kxn2vWxz1WrPzBdtZOwothB0V6G1M7QXhONag+ylKHV4TAKexFn55Onky6mz6K0f7cVeBtsnEonKD0Gf5Xe1aHQEt225ndHMXCe60uFKxfr9y6vIVpvB1vmLkhfOSPsrmUJpDoVzkKr06RPJTCY0LRiag/YQa9XHxpSPcpAoGAKbsiJnudyJjtLhmqWbkbXjNA4n515FjY6YPzH3RDnVJyiKVuGoc+vv0bkYEvAd3HzWSq++FdDTiHQbictFsEyb59McnlSFIv/C2Orptfkq6iKTzMxIBO6/fa6fF2vss5L5rtr33S38VJNTRjAOY/mH74BtV72rRY4LA40G+keRECgYEAiGg0DYxcSGf2bEP1WESYPTTdgS5ke1niIwZ00SgtkIjPSVgTCmf4Tciys6lGSe/Oqnvk24VR6pz07wzcbH92AURVaeqiEvVuVonzA6Yl0jxeOIM31S1BSBJRT8kDijuvwoJu2tPoZG0306KF9L8pyy1Z6cTTmIfGR0NpZCHWPSg= private_key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDXxbYfWE73x1RZ/U80o1ZNabELK+/LQ05HuJK+nvonQt3PIwnGzxYdUCZF9IY5v1K+kNmEFXHXVuFMGqSXWozMlbOf4XZm1MIfaddlwkfBJ8u4aqKfkVEFhbVasWSrCD5HzRo65FKtujSTVtmDck+SQLEhIeJ4Rk+NJXlrivv5eimPRr0OeHeuHicOYfan/C+i2THWgwp2EWCVhwAhOWT25wRR/4IO5J1MAUV4qD358+rAqpZlElk9BepXekZnX8lLuA+cEUZmiNbjh4m5+QF6JbelsG+b9wo6ROdvyjiiW1hQmyKthJAIIb6wO3FQC4bG4MK/yn6vx1YTXPn7EpyVAgMBAAECggEAR7YQ+kfqLtVThnj2mwLyCtZmndTjZEWhPZrtQmcpsmS5vT7i3+0xZ1qc7cD/3y9j+6u+bvSFmlDondd4/kh85P2X7joLlM9/GNufV9WC7YIhZdAi7i9ooxI2HMc6MtGRiWF0J0B87folwRYrQlF6epv/goh1cQ3FIJ7kxMYzSk0gF0JcmOZn3KH8tMt3t/GK+uVUIycuEQQKsaTTq45nIM7oqhlAwD/M+IO2pGFkLXJ23FzFACI10qBdpn+xL2xFGRO6EE8EAeDslT4OvvN3/vtnSnRNn8CJqfoEG40XO0xrZzH1noI35iPWX1WrN7qFLAhl2oLhu1ZSIA1tz0+I/QKBgQD3X/islXfVmV2XeuHvaf4qcMAdrgLQwtmLlHhFxfFURPh6au8uatDgUA8HWcRACmhtTruytlFRGKKFwZxuQE/LOZh67Uts3GTs5eHN8xvZTL+en+n7B/cCRYrrg3+yAM/k0eezIlk65iW700o+icEHxkwTXhhVBmIROBzpXsVCwwKBgQDfS6kVhgZxLMQePbXUQ1NBr2KbfhXLzceINhsoyWLa1rIk8+9HLSxw8q0zma12Jqkd22OAgkbeLDy+niYPi3pUrWAm8O59Ot1aaOarxMTvEv7+eS+urKId57sli/hQTWsS+xghA4+VfW2+EY++pYyZIp+j+1/Q2ciXWVJy4iv9xwKBgFzW8+kxn2vWxz1WrPzBdtZOwothB0V6G1M7QXhONag+ylKHV4TAKexFn55Onky6mz6K0f7cVeBtsnEonKD0Gf5Xe1aHQEt225ndHMXCe60uFKxfr9y6vIVpvB1vmLkhfOSPsrmUJpDoVzkKr06RPJTCY0LRiag/YQa9XHxpSPcpAoGAKbsiJnudyJjtLhmqWbkbXjNA4n515FjY6YPzH3RDnVJyiKVuGoc+vv0bkYEvAd3HzWSq++FdDTiHQbictFsEyb59McnlSFIv/C2Orptfkq6iKTzMxIBO6/fa6fF2vss5L5rtr33S38VJNTRjAOY/mH74BtV72rRY4LA40G+keRECgYEAiGg0DYxcSGf2bEP1WESYPTTdgS5ke1niIwZ00SgtkIjPSVgTCmf4Tciys6lGSe/Oqnvk24VR6pz07wzcbH92AURVaeqiEvVuVonzA6Yl0jxeOIM31S1BSBJRT8kDijuvwoJu2tPoZG0306KF9L8pyy1Z6cTTmIfGR0NpZCHWPSg=
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment