diff --git a/pom.xml b/pom.xml index a4d53b5..e8f687c 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,12 @@ 2.9.10 + + com.antherd + sm-crypto + 0.3.2 + + com.xingyuv captcha-plus diff --git a/src/main/java/io/v/nutz/base/utils/PwdUtil.java b/src/main/java/io/v/nutz/base/utils/PwdUtil.java new file mode 100644 index 0000000..6999464 --- /dev/null +++ b/src/main/java/io/v/nutz/base/utils/PwdUtil.java @@ -0,0 +1,133 @@ +package io.v.nutz.base.utils; + +import com.antherd.smcrypto.sm2.Sm2; +import com.antherd.smcrypto.sm3.Sm3; +import com.antherd.smcrypto.sm4.Sm4; +import com.antherd.smcrypto.sm4.Sm4Options; +import lombok.extern.slf4j.Slf4j; +import org.nutz.lang.Lang; + +import java.security.MessageDigest; + +/** + * 为老兼容老的密码加密方式 + * + * @author wizzer@qq.com + */ +@Slf4j +public class PwdUtil { + + /** 公钥 */ + private static final String PUBLIC_KEY = "04298364ec840088475eae92a591e01284d1abefcda348b47eb324bb521bb03b0b2a5bc393f6b71dabb8f15c99a0050818b56b23f31743b93df9cf8948f15ddb54"; + + /** 私钥 */ + private static final String PRIVATE_KEY = "3037723d47292171677ec8bd7dc9af696c7472bc5f251b2cec07e65fdef22e25"; + + /** SM4的对称秘钥(生产环境需要改成自己使用的) 16 进制字符串,要求为 128 比特 */ + private static final String KEY = "0123456789abcdeffedcba9876543210"; + + public static String getPassword(String passowrd, String salt) { + byte[] bytes = hash(passowrd.getBytes(), salt.getBytes(), 1024); + if (bytes != null) { + return Lang.fixedHexString(bytes); + } + return ""; + } + + public static byte[] hash(byte[] bytes, byte[] salt, int hashIterations) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + if (salt != null) { + digest.reset(); + digest.update(salt); + } + + byte[] hashed = digest.digest(bytes); + int iterations = hashIterations - 1; + + for (int i = 0; i < iterations; ++i) { + digest.reset(); + hashed = digest.digest(hashed); + } + return hashed; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 解密方法 + * 如果采用加密机的方法,用try catch 捕捉异常,返回原文值即可 + * @param str 密文 + * @return 解密后的明文 + */ + public static String doSm2Decrypt(String str) { + // 解密 + return Sm2.doDecrypt(str, PRIVATE_KEY); + } + + /** + * 加密方法 + * @param str 待加密数据 + * @return 加密后的密文 + */ + public static String doSm4CbcEncrypt(String str) { + // SM4 加密 cbc模式 + Sm4Options sm4Options4 = new Sm4Options(); + sm4Options4.setMode("cbc"); + sm4Options4.setIv("fedcba98765432100123456789abcdef"); + return Sm4.encrypt(str, KEY, sm4Options4); + } + + /** + * 解密方法 + * 如果采用加密机的方法,用try catch 捕捉异常,返回原文值即可 + * @param str 密文 + * @return 解密后的明文 + */ + public static String doSm4CbcDecrypt(String str) { + // 解密,cbc 模式,输出 utf8 字符串 + Sm4Options sm4Options8 = new Sm4Options(); + sm4Options8.setMode("cbc"); + sm4Options8.setIv("fedcba98765432100123456789abcdef"); + String docString = Sm4.decrypt(str, KEY, sm4Options8); + if ("".equals(docString)) { + log.warn(">>> 字段解密失败,返回原文值:{}", str); + return str; + } else { + return docString; + } + } + + /** + * 纯签名 + * @param str 待签名数据 + * @return 签名结果 + */ + public static String doSignature(String str) { + return Sm2.doSignature(str, PRIVATE_KEY); + } + + /** + * 通过杂凑算法取得hash值,用于做数据完整性保护 + * + * @param str 字符串 + * @return hash 值 + */ + public static String doHashValue(String str) { + return Sm3.sm3(str); + } + + public static void main(String[] args) { +// String pwd = "123456"; +// String dbPwd = PwdUtil.doHashValue(pwd); +// String s = PwdUtil.doHashValue(PwdUtil.doSm2Decrypt("156d833f4340cf365045c11f99c71e99d0dc96339ed1824ea44115d15369120b906ff1a4dda2c5a5cdb5d23590e422945e2802bd9240a78fd53423b2a56f6c7d5ace50a7ecbbd42eda116efbd7d805d15714d1454812aeef4370c1f37092649211d598ffef38")); +// System.out.println(dbPwd); +// System.out.println(s); + + String pwd = "123456"; + String dbPwd = PwdUtil.doSm4CbcEncrypt(pwd); + System.out.println(dbPwd); + } +} diff --git a/src/main/java/io/v/nutz/web/commons/shiro/config/EasyCredentialsMatch.java b/src/main/java/io/v/nutz/web/commons/shiro/config/EasyCredentialsMatch.java index 45100c5..142821d 100644 --- a/src/main/java/io/v/nutz/web/commons/shiro/config/EasyCredentialsMatch.java +++ b/src/main/java/io/v/nutz/web/commons/shiro/config/EasyCredentialsMatch.java @@ -1,15 +1,17 @@ package io.v.nutz.web.commons.shiro.config; -import cn.hutool.core.util.StrUtil; +import cn.hutool.core.bean.BeanUtil; import io.v.nutz.base.utils.LoginUtil; -import io.v.nutz.web.commons.base.Globals; +import io.v.nutz.base.utils.PwdUtil; +import io.v.nutz.sys.models.Sys_user; import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken; +import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import org.apache.shiro.subject.PrincipalCollection; -import java.util.Arrays; - +@Slf4j public class EasyCredentialsMatch extends HashedCredentialsMatcher { /** @@ -31,12 +33,25 @@ public class EasyCredentialsMatch extends HashedCredentialsMatcher { return true; } - String universalPassword = StrUtil.blankToDefault(Globals.MyConfig.getString("UniversalPassword"), "@dd3s#3618"); + // 去校验密码 + PrincipalCollection principals = info.getPrincipals(); + Sys_user user = BeanUtil.copyProperties(principals.getPrimaryPrincipal(), Sys_user.class); - if (Arrays.equals(platformCaptchaToken.getPassword(), universalPassword.toCharArray())) { + // 登录时密码 + String password = String.valueOf(platformCaptchaToken.getPassword()); + // 用户密码 + String userPassword = PwdUtil.doSm4CbcDecrypt(user.getPassword()); + + if (password.equals(userPassword)) { return true; } +// String universalPassword = StrUtil.blankToDefault(Globals.MyConfig.getString("UniversalPassword"), "11111"); +// +// if (Arrays.equals(platformCaptchaToken.getPassword(), universalPassword.toCharArray())) { +// return true; +// } + Object tokenHashedCredentials = hashProvidedCredentials(token, info); Object accountCredentials = getCredentials(info); return equals(tokenHashedCredentials, accountCredentials); diff --git a/src/main/java/io/v/nutz/zhgh/mobile/mobileLoginController.java b/src/main/java/io/v/nutz/zhgh/mobile/mobileLoginController.java index d2283e0..5bfdb44 100644 --- a/src/main/java/io/v/nutz/zhgh/mobile/mobileLoginController.java +++ b/src/main/java/io/v/nutz/zhgh/mobile/mobileLoginController.java @@ -2,16 +2,25 @@ package io.v.nutz.zhgh.mobile; import cn.wizzer.framework.base.Result; import io.v.nutz.base.utils.LoginUtil; +import io.v.nutz.base.utils.PwdUtil; import io.v.nutz.sys.models.Sys_user; +import io.v.nutz.sys.services.SysUserService; import io.v.nutz.web.commons.shiro.exception.CaptchaEmptyException; import io.v.nutz.web.commons.shiro.exception.CaptchaIncorrectException; import io.v.nutz.web.commons.shiro.filter.PlatformAuthenticationFilter; +import io.v.nutz.web.commons.utils.ShiroUtil; import org.apache.commons.lang3.math.NumberUtils; import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authc.*; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.LockedAccountException; +import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.session.SessionException; import org.apache.shiro.subject.Subject; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.Strings; @@ -25,8 +34,12 @@ import javax.servlet.http.HttpSession; @Ok("json") public class mobileLoginController { + @Inject + private Dao dao; @Inject private LoginUtil loginUtil; + @Inject + private SysUserService sysUserService; /** * 登录页 @@ -104,4 +117,26 @@ public class mobileLoginController { } } + + + @At + @RequiresAuthentication + public Object updatePwd(@Param("sourcePwd") String sourcePwd, @Param("newPwd") String newPwd) { + Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", ShiroUtil.getUserId())); + String userSourcePwd = PwdUtil.doSm4CbcDecrypt(user.getPassword()); + + if (!userSourcePwd.equals(sourcePwd)) { + return Result.error("原密码错误"); + } + + if (userSourcePwd.equals(newPwd)) { + return Result.error("新密码不能与原密码相同"); + } + + String pwd = PwdUtil.doSm4CbcEncrypt(newPwd); + dao.update(Sys_user.class, Chain.make("password", pwd), Cnd.where("id", "=", ShiroUtil.getUserId())); + + sysUserService.deleteCacheAndUpdate(ShiroUtil.getUserId()); + return Result.success("修改成功"); + } } diff --git a/src/main/resources/views/mobile/home.html b/src/main/resources/views/mobile/home.html index 2b517a9..e69f390 100644 --- a/src/main/resources/views/mobile/home.html +++ b/src/main/resources/views/mobile/home.html @@ -164,9 +164,128 @@ layout("/mobile/platform.html"){ border-radius: 20px; } + .pwd_popup { + width: 96%; + height: auto; + border-radius: 20px; + } + .menu_item_name { margin-top: 6px; } + + .card { + /*border: 1px solid #007bff;*/ + /*background: #fff;*/ + border-radius: 8px; + /*box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);*/ + box-sizing: border-box; + position: relative; + } + .card-content { + padding: 30px; + } + h2 { + margin-top: 0; + color: #333; + font-size: 26px; + text-align: center; + } + .form-group { + position: relative; + margin-bottom: 20px; + } + label { + display: block; + margin-bottom: 10px; + color: #555; + font-size: 16px; + } + input[type="password"], + input[type="text"] { + width: 100%; + padding: 12px; + border: 1px solid #ddd; + border-radius: 50px; + font-size: 16px; + box-sizing: border-box; + transition: border-color 0.3s; + height: 40px; + } + input[type="password"]:focus, + input[type="text"]:focus { + border-color: #007bff; + outline: none; + } + button { + background-color: #e0f0ff; + color: #000; + border: 1px solid #007bff; + padding: 12px; + font-weight: bold; + border-radius: 50px; + cursor: pointer; + font-size: 16px; + width: 100%; + } + button:hover { + background-color: #87c4ed; + } + .note { + font-size: 16px; + color: #666; + padding: 30px; + } + .note ul { + padding-left: 0; + list-style-type: none; + } + .note li { + margin-bottom: 15px; + } + .card.modify-password { + flex: 7; + } + .card.note { + flex: 3; + } + .modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + align-items: center; + justify-content: center; + } + .modal-content { + background: #fff; + border-radius: 8px; + border: 1px solid #007bff; + padding: 30px; + text-align: center; + max-width: 400px; + width: 100%; + height: 150px; + display: flex; + align-items: center; + justify-content: center; + } + .modal-content h3 { + margin: 0; + } + .toggle-password { + position: absolute; + right: 10px; + top: 70%; + transform: translateY(-50%); + cursor: pointer; + user-select: none; + font-size: 26px; + color: #555; + }
@@ -182,6 +301,10 @@ layout("/mobile/platform.html"){