Commit 7f934a84 authored by mengmeng's avatar mengmeng

修改加密传输功能;非法登录

parent 2595cc25
...@@ -71,6 +71,7 @@ public class ShiroConfig { ...@@ -71,6 +71,7 @@ public class ShiroConfig {
filterMap.put("/modeler.html", "anon"); filterMap.put("/modeler.html", "anon");
filterMap.put("/captcha", "anon"); filterMap.put("/captcha", "anon");
filterMap.put("/favicon.ico", "anon"); filterMap.put("/favicon.ico", "anon");
filterMap.put("/getCryptoKey", "anon");
filterMap.put("/**", "oauth2"); filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap); shiroFilter.setFilterChainDefinitionMap(filterMap);
...@@ -89,4 +90,4 @@ public class ShiroConfig { ...@@ -89,4 +90,4 @@ public class ShiroConfig {
advisor.setSecurityManager(securityManager); advisor.setSecurityManager(securityManager);
return advisor; return advisor;
} }
} }
\ No newline at end of file
...@@ -2,9 +2,15 @@ package io.hmit.modules.security.controller; ...@@ -2,9 +2,15 @@ package io.hmit.modules.security.controller;
import io.hmit.common.exception.ErrorCode; import io.hmit.common.exception.ErrorCode;
import io.hmit.common.exception.HmitException; import io.hmit.common.exception.HmitException;
import io.hmit.common.redis.RedisKeys;
import io.hmit.common.redis.RedisUtils;
import io.hmit.common.utils.DateUtils;
import io.hmit.common.utils.IpUtils; import io.hmit.common.utils.IpUtils;
import io.hmit.common.utils.NumberUtils;
import io.hmit.common.utils.Result; import io.hmit.common.utils.Result;
import io.hmit.common.validator.AESUtils;
import io.hmit.common.validator.AssertUtils; import io.hmit.common.validator.AssertUtils;
import io.hmit.common.validator.CodeUtils;
import io.hmit.common.validator.ValidatorUtils; import io.hmit.common.validator.ValidatorUtils;
import io.hmit.modules.log.entity.SysLogLoginEntity; import io.hmit.modules.log.entity.SysLogLoginEntity;
import io.hmit.modules.log.enums.LoginOperationEnum; import io.hmit.modules.log.enums.LoginOperationEnum;
...@@ -46,6 +52,13 @@ import java.util.Date; ...@@ -46,6 +52,13 @@ import java.util.Date;
@Api(tags = "登录管理") @Api(tags = "登录管理")
public class LoginController { public class LoginController {
private static final int LOGIN_IP_LIMIT_TIME = 3; // 登录失败三次,需要进行验证码验证
private static final int LOGIN_USERNAME_DISABLE_TIME = 10; // 同一个用户名登录失败10次,账号将被冻结
private static final int LOGIN_USERNAME_PROMPT_TIME = 5; // 同一个账号错误5次,提醒用户超过10次,账号将被冻结
private static final long LOGIN_EXPIRE_TIME = 1800; // 登录失败记录时长
private static final long LOGIN_CRYPTO_KEY_EXPIRE_TIME = 60 * 60 * 24; // 登录失败记录时长
@Autowired @Autowired
private SysUserService sysUserService; private SysUserService sysUserService;
@Autowired @Autowired
...@@ -55,6 +68,9 @@ public class LoginController { ...@@ -55,6 +68,9 @@ public class LoginController {
@Autowired @Autowired
private SysLogLoginService sysLogLoginService; private SysLogLoginService sysLogLoginService;
@Autowired
private RedisUtils redisUtils;
@GetMapping("captcha") @GetMapping("captcha")
@ApiOperation(value = "验证码", produces = "application/octet-stream") @ApiOperation(value = "验证码", produces = "application/octet-stream")
@ApiImplicitParam(paramType = "query", dataType = "string", name = "uuid", required = true) @ApiImplicitParam(paramType = "query", dataType = "string", name = "uuid", required = true)
...@@ -73,9 +89,18 @@ public class LoginController { ...@@ -73,9 +89,18 @@ public class LoginController {
out.close(); out.close();
} }
@ApiOperation(value = "获取加密的KEY")
@GetMapping("getCryptoKey")
public Result getCryptoKey() {
String key = CodeUtils.getNumCode(16);
redisUtils.set(RedisKeys.getCryptoKey(key), key, LOGIN_CRYPTO_KEY_EXPIRE_TIME);
return new Result().ok(AESUtils.encrypt(key));
}
@PostMapping("login") @PostMapping("login")
@ApiOperation(value = "登录") @ApiOperation(value = "登录")
public Result login(HttpServletRequest request, @RequestBody LoginDTO login) { public Result login(HttpServletRequest request, @RequestBody LoginDTO login)throws Exception {
//校验数据 //校验数据
ValidatorUtils.validateEntity(login); ValidatorUtils.validateEntity(login);
...@@ -86,8 +111,14 @@ public class LoginController { ...@@ -86,8 +111,14 @@ public class LoginController {
return new Result().error(ErrorCode.CAPTCHA_ERROR); return new Result().error(ErrorCode.CAPTCHA_ERROR);
} }
//获取解密后的登录名和密码
checkLogin(request,login);
String username = login.getUsername();
String password = login.getPassword();
//用户信息 //用户信息
SysUserDTO user = sysUserService.getByUsername(login.getUsername()); // SysUserDTO user = sysUserService.getByUsername(login.getUsername());
SysUserDTO user = sysUserService.getByUsername(username);
SysLogLoginEntity log = new SysLogLoginEntity(); SysLogLoginEntity log = new SysLogLoginEntity();
log.setOperation(LoginOperationEnum.LOGIN.value()); log.setOperation(LoginOperationEnum.LOGIN.value());
...@@ -106,12 +137,13 @@ public class LoginController { ...@@ -106,12 +137,13 @@ public class LoginController {
} }
//密码错误 //密码错误
if (!PasswordUtils.matches(PasswordUtils.JM(login.getPassword()), user.getPassword())) { if (!PasswordUtils.matches(password, user.getPassword())) {
log.setStatus(LoginStatusEnum.FAIL.value()); log.setStatus(LoginStatusEnum.FAIL.value());
log.setCreator(user.getId()); log.setCreator(user.getId());
log.setCreatorName(user.getUsername()); log.setCreatorName(user.getUsername());
sysLogLoginService.save(log); sysLogLoginService.save(log);
addFailureLogin(login, request);
throw new HmitException(ErrorCode.ACCOUNT_PASSWORD_ERROR); throw new HmitException(ErrorCode.ACCOUNT_PASSWORD_ERROR);
} }
...@@ -158,4 +190,32 @@ public class LoginController { ...@@ -158,4 +190,32 @@ public class LoginController {
return new Result(); return new Result();
} }
private void checkLogin(HttpServletRequest request, LoginDTO login) throws Exception {
// 检查cryptoKey有效性
String cryptoKey = AESUtils.decrypt(login.getCryptoKey());
if (null == redisUtils.get(RedisKeys.getCryptoKey(cryptoKey))) {
throw new HmitException("刷新页面重新登陆");
}
login.setUsername(AESUtils.decrypt(login.getUsername(), cryptoKey));
login.setPassword(AESUtils.decrypt(login.getPassword(), cryptoKey));
checkUsernameLogin(request, login, true);
}
private void checkUsernameLogin(HttpServletRequest request, LoginDTO login, boolean prompt) throws Exception {
int unCount = NumberUtils.initInt(redisUtils.get(RedisKeys.getUserNameKey(login.getUsername())));
if (unCount >= LOGIN_USERNAME_DISABLE_TIME) {
throw new HmitException("用户名登录次数超过" + LOGIN_USERNAME_DISABLE_TIME + "次,被锁定,解冻剩余时间 "
+ DateUtils.formatLong(redisUtils.getExpire(RedisKeys.getUserNameKey(login.getUsername()))));
}
if (prompt && unCount >= LOGIN_USERNAME_PROMPT_TIME) {
throw new HmitException("用户名或密码不正确, 失败登录" + unCount + "次, 超过" + LOGIN_USERNAME_DISABLE_TIME + "次, 账号将被冻结" + LOGIN_EXPIRE_TIME/60 + "分钟");
}
}
private void addFailureLogin(LoginDTO login, HttpServletRequest request) throws Exception {
redisUtils.incr(RedisKeys.getUserNameKey(login.getUsername()), LOGIN_EXPIRE_TIME);
}
} }
...@@ -34,4 +34,9 @@ public class LoginDTO implements Serializable { ...@@ -34,4 +34,9 @@ public class LoginDTO implements Serializable {
@NotBlank(message = "{sysuser.uuid.require}") @NotBlank(message = "{sysuser.uuid.require}")
private String uuid; private String uuid;
} @ApiModelProperty(value = "加密密钥")
\ No newline at end of file @NotBlank(message = "{sysuser.cryptoKey.require}")
private String cryptoKey;
}
...@@ -26,9 +26,9 @@ spring: ...@@ -26,9 +26,9 @@ spring:
enabled: true enabled: true
redis: redis:
database: 0 database: 0
host: 192.168.10.10 host: localhost
port: 6379 port: 6379
password: # 密码(默认为空) password: Hmit@1234@_@ # 密码(默认为空)
timeout: 6000ms # 连接超时时长(毫秒) timeout: 6000ms # 连接超时时长(毫秒)
jedis: jedis:
pool: pool:
...@@ -47,7 +47,7 @@ fdfs: ...@@ -47,7 +47,7 @@ fdfs:
- 192.168.10.10:22122 - 192.168.10.10:22122
# 是否开启redis缓存 true开启 false关闭 # 是否开启redis缓存 true开启 false关闭
hmit.redis.open: false hmit.redis.open: true
#mybatis #mybatis
mybatis-plus: mybatis-plus:
......
...@@ -27,6 +27,20 @@ public class RedisKeys { ...@@ -27,6 +27,20 @@ public class RedisKeys {
return "sys:security:user:" + id; return "sys:security:user:" + id;
} }
/**
* 密钥key
*/
public static String getCryptoKey(String crypto) {
return "sys:security:crypto:" + crypto;
}
/**
* 登录用户名key
*/
public static String getUserNameKey(String userName) {
return "sys:security:userName:" + userName;
}
/** /**
* 系统日志Key * 系统日志Key
*/ */
......
...@@ -124,4 +124,27 @@ public class RedisUtils { ...@@ -124,4 +124,27 @@ public class RedisUtils {
public Object rightPop(String key) { public Object rightPop(String key) {
return redisTemplate.opsForList().rightPop(key); return redisTemplate.opsForList().rightPop(key);
} }
}
\ No newline at end of file // 新增
/**
* 获取过期时间
* @param key
* @return
* @throws Exception
*/
public Long getExpire(final String key) throws Exception {
return redisTemplate.opsForValue().getOperations().getExpire(key);
}
// 数值加一
public void incr(final String key, final Long expireSec) throws Exception {
Object obj = get(key);
if (null != obj) {
set(key, Integer.valueOf(obj.toString()) + 1, expireSec);
} else {
set(key, 1, expireSec);
}
}
}
...@@ -26,6 +26,9 @@ public class DateUtils { ...@@ -26,6 +26,9 @@ public class DateUtils {
*/ */
public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public final static String VAL_TIMESTAMP_FORMAT = "yyyy-MM-dd-HH.mm.ss.SSS";
/** /**
* 日期格式化 日期格式为:yyyy-MM-dd * 日期格式化 日期格式为:yyyy-MM-dd
* *
...@@ -36,6 +39,15 @@ public class DateUtils { ...@@ -36,6 +39,15 @@ public class DateUtils {
return format(date, DATE_PATTERN); return format(date, DATE_PATTERN);
} }
/**
* 将当前日期转换成特定的时间格式
* @param pattern 要转换的日期格式
* @return 返回对应时间格式的时间
*/
public static String format(String pattern) {
return format(new Date(), pattern);
}
/** /**
* 日期格式化 日期格式为:yyyy-MM-dd * 日期格式化 日期格式为:yyyy-MM-dd
* *
...@@ -192,4 +204,21 @@ public class DateUtils { ...@@ -192,4 +204,21 @@ public class DateUtils {
DateTime dateTime = new DateTime(date); DateTime dateTime = new DateTime(date);
return dateTime.plusYears(years).toDate(); return dateTime.plusYears(years).toDate();
} }
/**
* 将long值转换为以小时计算的格式
* @param mss
* @return
*/
public static String formatLong(long mss) {
String DateTimes = null;
long hours = (mss % ( 60 * 60 * 24)) / (60 * 60);
long minutes = (mss % ( 60 * 60)) /60;
long seconds = mss % 60;
DateTimes=String.format("%02d:", hours)+ String.format("%02d:", minutes) + String.format("%02d", seconds);
String.format("%2d:", hours);
return DateTimes;
}
} }
/**
* Project: yui3-common-utils
* Class NumberUtils
* Version 1.0
* File Created at 2017年11月22日
* $Id$
*
* Copyright 2010-2015 Yui.com Corporation Limited.
* All rights reserved.
*
* This software is the confidential and proprietary information of
* Yui Personal. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Yui.com.
*/
package io.hmit.common.utils;
import java.math.BigDecimal;
/**
* 数字计算
* @author yuyi (1060771195@qq.com)
*/
public class NumberUtils {
public static int initInt(Object obj) {
if (null == obj) {
return 0;
}
try {
return Integer.parseInt(obj.toString());
} catch (Exception e) {
return 0;
}
}
public static boolean isLong(String str) {
try {
Long.parseLong(str);
return true;
} catch (Exception e) {
return false;
}
}
public static boolean equal(Long n1, Long n2) {
if (null == n1 || null == n2) {
return false;
}
if (n1.longValue() == n2.longValue()) {
return true;
}
return false;
}
public static boolean equal(Integer n1, Integer n2) {
if (null == n1 || null == n2) {
return false;
}
if (n1.intValue() == n2.intValue()) {
return true;
}
return false;
}
public static BigDecimal add(BigDecimal d1, BigDecimal d2) {
return d1.add(d2);
}
public static BigDecimal subtract(BigDecimal d1, BigDecimal d2) {
return d1.subtract(d2);
}
public static BigDecimal multiply(BigDecimal d1, int modulus, int scale) {
return d1.multiply(new BigDecimal(modulus)).setScale(scale, BigDecimal.ROUND_HALF_UP);
}
public static BigDecimal multiply(BigDecimal d1, BigDecimal modulus, int scale) {
return d1.multiply(modulus).setScale(scale, BigDecimal.ROUND_HALF_UP);
}
public static BigDecimal divide(BigDecimal numerator, int denominator, int scale) {
return numerator.divide(new BigDecimal(denominator), BigDecimal.ROUND_HALF_UP);//.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
public static BigDecimal divide(BigDecimal numerator, BigDecimal denominator, int scale) {
return numerator.divide(denominator, BigDecimal.ROUND_HALF_UP);//.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
/**
* 提供精确的乘法运算。
*
* @param value1 被乘数
* @param value2 乘数
* @return 两个参数的积
*/
public static Double mul(Double value1, Double value2) {
if (null == value1) {
return 0D;
}
if (null == value2) {
return 0D;
}
BigDecimal b1 = new BigDecimal(Double.toString(value1));
BigDecimal b2 = new BigDecimal(Double.toString(value2));
return b1.multiply(b2).doubleValue();
}
public static Double mul(Double value1, Integer value2) {
if (null == value1) {
return 0D;
}
if (null == value2) {
return 0D;
}
BigDecimal b1 = new BigDecimal(Double.toString(value1));
BigDecimal b2 = new BigDecimal(Integer.toString(value2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时, 精确到小数点以后10位,以后的数字四舍五入。
*
* @param dividend 被除数
* @param divisor 除数
* @return 两个参数的商
*/
public static Double divide(Double dividend, Double divisor) {
if (null == dividend) {
return 0D;
}
return divide(dividend, divisor, BigDecimal.ROUND_HALF_UP);
}
/**
* 提供(相对)精确的除法运算。 当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。
*
* @param dividend 被除数
* @param divisor 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static Double divide(Double dividend, Double divisor, Integer scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(dividend));
BigDecimal b2 = new BigDecimal(Double.toString(divisor));
return b1.divide(b2, scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 提供精确的加法运算。
*
* @param value1 被加数
* @param value2 加数
* @return 两个参数的和
*/
public static Double add(Double value1, Double value2) {
if (null == value1) {
value1 = 0D;
}
if (null == value2) {
value2 = 0D;
}
BigDecimal b1 = new BigDecimal(Double.toString(value1));
BigDecimal b2 = new BigDecimal(Double.toString(value2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确的减法运算。
*
* @param value1 被减数
* @param value2 减数
* @return 两个参数的差
*/
public static double sub(Double value1, Double value2) {
if (null == value1) {
value1 = 0D;
}
if (null == value2) {
value2 = 0D;
}
BigDecimal b1 = new BigDecimal(Double.toString(value1));
BigDecimal b2 = new BigDecimal(Double.toString(value2));
return b1.subtract(b2).doubleValue();
}
}
package io.hmit.common.validator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
@Slf4j
public class AESUtils {
private static final String ALGORITHM = "AES";
private static final String CHARSET_NAME = "UTF-8";
private static final String DEF_KEY = "2020020202020202";
public static String decrypt(String content) {
return decrypt(content, DEF_KEY);
}
public static String decrypt(String content, String key) {
try {
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(CHARSET_NAME), ALGORITHM);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // "算法/模式/补码方式"
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
return new String(cipher.doFinal(Base64.decodeBase64(content)));
} catch (Exception e) {
log.error("AES decrypt error", e);
}
return null;
}
public static String encrypt(String content) {
return encrypt(content, DEF_KEY);
}
public static String encrypt(String content, String key) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(CHARSET_NAME), ALGORITHM);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);//ENCRYPT_MODE指加密操作
return Base64.encodeBase64String(cipher.doFinal(content.getBytes(CHARSET_NAME)));
} catch (Exception e) {
log.error("AES encrypt error, [key = " + key + "]", e);
}
return null;
}
public static void main(String[] args) {
String key = "20210106153411242021010615341124";
String encrypt = encrypt("1124", key);
String decrypt = decrypt(encrypt, key);
System.out.println(encrypt);
System.out.println(decrypt);
}
}
/**
* Project: yui-common-utils
* Class CodeUtils
* Version 1.0
* File Created at 2018年4月15日
* $Id$
*
* Copyright 2010-2015 Yui.com Corporation Limited.
* All rights reserved.
*
* This software is the confidential and proprietary information of
* Yui Personal. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Yui.com.
*/
package io.hmit.common.validator;
import io.hmit.common.utils.DateUtils;
import java.util.Random;
/**
* 编码生成器
* @author Eve
* @email mengmengeve@gmail.com
*/
public class CodeUtils {
@SuppressWarnings("unused")
private static String letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //去除1l和0o,防止0和o,1和l混合不清
@SuppressWarnings("unused")
private static String digital = "0123456789"; //去除1l和0o,防止0和o,1和l混合不清
private static String letterDigital = "ABCDEFGHIJKMNPQRSTUVWXYZ0123456789";
public static char[] numSequence = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
public static String genCode() {
String timestampFormat = DateUtils.format(DateUtils.VAL_TIMESTAMP_FORMAT);
timestampFormat = timestampFormat.replace("-", "");
timestampFormat = timestampFormat.replace(".", "");
timestampFormat = timestampFormat.substring(2);
return (timestampFormat + getRandomThreeeDigit()).substring(0,12);
}
public static int getRandom(int count) {
return (int) Math.round(Math.random() * count);
}
public static String getRandomLetterDigital(int len){
StringBuffer sb = new StringBuffer();
int letterDigitalLength = letterDigital.length();
for (int i = 0; i < len; i++) {
sb.append(letterDigital.charAt(getRandom(letterDigitalLength-1)));
}
return sb.toString();
}
public static int getRandomThreeeDigit() {
int i=(int)(Math.random()*900)+100;
return i;
}
public static String getNumCode(int len) {
StringBuffer randomCode = new StringBuffer();
Random random = new Random();
for (int i = 0; i < len; i++) {
randomCode.append(String.valueOf(numSequence[random.nextInt(10)]));
}
return randomCode.toString();
}
}
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