OAToSHR.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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 static ExpiringMapCache<String, String> redirectUrlCache = new ExpiringMapCache<>();
  34. private Properties prop = new Properties();
  35. private String propPath = System.getProperty("EAS_HOME") + "/server/properties/scy/OASSOConfig.properties";
  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. Random random = new Random();
  114. String key = System.currentTimeMillis() + String.valueOf(random.nextInt(99999));
  115. redirectUrlCache.put(key, redirectUrl,300000);
  116. logger.error("callBack redirectUrl" + redirectUrl);
  117. redirect_uri1 += "?redirect=" + key;
  118. Map params = new HashMap();
  119. params.put("client_id", client_id);
  120. params.put("response_type", response_type);
  121. params.put("redirect_uri", URLEncoder.encode(redirect_uri1, "UTF-8"));
  122. String urlString = appendUrl(authorizePath, params);
  123. resp.sendRedirect(urlString);
  124. logger.error("authorize url" + urlString);
  125. }
  126. /**
  127. * OA回调方法
  128. * 获取Token和用户信息,单点到shr
  129. *
  130. * @param req
  131. * @param resp
  132. * @param ticket
  133. * @throws SHRWebException
  134. * @throws UnsupportedEncodingException
  135. */
  136. public void callBack(HttpServletRequest req, HttpServletResponse resp, String ticket) throws
  137. SHRWebException, UnsupportedEncodingException {
  138. logger.error("callback方法入参");
  139. if (StringUtils.isEmpty(ticket)) {
  140. throw new RuntimeException("ticket不能为空!");
  141. }
  142. String getAccessTokenPath = prop.getProperty("getAccessTokenPath");
  143. if (StringUtils.isEmpty(getAccessTokenPath)) {
  144. throw new RuntimeException("getAccessTokenPath不能为空! 请检查配置文件: " + propPath);
  145. }
  146. String client_secret = prop.getProperty("client_secret");
  147. if (StringUtils.isEmpty(client_secret)) {
  148. throw new RuntimeException("client_secret不能为空! 请检查配置文件: " + propPath);
  149. }
  150. String client_id = prop.getProperty("client_id");
  151. if (StringUtils.isEmpty(client_id)) {
  152. throw new RuntimeException("client_id不能为空! 请检查配置文件: " + propPath);
  153. }
  154. String redirect_uri2 = prop.getProperty("redirect_uri2");
  155. if (StringUtils.isEmpty(redirect_uri2)) {
  156. throw new RuntimeException("redirect_uri2不能为空! 请检查配置文件: " + propPath);
  157. }
  158. String getLoginIdPath = prop.getProperty("getLoginIdPath");
  159. if (StringUtils.isEmpty(getLoginIdPath)) {
  160. throw new RuntimeException("getLoginIdPath不能为空! 请检查配置文件: " + propPath);
  161. }
  162. String redirectUrl = req.getParameter("redirect");
  163. logger.error("callBack redirectUrl" + redirectUrl);
  164. Map params = new HashMap();
  165. params.put("client_id", client_id);
  166. params.put("client_secret", client_secret);
  167. params.put("grant_type", "authorization_code");
  168. params.put("code", ticket);
  169. params.put("redirect_uri", URLEncoder.encode(redirect_uri2, "UTF-8"));
  170. try {
  171. String token = getAccessToken(getAccessTokenPath, params);
  172. logger.error(token);
  173. //从人员对象,获取纷享用户userId
  174. String loginId = loginId2userId(getLoginIdPath, token);
  175. String loginUrl = login(loginId, redirectUrl);
  176. resp.sendRedirect(loginUrl);
  177. } catch (Exception e) {
  178. e.printStackTrace();
  179. throw new RuntimeException(e.getMessage());
  180. }
  181. }
  182. /**
  183. * 获取携带账号和密码的登录地址
  184. *
  185. * @param userNumber
  186. * @param redirectUrlKey
  187. * @return
  188. */
  189. private String login(String userNumber, String redirectUrlKey) throws Exception {
  190. String password = LtpaTokenManager.generate(userNumber, LtpaTokenManager.getDefaultLtpaConfig()).toString();
  191. logger.error("login: password" + password);
  192. String serverName = prop.getProperty("serverName");
  193. if (StringUtils.isEmpty(serverName)) {
  194. throw new RuntimeException("serverName不能为空! 请检查配置文件: " + propPath);
  195. }
  196. StringBuilder url = new StringBuilder();
  197. url.append("/shr/index2sso.jsp?username=").append(userNumber)
  198. .append("&password=").append(password).append("&redirectTo=");
  199. StringBuilder redirectUrlStr = new StringBuilder();
  200. if (StringUtils.isEmpty(redirectUrlKey)) {
  201. redirectUrlStr.append(serverName).append("/shr/dynamic.do?uipk=shr.perself.homepage");
  202. } else {
  203. String redirectUrl = redirectUrlCache.get(redirectUrlKey);
  204. if (redirectUrl.contains(serverName)) {
  205. redirectUrlStr.append(redirectUrl);
  206. } else {
  207. redirectUrlStr.append(serverName).append(redirectUrl);
  208. }
  209. }
  210. String encode = URLEncoder.encode(redirectUrlStr.toString(), "UTF-8");
  211. url.append(URLEncoder.encode(encode, "UTF-8"));
  212. logger.error("login: url" + url);
  213. return url.toString();
  214. }
  215. /**
  216. * 获取泛微token方法
  217. *
  218. * @param getAccessTokenPath
  219. * @param params
  220. * @return
  221. * @throws IOException
  222. */
  223. private String getAccessToken(String getAccessTokenPath, Map<String, String> params)
  224. throws IOException {
  225. logger.error("getAccessToken方法参数: " + params);
  226. String url = appendUrl(getAccessTokenPath, params);
  227. logger.error("access_token url" + url);
  228. OkHttpClient client = new OkHttpClient();
  229. Request request = new Request.Builder()
  230. .url(url)
  231. .get()
  232. .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001")
  233. .build();
  234. Response response = client.newCall(request).execute();
  235. if (response.isSuccessful()) {
  236. String string = response.body().string();
  237. JSONObject jsonObject = JSONObject.parseObject(string);
  238. String code = jsonObject.getString("code");
  239. if ("0".equals(code)) {
  240. String access_token = jsonObject.getString("access_token");
  241. logger.error("access_token " + access_token);
  242. return access_token;
  243. } else {
  244. throw new RuntimeException(jsonObject.getString("msg"));
  245. }
  246. } else {
  247. //网络超时
  248. throw new RuntimeException("获取token超时");
  249. }
  250. }
  251. /**
  252. * 获取用户信息
  253. *
  254. * @param accessToken
  255. * @return
  256. * @throws IOException
  257. * @throws BOSException
  258. * @throws SQLException
  259. */
  260. private String loginId2userId(String getLoginIdPath, String accessToken)
  261. throws IOException, BOSException, SQLException {
  262. if (StringUtils.isEmpty(accessToken)) {
  263. throw new RuntimeException("accessToken不能为空! ");
  264. }
  265. //获取第三方用户信息
  266. Map params = new HashMap();
  267. params.put("access_token", accessToken);
  268. String url = appendUrl(getLoginIdPath, params);
  269. logger.error("loginId2userId url" + url);
  270. OkHttpClient client = new OkHttpClient();
  271. Request request = new Request.Builder()
  272. .url(url)
  273. .get()
  274. .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001")
  275. .build();
  276. Response response = client.newCall(request).execute();
  277. if (response.isSuccessful()) {
  278. String string = response.body().string();
  279. JSONObject jsonObject = JSONObject.parseObject(string);
  280. String code = jsonObject.getString("code");
  281. if ("0".equals(code)) {
  282. JSONObject attributes = jsonObject.getJSONObject("attributes");
  283. //登录id
  284. String loginid = attributes.getString("loginid");
  285. String dataCenter = CASLoginConfigPropUtil.getDataCenter();
  286. String locale = CASLoginConfigPropUtil.getLocale();
  287. if (!StringUtils.isEmpty(dataCenter) && !StringUtils.isEmpty(locale)) {
  288. Context ctx = CloudParamUtil.getContext(dataCenter, locale, "administrator");
  289. String sql = "SELECT count(1) total FROM T_PM_USER WHERE fnumber=?";
  290. IRowSet rs = DbUtil.executeQuery(ctx, sql, new Object[]{loginid});
  291. int total = 0;
  292. if (rs.next()) {
  293. total = rs.getInt("total");
  294. }
  295. if (total <= 0) {
  296. logger.error("SHR找不到对应的用户, loginid:" + loginid);
  297. throw new RuntimeException("SHR找不到对应的用户, loginid: " + loginid);
  298. //("您无权限访问SHR系统,请联系管理员处理。")
  299. } else if (total > 1) {
  300. logger.error("SHR找到多个对应的用户, loginid:" + loginid);
  301. throw new RuntimeException("SHR找到多个对应的用户, loginid: " + loginid);
  302. //("您无权限访问SHR系统,请联系管理员处理。")
  303. } else {
  304. return loginid;
  305. }
  306. }
  307. logger.error("获取用户信息报错,数据中心没找到!");
  308. throw new RuntimeException("获取用户信息报错,数据中心没找到!");
  309. } else {
  310. logger.error(jsonObject.getString("msg"));
  311. throw new RuntimeException(jsonObject.getString("msg"));
  312. }
  313. } else {
  314. //网络超时
  315. logger.error("网络超时");
  316. throw new RuntimeException("网络超时");
  317. }
  318. }
  319. /**
  320. * 拼接地址参数
  321. */
  322. private static String appendUrl(String url, Map<String, String> data) {
  323. logger.error("appendUrl_url: " + url);
  324. logger.error("appendUrl_data: " + data);
  325. StringBuilder paramStr = new StringBuilder();
  326. for (String key : data.keySet()) {
  327. paramStr.append(key).append("=").append(data.get(key)).append("&");
  328. }
  329. paramStr.deleteCharAt(paramStr.lastIndexOf("&"));
  330. String str = url.contains("?") ? (url + "&" + paramStr) : (url + "?" + paramStr);
  331. logger.error("拼接后的地址为:" + str);
  332. return str;
  333. }
  334. }