first commit

This commit is contained in:
2026-09-23 09:38:39 +08:00
commit 98a8b8cace
6257 changed files with 1266718 additions and 0 deletions
@@ -0,0 +1,167 @@
package io.v.nutz.wechat;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import io.v.nutz.base.result.Result;
import io.v.nutz.base.utils.LoginUtil;
import io.v.nutz.sys.models.Sys_user;
import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.task.model.TaskToken;
import io.v.nutz.web.commons.base.Globals;
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.token.PlatformCaptchaToken;
import io.v.nutz.web.commons.slog.annotation.SLog;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.eclipse.jetty.server.session.Session;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.http.Http;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.Serializable;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.sql.Struct;
/*
"/dd3s/mobile/union"是针对于南京理工大学特定的
因为移动端获取工号和其他学校不一样
原理是移动端访问/dd3s/mobile/union/getUserInfo,被微信认证集成平台反向代理拦截,拦截后获取到工号再通过转发的方式给我们
工号信息就放在request头部信息中
因为第三方公司说路径最好加个特殊标识,所以加了dd3s
*/
@IocBean
@Slf4j
@At({"/weChat/union", "/dd3s/mobile/union"})
public class WeChatLoginController {
private static final String seckey = "zhghDap863vth6k1t";
private static final String taskType = "企业微信";
private static String APPID = "wxf2f6f120053cf5eb";
private static String accessTokenUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s";
private static String oauthUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
@Inject
private Dao dao;
@Inject
private SysUserService sysUserService;
@Inject
private LoginUtil loginUtil;
public static String aesDecodeAccountInfo(String key, String hexAccount) throws Exception {
MessageDigest md5Encoder = MessageDigest.getInstance("MD5");
byte[] accountSrcBytes = Hex.decodeHex(hexAccount.toCharArray());
byte[] md5Key = md5Encoder.digest(key.getBytes());
md5Encoder.reset();
md5Encoder.update(md5Key);
md5Encoder.update(key.getBytes());
byte[] iv = md5Encoder.digest();
SecretKey secretKey = new SecretKeySpec(md5Key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] accountDstBytes = cipher.doFinal(accountSrcBytes);
return new String(accountDstBytes);
}
@At("/login")
@Ok("re")
public String login() {
String redirect_url = Globals.AppDomain + "/dd3s/mobile/union/getUserInfo";
return ">>:" + redirect_url;
}
@At("/getUserInfo")
@Ok("re")
public String getUserInfo(HttpServletRequest request, HttpSession session) throws Exception {
try {
String encryptUser = request.getHeader("EncryptUser");
log.debug("encryptUser为:" + encryptUser);
String resultStr = aesDecodeAccountInfo(seckey, encryptUser);
String loginName = resultStr.split(",")[0];
log.debug("企业微信获取的工号为:" + loginName);
return ">>:" + Globals.AppDomain + "/dd3s/mobile/union/toLogin?loginName=" + loginName;
} catch (Exception e) {
log.error("企业微信登录失败:" + e.getMessage());
request.setAttribute("errMsg", "系统中没有此用户");
return ">>:" + Globals.AppDomain + "/platform/home/UnknownAccountError";
}
}
@At("/toLogin")
@Ok("re")
public String toLogin(String loginName, HttpServletRequest request, HttpSession session) {
try {
loginUtil.doLogin(loginName, request, session, LoginUtil.LoginOrigin.QI_YE_WEI_XIN);
return ">>:" + Globals.AppDomain + "/mobile/index";
} catch (LockedAccountException e) {
log.error("企业微信登录失败:" + e.getMessage());
request.setAttribute("errMsg", "帐号被锁定,请5分钟后再试");
return ">>:" + Globals.AppDomain + "/platform/home/LockedAccountError";
} catch (UnknownAccountException e) {
log.error("企业微信登录失败:" + e.getMessage());
request.setAttribute("errMsg", "系统中没有此用户");
return ">>:" + Globals.AppDomain + "/platform/home/UnknownAccountError";
} catch (Exception e) {
log.error("企业微信登录失败:" + e.getMessage());
request.setAttribute("errMsg", "用户名或密码错误");
return ">>:" + Globals.AppDomain + "/platform/home/500";
}
}
@At
@Ok("re")
public String getCode(@Param("code") String code, HttpSession session) {
String token = getAccessToken();
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=" + token + "&code=" + code;
NutMap resMap = Json.fromJson(NutMap.class, HttpUtil.get(url));
String loginname = resMap.getString("UserId");
Sys_user user = sysUserService.fetch(Cnd.where("loginname", "=", loginname));
session.setAttribute("user", user);
session.setAttribute("userId", user.getId());
return ">>:/mobile/doLogin?username=" + loginname + "&password=dd3s@#2022&platformCaptcha=";
}
private String getAccessToken() {
TaskToken taskToken = dao.fetch(TaskToken.class, Cnd.where("token_type", "=", taskType));
if (Lang.isNotEmpty(taskToken) && System.currentTimeMillis() / 1000 < (taskToken.getGet_time() + taskToken.getExpires_in() - 200)) {
return taskToken.getToken();
}
accessTokenUrl = accessTokenUrl.formatted("wxf2f6f120053cf5eb", "eTGvDbfP3CpNibTNfpOOgIyv4KgRmJL7c7IDNXPD-kM");
String access_token = Json.fromJson(NutMap.class, Http.get(accessTokenUrl).getContent()).getString("access_token");
TaskToken t = new TaskToken();
t.setToken(access_token);
t.setExpires_in(7200);
t.setToken_type(taskType);
t.setGet_time(System.currentTimeMillis() / 1000);
dao.clear("task_token", Cnd.where("token_type", "=", taskType));
dao.insert("task_token", Chain.from(t));
return access_token;
}
}