OAToSHR.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. package com.kingdee.eas.custom.sso;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.kingdee.bos.BOSException;
  5. import com.kingdee.bos.Context;
  6. import com.kingdee.eas.cp.eip.sso.ltpa.LtpaTokenManager;
  7. import com.kingdee.eas.cp.eip.sso.util.CASLoginConfigPropUtil;
  8. import com.kingdee.eas.cp.eip.sso.util.CloudParamUtil;
  9. import com.kingdee.eas.util.app.DbUtil;
  10. import com.kingdee.jdbc.rowset.IRowSet;
  11. import com.kingdee.shr.base.syssetting.exception.SHRWebException;
  12. import com.kingdee.util.StringUtils;
  13. import okhttp3.*;
  14. import org.apache.log4j.Logger;
  15. import javax.crypto.Cipher;
  16. import javax.crypto.SecretKey;
  17. import javax.crypto.spec.SecretKeySpec;
  18. import javax.servlet.ServletException;
  19. import javax.servlet.http.HttpServlet;
  20. import javax.servlet.http.HttpServletRequest;
  21. import javax.servlet.http.HttpServletResponse;
  22. import java.io.*;
  23. import java.net.URLEncoder;
  24. import java.sql.SQLException;
  25. import java.util.*;
  26. /**
  27. * @Description OA单点sHR
  28. * @Date 2024/10/30 11:22
  29. * @Created by Heyuan
  30. */
  31. public class OAToSHR extends HttpServlet {
  32. private static Logger logger = Logger.getLogger(OAToSHR.class);
  33. private Properties prop = new Properties();
  34. private String propPath = System.getProperty("EAS_HOME") + "/server/properties/scy/OASSOConfig.properties";
  35. private final String SECRETKEY = "jOK7MpIonY+/56ulnl6RGQ==";
  36. @Override
  37. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  38. throws ServletException, IOException {
  39. logger.error("OAToSHR -> doGet");
  40. doPost(req, resp);
  41. }
  42. @Override
  43. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  44. throws ServletException, IOException {
  45. logger.error("OAToSHR -> doPost");
  46. BufferedReader streamReader = null;
  47. String resultStr = null;
  48. prop.load(new FileInputStream(propPath));
  49. logger.error("OAToSHR requestUrl" + req.getRequestURL().toString());
  50. try {
  51. String ticket = req.getParameter("ticket");
  52. logger.error("接收到的请求参数是:ticket " + ticket);
  53. if (StringUtils.isEmpty(ticket)) {
  54. //认证
  55. authorize(req, resp);
  56. } else {
  57. //获取用户信息
  58. callBack(req, resp, ticket);
  59. }
  60. } catch (Exception e) {
  61. e.printStackTrace();
  62. Map result = new HashMap();
  63. result.put("msgType", "0");
  64. result.put("reason", e.getMessage());
  65. resultStr = JSON.toJSONString(result);
  66. resp.setStatus(500);
  67. PrintWriter writer = resp.getWriter();
  68. resp.setContentType("application/json");
  69. writer.write(resultStr);
  70. writer.close();
  71. } finally {
  72. try {
  73. if (streamReader != null) {
  74. streamReader.close();
  75. }
  76. } catch (Exception e) {
  77. e.printStackTrace();
  78. }
  79. }
  80. }
  81. /**
  82. * 认证
  83. * 拼接OA认证接口地址,转发
  84. *
  85. * @param resp
  86. * @throws Exception
  87. */
  88. public void authorize(HttpServletRequest req, HttpServletResponse resp) throws Exception {
  89. String authorizePath = prop.getProperty("authorizePath");
  90. if (StringUtils.isEmpty(authorizePath)) {
  91. throw new RuntimeException("authorizePath不能为空! 请检查配置文件: " + propPath);
  92. }
  93. String response_type = prop.getProperty("response_type");
  94. if (StringUtils.isEmpty(response_type)) {
  95. throw new RuntimeException("response_type不能为空! 请检查配置文件: " + propPath);
  96. }
  97. String client_id = prop.getProperty("client_id");
  98. if (StringUtils.isEmpty(client_id)) {
  99. throw new RuntimeException("client_id不能为空! 请检查配置文件: " + propPath);
  100. }
  101. String redirect_uri1 = prop.getProperty("redirect_uri1");
  102. if (StringUtils.isEmpty(redirect_uri1)) {
  103. throw new RuntimeException("redirect_uri1不能为空! 请检查配置文件: " + propPath);
  104. }
  105. String redirectUrl = req.getParameter("redirect");
  106. if (StringUtils.isEmpty(redirectUrl)) {
  107. String serverName = prop.getProperty("serverName");
  108. if (StringUtils.isEmpty(serverName)) {
  109. throw new RuntimeException("serverName不能为空! 请检查配置文件: " + propPath);
  110. }
  111. redirectUrl = serverName + "/shr/dynamic.do?uipk=shr.perself.homepage";
  112. }
  113. logger.error("callBack redirectUrl" + redirectUrl);
  114. String encrypt = encrypt(redirectUrl, SECRETKEY);
  115. logger.error("callBack encrypt" + encrypt);
  116. redirect_uri1 += "?redirect=" + URLEncoder.encode(encrypt, "UTF-8");
  117. Map params = new HashMap();
  118. params.put("client_id", client_id);
  119. params.put("response_type", response_type);
  120. params.put("redirect_uri", URLEncoder.encode(redirect_uri1, "UTF-8"));
  121. String urlString = appendUrl(authorizePath, params);
  122. resp.sendRedirect(urlString);
  123. logger.error("authorize url" + urlString);
  124. }
  125. /**
  126. * OA回调方法
  127. * 获取Token和用户信息,单点到shr
  128. *
  129. * @param req
  130. * @param resp
  131. * @param ticket
  132. * @throws SHRWebException
  133. * @throws UnsupportedEncodingException
  134. */
  135. public void callBack(HttpServletRequest req, HttpServletResponse resp, String ticket) throws
  136. SHRWebException, UnsupportedEncodingException {
  137. logger.error("callback方法入参");
  138. if (StringUtils.isEmpty(ticket)) {
  139. throw new RuntimeException("ticket不能为空!");
  140. }
  141. String getAccessTokenPath = prop.getProperty("getAccessTokenPath");
  142. if (StringUtils.isEmpty(getAccessTokenPath)) {
  143. throw new RuntimeException("getAccessTokenPath不能为空! 请检查配置文件: " + propPath);
  144. }
  145. String client_secret = prop.getProperty("client_secret");
  146. if (StringUtils.isEmpty(client_secret)) {
  147. throw new RuntimeException("client_secret不能为空! 请检查配置文件: " + propPath);
  148. }
  149. String client_id = prop.getProperty("client_id");
  150. if (StringUtils.isEmpty(client_id)) {
  151. throw new RuntimeException("client_id不能为空! 请检查配置文件: " + propPath);
  152. }
  153. String redirect_uri2 = prop.getProperty("redirect_uri2");
  154. if (StringUtils.isEmpty(redirect_uri2)) {
  155. throw new RuntimeException("redirect_uri2不能为空! 请检查配置文件: " + propPath);
  156. }
  157. String getLoginIdPath = prop.getProperty("getLoginIdPath");
  158. if (StringUtils.isEmpty(getLoginIdPath)) {
  159. throw new RuntimeException("getLoginIdPath不能为空! 请检查配置文件: " + propPath);
  160. }
  161. String redirectUrl = req.getParameter("redirect");
  162. logger.error("callBack redirectUrl" + redirectUrl);
  163. Map params = new HashMap();
  164. params.put("client_id", client_id);
  165. params.put("client_secret", client_secret);
  166. params.put("grant_type", "authorization_code");
  167. params.put("code", ticket);
  168. params.put("redirect_uri", URLEncoder.encode(redirect_uri2, "UTF-8"));
  169. try {
  170. String token = getAccessToken(getAccessTokenPath, params);
  171. logger.error(token);
  172. //从人员对象,获取纷享用户userId
  173. String loginId = loginId2userId(getLoginIdPath, token);
  174. String loginUrl = login(loginId, redirectUrl);
  175. resp.sendRedirect(loginUrl);
  176. } catch (Exception e) {
  177. e.printStackTrace();
  178. throw new RuntimeException(e.getMessage());
  179. }
  180. }
  181. /**
  182. * 获取携带账号和密码的登录地址
  183. *
  184. * @param userNumber
  185. * @param redirectUrl
  186. * @return
  187. */
  188. private String login(String userNumber, String redirectUrl) throws Exception {
  189. String password = LtpaTokenManager.generate(userNumber, LtpaTokenManager.getDefaultLtpaConfig()).toString();
  190. logger.error("login: password" + password);
  191. String serverName = prop.getProperty("serverName");
  192. if (StringUtils.isEmpty(serverName)) {
  193. throw new RuntimeException("serverName不能为空! 请检查配置文件: " + propPath);
  194. }
  195. StringBuilder url = new StringBuilder();
  196. url.append("/shr/index2sso.jsp?username=").append(userNumber)
  197. .append("&password=").append(password).append("&redirectTo=");
  198. StringBuilder redirectUrlStr = new StringBuilder();
  199. if (StringUtils.isEmpty(redirectUrl)) {
  200. redirectUrlStr.append(serverName).append("/shr/dynamic.do?uipk=shr.perself.homepage");
  201. } else {
  202. //解密
  203. String decrpt = decrypt(redirectUrl, SECRETKEY);
  204. if (decrpt.contains(serverName)) {
  205. redirectUrlStr.append(decrpt);
  206. } else {
  207. redirectUrlStr.append(serverName).append(decrpt);
  208. }
  209. }
  210. url.append(URLEncoder.encode(redirectUrlStr.toString(), "UTF-8"));
  211. logger.error("login: url" + url);
  212. return url.toString();
  213. }
  214. /**
  215. * 获取泛微token方法
  216. *
  217. * @param getAccessTokenPath
  218. * @param params
  219. * @return
  220. * @throws IOException
  221. */
  222. private String getAccessToken(String getAccessTokenPath, Map<String, String> params)
  223. throws IOException {
  224. logger.error("getAccessToken方法参数: " + params);
  225. String url = appendUrl(getAccessTokenPath, params);
  226. logger.error("access_token url" + url);
  227. OkHttpClient client = new OkHttpClient();
  228. Request request = new Request.Builder()
  229. .url(url)
  230. .get()
  231. .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001")
  232. .build();
  233. Response response = client.newCall(request).execute();
  234. if (response.isSuccessful()) {
  235. String string = response.body().string();
  236. JSONObject jsonObject = JSONObject.parseObject(string);
  237. String code = jsonObject.getString("code");
  238. if ("0".equals(code)) {
  239. String access_token = jsonObject.getString("access_token");
  240. logger.error("access_token " + access_token);
  241. return access_token;
  242. } else {
  243. throw new RuntimeException(jsonObject.getString("msg"));
  244. }
  245. } else {
  246. //网络超时
  247. throw new RuntimeException("获取token超时");
  248. }
  249. }
  250. /**
  251. * 获取用户信息
  252. *
  253. * @param accessToken
  254. * @return
  255. * @throws IOException
  256. * @throws BOSException
  257. * @throws SQLException
  258. */
  259. private String loginId2userId(String getLoginIdPath, String accessToken)
  260. throws IOException, BOSException, SQLException {
  261. if (StringUtils.isEmpty(accessToken)) {
  262. throw new RuntimeException("accessToken不能为空! ");
  263. }
  264. //获取第三方用户信息
  265. Map params = new HashMap();
  266. params.put("access_token", accessToken);
  267. String url = appendUrl(getLoginIdPath, params);
  268. logger.error("loginId2userId url" + url);
  269. OkHttpClient client = new OkHttpClient();
  270. Request request = new Request.Builder()
  271. .url(url)
  272. .get()
  273. .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001")
  274. .build();
  275. Response response = client.newCall(request).execute();
  276. if (response.isSuccessful()) {
  277. String string = response.body().string();
  278. JSONObject jsonObject = JSONObject.parseObject(string);
  279. String code = jsonObject.getString("code");
  280. if ("0".equals(code)) {
  281. JSONObject attributes = jsonObject.getJSONObject("attributes");
  282. //登录id
  283. String loginid = attributes.getString("loginid");
  284. String dataCenter = CASLoginConfigPropUtil.getDataCenter();
  285. String locale = CASLoginConfigPropUtil.getLocale();
  286. if (!StringUtils.isEmpty(dataCenter) && !StringUtils.isEmpty(locale)) {
  287. Context ctx = CloudParamUtil.getContext(dataCenter, locale, "administrator");
  288. String sql = "SELECT count(1) total FROM T_PM_USER WHERE fnumber=?";
  289. IRowSet rs = DbUtil.executeQuery(ctx, sql, new Object[]{loginid});
  290. int total = 0;
  291. if (rs.next()) {
  292. total = rs.getInt("total");
  293. }
  294. if (total <= 0) {
  295. logger.error("SHR找不到对应的用户, loginid:" + loginid);
  296. throw new RuntimeException("SHR找不到对应的用户, loginid: " + loginid);
  297. //("您无权限访问SHR系统,请联系管理员处理。")
  298. } else if (total > 1) {
  299. logger.error("SHR找到多个对应的用户, loginid:" + loginid);
  300. throw new RuntimeException("SHR找到多个对应的用户, loginid: " + loginid);
  301. //("您无权限访问SHR系统,请联系管理员处理。")
  302. } else {
  303. return loginid;
  304. }
  305. }
  306. logger.error("获取用户信息报错,数据中心没找到!");
  307. throw new RuntimeException("获取用户信息报错,数据中心没找到!");
  308. } else {
  309. logger.error(jsonObject.getString("msg"));
  310. throw new RuntimeException(jsonObject.getString("msg"));
  311. }
  312. } else {
  313. //网络超时
  314. logger.error("网络超时");
  315. throw new RuntimeException("网络超时");
  316. }
  317. }
  318. /**
  319. * 拼接地址参数
  320. */
  321. private static String appendUrl(String url, Map<String, String> data) {
  322. logger.error("appendUrl_url: " + url);
  323. logger.error("appendUrl_data: " + data);
  324. StringBuilder paramStr = new StringBuilder();
  325. for (String key : data.keySet()) {
  326. paramStr.append(key).append("=").append(data.get(key)).append("&");
  327. }
  328. paramStr.deleteCharAt(paramStr.lastIndexOf("&"));
  329. String str = url.contains("?") ? (url + "&" + paramStr) : (url + "?" + paramStr);
  330. logger.error("拼接后的地址为:" + str);
  331. return str;
  332. }
  333. // 使用 AES 加密
  334. public static String encrypt(String plainText, String encodedKey) throws Exception {
  335. // 将Base64字符串解码为字节数组
  336. byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
  337. // 使用字节数组创建一个AES密钥
  338. SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
  339. Cipher cipher = Cipher.getInstance("AES");
  340. cipher.init(Cipher.ENCRYPT_MODE, secretKey);
  341. byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
  342. return Base64.getEncoder().encodeToString(encryptedBytes);
  343. }
  344. // 使用 AES 解密
  345. public static String decrypt(String encryptedText, String encodedKey) throws Exception {
  346. // 将Base64字符串解码为字节数组
  347. byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
  348. // 使用字节数组创建一个AES密钥
  349. SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
  350. Cipher cipher = Cipher.getInstance("AES");
  351. cipher.init(Cipher.DECRYPT_MODE, secretKey);
  352. byte[] decodedBytes = Base64.getDecoder().decode(encryptedText);
  353. byte[] decryptedBytes = cipher.doFinal(decodedBytes);
  354. return new String(decryptedBytes, "UTF-8");
  355. }
  356. }