package com.kingdee.eas.custom.sso; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.kingdee.bos.BOSException; import com.kingdee.bos.Context; import com.kingdee.eas.cp.eip.sso.ltpa.LtpaTokenManager; import com.kingdee.eas.cp.eip.sso.util.CASLoginConfigPropUtil; import com.kingdee.eas.cp.eip.sso.util.CloudParamUtil; import com.kingdee.eas.util.app.DbUtil; import com.kingdee.jdbc.rowset.IRowSet; import com.kingdee.shr.base.syssetting.exception.SHRWebException; import com.kingdee.util.StringUtils; import okhttp3.*; import org.apache.log4j.Logger; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.sql.SQLException; import java.util.*; /** * @Description OA单点sHR * @Date 2024/10/30 11:22 * @Created by Heyuan */ public class OAToSHR extends HttpServlet { private static Logger logger = Logger.getLogger(OAToSHR.class); private Properties prop = new Properties(); private String propPath = System.getProperty("EAS_HOME") + "/server/properties/scy/OASSOConfig.properties"; private final String SECRETKEY = "jOK7MpIonY+/56ulnl6RGQ=="; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { logger.error("OAToSHR -> doGet"); doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { logger.error("OAToSHR -> doPost"); BufferedReader streamReader = null; String resultStr = null; prop.load(new FileInputStream(propPath)); logger.error("OAToSHR requestUrl" + req.getRequestURL().toString()); try { String ticket = req.getParameter("ticket"); logger.error("接收到的请求参数是:ticket " + ticket); if (StringUtils.isEmpty(ticket)) { //认证 authorize(req, resp); } else { //获取用户信息 callBack(req, resp, ticket); } } catch (Exception e) { e.printStackTrace(); Map result = new HashMap(); result.put("msgType", "0"); result.put("reason", e.getMessage()); resultStr = JSON.toJSONString(result); resp.setStatus(500); PrintWriter writer = resp.getWriter(); resp.setContentType("application/json"); writer.write(resultStr); writer.close(); } finally { try { if (streamReader != null) { streamReader.close(); } } catch (Exception e) { e.printStackTrace(); } } } /** * 认证 * 拼接OA认证接口地址,转发 * * @param resp * @throws Exception */ public void authorize(HttpServletRequest req, HttpServletResponse resp) throws Exception { String authorizePath = prop.getProperty("authorizePath"); if (StringUtils.isEmpty(authorizePath)) { throw new RuntimeException("authorizePath不能为空! 请检查配置文件: " + propPath); } String response_type = prop.getProperty("response_type"); if (StringUtils.isEmpty(response_type)) { throw new RuntimeException("response_type不能为空! 请检查配置文件: " + propPath); } String client_id = prop.getProperty("client_id"); if (StringUtils.isEmpty(client_id)) { throw new RuntimeException("client_id不能为空! 请检查配置文件: " + propPath); } String redirect_uri1 = prop.getProperty("redirect_uri1"); if (StringUtils.isEmpty(redirect_uri1)) { throw new RuntimeException("redirect_uri1不能为空! 请检查配置文件: " + propPath); } String redirectUrl = req.getParameter("redirect"); if (StringUtils.isEmpty(redirectUrl)) { String serverName = prop.getProperty("serverName"); if (StringUtils.isEmpty(serverName)) { throw new RuntimeException("serverName不能为空! 请检查配置文件: " + propPath); } redirectUrl = serverName + "/shr/dynamic.do?uipk=shr.perself.homepage"; } logger.error("callBack redirectUrl" + redirectUrl); String encrypt = encrypt(redirectUrl, SECRETKEY); logger.error("callBack encrypt" + encrypt); redirect_uri1 += "?redirect=" + URLEncoder.encode(encrypt, "UTF-8"); Map params = new HashMap(); params.put("client_id", client_id); params.put("response_type", response_type); params.put("redirect_uri", URLEncoder.encode(redirect_uri1, "UTF-8")); String urlString = appendUrl(authorizePath, params); resp.sendRedirect(urlString); logger.error("authorize url" + urlString); } /** * OA回调方法 * 获取Token和用户信息,单点到shr * * @param req * @param resp * @param ticket * @throws SHRWebException * @throws UnsupportedEncodingException */ public void callBack(HttpServletRequest req, HttpServletResponse resp, String ticket) throws SHRWebException, UnsupportedEncodingException { logger.error("callback方法入参"); if (StringUtils.isEmpty(ticket)) { throw new RuntimeException("ticket不能为空!"); } String getAccessTokenPath = prop.getProperty("getAccessTokenPath"); if (StringUtils.isEmpty(getAccessTokenPath)) { throw new RuntimeException("getAccessTokenPath不能为空! 请检查配置文件: " + propPath); } String client_secret = prop.getProperty("client_secret"); if (StringUtils.isEmpty(client_secret)) { throw new RuntimeException("client_secret不能为空! 请检查配置文件: " + propPath); } String client_id = prop.getProperty("client_id"); if (StringUtils.isEmpty(client_id)) { throw new RuntimeException("client_id不能为空! 请检查配置文件: " + propPath); } String redirect_uri2 = prop.getProperty("redirect_uri2"); if (StringUtils.isEmpty(redirect_uri2)) { throw new RuntimeException("redirect_uri2不能为空! 请检查配置文件: " + propPath); } String getLoginIdPath = prop.getProperty("getLoginIdPath"); if (StringUtils.isEmpty(getLoginIdPath)) { throw new RuntimeException("getLoginIdPath不能为空! 请检查配置文件: " + propPath); } String redirectUrl = req.getParameter("redirect"); logger.error("callBack redirectUrl" + redirectUrl); Map params = new HashMap(); params.put("client_id", client_id); params.put("client_secret", client_secret); params.put("grant_type", "authorization_code"); params.put("code", ticket); params.put("redirect_uri", URLEncoder.encode(redirect_uri2, "UTF-8")); try { String token = getAccessToken(getAccessTokenPath, params); logger.error(token); //从人员对象,获取纷享用户userId String loginId = loginId2userId(getLoginIdPath, token); String loginUrl = login(loginId, redirectUrl); resp.sendRedirect(loginUrl); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } } /** * 获取携带账号和密码的登录地址 * * @param userNumber * @param redirectUrl * @return */ private String login(String userNumber, String redirectUrl) throws Exception { String password = LtpaTokenManager.generate(userNumber, LtpaTokenManager.getDefaultLtpaConfig()).toString(); logger.error("login: password" + password); String serverName = prop.getProperty("serverName"); if (StringUtils.isEmpty(serverName)) { throw new RuntimeException("serverName不能为空! 请检查配置文件: " + propPath); } StringBuilder url = new StringBuilder(); url.append("/shr/index2sso.jsp?username=").append(userNumber) .append("&password=").append(password).append("&redirectTo="); StringBuilder redirectUrlStr = new StringBuilder(); if (StringUtils.isEmpty(redirectUrl)) { redirectUrlStr.append(serverName).append("/shr/dynamic.do?uipk=shr.perself.homepage"); } else { //解密 String decrpt = decrypt(redirectUrl, SECRETKEY); if (decrpt.contains(serverName)) { redirectUrlStr.append(decrpt); } else { redirectUrlStr.append(serverName).append(decrpt); } } url.append(URLEncoder.encode(redirectUrlStr.toString(), "UTF-8")); logger.error("login: url" + url); return url.toString(); } /** * 获取泛微token方法 * * @param getAccessTokenPath * @param params * @return * @throws IOException */ private String getAccessToken(String getAccessTokenPath, Map params) throws IOException { logger.error("getAccessToken方法参数: " + params); String url = appendUrl(getAccessTokenPath, params); logger.error("access_token url" + url); OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(url) .get() .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001") .build(); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { String string = response.body().string(); JSONObject jsonObject = JSONObject.parseObject(string); String code = jsonObject.getString("code"); if ("0".equals(code)) { String access_token = jsonObject.getString("access_token"); logger.error("access_token " + access_token); return access_token; } else { throw new RuntimeException(jsonObject.getString("msg")); } } else { //网络超时 throw new RuntimeException("获取token超时"); } } /** * 获取用户信息 * * @param accessToken * @return * @throws IOException * @throws BOSException * @throws SQLException */ private String loginId2userId(String getLoginIdPath, String accessToken) throws IOException, BOSException, SQLException { if (StringUtils.isEmpty(accessToken)) { throw new RuntimeException("accessToken不能为空! "); } //获取第三方用户信息 Map params = new HashMap(); params.put("access_token", accessToken); String url = appendUrl(getLoginIdPath, params); logger.error("loginId2userId url" + url); OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(url) .get() .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001") .build(); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { String string = response.body().string(); JSONObject jsonObject = JSONObject.parseObject(string); String code = jsonObject.getString("code"); if ("0".equals(code)) { JSONObject attributes = jsonObject.getJSONObject("attributes"); //登录id String loginid = attributes.getString("loginid"); String dataCenter = CASLoginConfigPropUtil.getDataCenter(); String locale = CASLoginConfigPropUtil.getLocale(); if (!StringUtils.isEmpty(dataCenter) && !StringUtils.isEmpty(locale)) { Context ctx = CloudParamUtil.getContext(dataCenter, locale, "administrator"); String sql = "SELECT count(1) total FROM T_PM_USER WHERE fnumber=?"; IRowSet rs = DbUtil.executeQuery(ctx, sql, new Object[]{loginid}); int total = 0; if (rs.next()) { total = rs.getInt("total"); } if (total <= 0) { logger.error("SHR找不到对应的用户, loginid:" + loginid); throw new RuntimeException("SHR找不到对应的用户, loginid: " + loginid); //("您无权限访问SHR系统,请联系管理员处理。") } else if (total > 1) { logger.error("SHR找到多个对应的用户, loginid:" + loginid); throw new RuntimeException("SHR找到多个对应的用户, loginid: " + loginid); //("您无权限访问SHR系统,请联系管理员处理。") } else { return loginid; } } logger.error("获取用户信息报错,数据中心没找到!"); throw new RuntimeException("获取用户信息报错,数据中心没找到!"); } else { logger.error(jsonObject.getString("msg")); throw new RuntimeException(jsonObject.getString("msg")); } } else { //网络超时 logger.error("网络超时"); throw new RuntimeException("网络超时"); } } /** * 拼接地址参数 */ private static String appendUrl(String url, Map data) { logger.error("appendUrl_url: " + url); logger.error("appendUrl_data: " + data); StringBuilder paramStr = new StringBuilder(); for (String key : data.keySet()) { paramStr.append(key).append("=").append(data.get(key)).append("&"); } paramStr.deleteCharAt(paramStr.lastIndexOf("&")); String str = url.contains("?") ? (url + "&" + paramStr) : (url + "?" + paramStr); logger.error("拼接后的地址为:" + str); return str; } // 使用 AES 加密 public static String encrypt(String plainText, String encodedKey) throws Exception { // 将Base64字符串解码为字节数组 byte[] decodedKey = Base64.getDecoder().decode(encodedKey); // 使用字节数组创建一个AES密钥 SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8")); return Base64.getEncoder().encodeToString(encryptedBytes); } // 使用 AES 解密 public static String decrypt(String encryptedText, String encodedKey) throws Exception { // 将Base64字符串解码为字节数组 byte[] decodedKey = Base64.getDecoder().decode(encodedKey); // 使用字节数组创建一个AES密钥 SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decodedBytes = Base64.getDecoder().decode(encryptedText); byte[] decryptedBytes = cipher.doFinal(decodedBytes); return new String(decryptedBytes, "UTF-8"); } }