diff --git a/JSD-7639-5860-需求文档V1.docx b/JSD-7639-5860-需求文档V1.docx new file mode 100644 index 0000000..06a4f12 Binary files /dev/null and b/JSD-7639-5860-需求文档V1.docx differ diff --git a/README.md b/README.md index e10b96c..9d1e2e5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # open-JSD-7639 -JSD-7639、JSD-5860 开源任务代码 \ No newline at end of file +JSD-7639、JSD-5860 开源任务代码\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途! \ No newline at end of file diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..76bb1a3 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,17 @@ + + + com.fr.plugin.jsd3998.sso + + yes + 1.9 + 10.0 + 2018-07-31 + fr.open + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/jsd3998/sso/DesECBUtil.java b/src/main/java/com/fr/plugin/jsd3998/sso/DesECBUtil.java new file mode 100644 index 0000000..65c3604 --- /dev/null +++ b/src/main/java/com/fr/plugin/jsd3998/sso/DesECBUtil.java @@ -0,0 +1,64 @@ +package com.fr.plugin.jsd3998.sso; + +import com.fr.third.org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.security.Key; + +/** + * @author Holger + * @date 2019/1/18 + */ +public class DesECBUtil { + /** + * 加密数据 + * + * @param encryptString + * @param encryptKey + * @return + * @throws Exception + */ + public static String encryptDES(String encryptString, String encryptKey) throws Exception { + Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(getKey(encryptKey), "DES")); + byte[] encryptedData = cipher.doFinal(encryptString.getBytes("gb2312")); + return Base64.encodeBase64String(encryptedData); + } + + /** + * key 不足8位补位 + * + * @param + */ + public static byte[] getKey(String keyRule) { + Key key = null; + byte[] keyByte = keyRule.getBytes(); + // 创建一个空的八位数组,默认情况下为0 + byte[] byteTemp = new byte[8]; + // 将用户指定的规则转换成八位数组 + for (int i = 0; i < byteTemp.length && i < keyByte.length; i++) { + byteTemp[i] = keyByte[i]; + } + key = new SecretKeySpec(byteTemp, "DES"); + return key.getEncoded(); + } + + /*** + * 解密数据 + * @param decryptString + * @param decryptKey + * @return + * @throws Exception + */ + + public static String decryptDES(String decryptString, String decryptKey) throws Exception { + byte[] sourceBytes = Base64.decodeBase64(decryptString); + Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(getKey(decryptKey), "DES")); + byte[] decoded = cipher.doFinal(sourceBytes); + return new String(decoded, "UTF-8"); + + } +} + diff --git a/src/main/java/com/fr/plugin/jsd3998/sso/H5SsoHandler.java b/src/main/java/com/fr/plugin/jsd3998/sso/H5SsoHandler.java new file mode 100644 index 0000000..ca5a869 --- /dev/null +++ b/src/main/java/com/fr/plugin/jsd3998/sso/H5SsoHandler.java @@ -0,0 +1,113 @@ +package com.fr.plugin.jsd3998.sso; + +import com.fr.base.PropertiesUtils; +import com.fr.decision.authority.data.User; +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.decision.webservice.exception.user.UserNotExistException; +import com.fr.decision.webservice.utils.DecisionServiceConstants; +import com.fr.decision.webservice.v10.login.LoginService; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; +import com.fr.third.org.apache.commons.lang3.math.NumberUtils; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Calendar; +import java.util.Properties; + +// @EnableMetrics +public class H5SsoHandler extends BaseHttpHandler { + + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return "/h5/callback"; + } + + @Override + public boolean isPublic() { + return true; + } + + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws Exception { + Properties props = PropertiesUtils.getProperties("sso"); + String encryptKey = props.getProperty("encrypt.key"); + Long ticketTimeoutInSeconds = NumberUtils.createLong(props.getProperty("ticket.timeoutInSeconds", "300")); + String ticket = request.getParameter("ticket"); + FineLoggerFactory.getLogger().info("获取到的ticket值为[{}]", ticket); + if (StringUtils.isNotBlank(ticket)) { + ticket = DesECBUtil.decryptDES(ticket, encryptKey); + FineLoggerFactory.getLogger().info("解密后的ticket值为[{}]", ticket); + String[] tickets = ticket.split("\\+"); + String username = tickets[0]; + FineLoggerFactory.getLogger().info("获取到username为[{}]", username); + Long ticketTime = NumberUtils.createLong(tickets[1]); + Long nowTime = Calendar.getInstance().getTimeInMillis(); + JSONObject res = new JSONObject(); + if ((nowTime - ticketTime) > ticketTimeoutInSeconds * 1000) { + FineLoggerFactory.getLogger().info("ticket[{}]已过期", ticket); + request.getRequestDispatcher("/login.html").forward(request, response); + res.put("res", "ticket已过期"); + WebUtils.printAsJSON(response, res); + return; + } + if (loginFromToken(request, response, username)) { + request.getRequestDispatcher( "/decision/url/mobile").forward(request, response); + } else { + res.put("res", "fail"); + WebUtils.printAsJSON(response, res); + } + } else { + FineLoggerFactory.getLogger().info("未找到用户名,跳转到登录页面"); + request.getRequestDispatcher("/login.html").forward(request, response); + } + } + + protected boolean loginFromToken(HttpServletRequest req, HttpServletResponse res, String username) throws Exception { + try { + if (com.fr.stable.StringUtils.isNotEmpty(username)) { + FineLoggerFactory.getLogger().info("current username:" + username); + User user = UserService.getInstance().getUserByUserName(username); + FineLoggerFactory.getLogger().info("get user:" + user); + if (user == null) { + throw new UserNotExistException(); + } + String token = LoginService.getInstance().login(req, res, username); + FineLoggerFactory.getLogger().info("get login token:" + token); + req.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token); + FineLoggerFactory.getLogger().info("username:" + username + " login success"); + return true; + } else { + FineLoggerFactory.getLogger().warn("username is null!"); + return false; + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return false; + } + +// public static void main(String... args) throws Exception { +// String ticket = URLDecoder.decode("7O%2Fyt%2FGeLqg8fxjY4Lm8%2F31FkSgLZh3a", "utf-8"); +// System.out.println(ticket); +// ticket = DesECBUtil.decryptDES(ticket, "1111111111111111111"); +// System.out.println(ticket); +// String[] tickets = ticket.split("\\+"); +// String username = tickets[0]; +// System.out.println(username); +// +// +// String t = "1+" + Calendar.getInstance().getTimeInMillis(); +// System.out.println(URLEncoder.encode(DesECBUtil.encryptDES(t, "1111111111111111111"), "utf-8")); +// } +} diff --git a/src/main/java/com/fr/plugin/jsd3998/sso/SsoHttpHandler.java b/src/main/java/com/fr/plugin/jsd3998/sso/SsoHttpHandler.java new file mode 100644 index 0000000..63bac8d --- /dev/null +++ b/src/main/java/com/fr/plugin/jsd3998/sso/SsoHttpHandler.java @@ -0,0 +1,95 @@ +package com.fr.plugin.jsd3998.sso; + +import com.fr.decision.authority.data.User; +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.decision.webservice.Response; +import com.fr.decision.webservice.exception.user.UserNotExistException; +import com.fr.decision.webservice.utils.DecisionServiceConstants; +import com.fr.decision.webservice.v10.login.LoginService; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.stable.StringUtils; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author luomiao + * @since 2020/08/28 + */ +@EnableMetrics +public class SsoHttpHandler extends BaseHttpHandler { + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return "/sso"; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws Exception { + String src = request.getParameter("src"); + String username = request.getParameter("username"); + if ("sys".equals(src) && StringUtils.isNotBlank(username)) { + JSONObject res = new JSONObject(); + response.setContentType("application/json;charset=UTF-8"); + if (loginFromToken(request, response, username)) { + res.put("res", "success"); + WebUtils.printAsJSON(response, res); + } else { + res.put("res", "fail"); + WebUtils.printAsJSON(response, res); + } + } else if (!"sys".equals(src)) { + FineLoggerFactory.getLogger().info("未找到用户名,跳转到登录页面"); + request.getRequestDispatcher("/login.html").forward(request, response); + } + } + + protected boolean loginFromToken(HttpServletRequest req, HttpServletResponse res, String username) throws Exception { + try { + if (com.fr.stable.StringUtils.isNotEmpty(username)) { + FineLoggerFactory.getLogger().info("current username:" + username); + User user = UserService.getInstance().getUserByUserName(username); + FineLoggerFactory.getLogger().info("get user:" + user); + if (user == null) { + throw new UserNotExistException(); + } + String token = LoginService.getInstance().login(req, res, username); + FineLoggerFactory.getLogger().info("get login token:" + token); + req.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token); + FineLoggerFactory.getLogger().info("username:" + username + " login success"); + return true; + } else { + FineLoggerFactory.getLogger().warn("username is null!"); + return false; + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return false; + } + + + protected void sendError(HttpServletResponse response, String errorCode) { + Response res = Response.error(errorCode, errorCode); + try { + response.setContentType("application/json;charset=UTF-8"); + WebUtils.printAsJSON(response, JSONObject.mapFrom(res)); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("输出响应错误失败", e); + } + } +} diff --git a/src/main/java/com/fr/plugin/jsd3998/sso/SsoRequestHandlerBridge.java b/src/main/java/com/fr/plugin/jsd3998/sso/SsoRequestHandlerBridge.java new file mode 100644 index 0000000..9585b2b --- /dev/null +++ b/src/main/java/com/fr/plugin/jsd3998/sso/SsoRequestHandlerBridge.java @@ -0,0 +1,20 @@ +package com.fr.plugin.jsd3998.sso; + +import com.fr.decision.fun.HttpHandler; +import com.fr.decision.fun.impl.AbstractHttpHandlerProvider; +import com.fr.plugin.transform.FunctionRecorder; + +/** + * @author luomiao + * @since 2020/08/28 + */ +@FunctionRecorder +public class SsoRequestHandlerBridge extends AbstractHttpHandlerProvider { + @Override + public HttpHandler[] registerHandlers() { + return new HttpHandler[]{ + new SsoHttpHandler(), + new H5SsoHandler() + }; + } +} diff --git a/src/main/java/com/fr/plugin/jsd3998/sso/SsoRequestURLAliasBridge.java b/src/main/java/com/fr/plugin/jsd3998/sso/SsoRequestURLAliasBridge.java new file mode 100644 index 0000000..9585d69 --- /dev/null +++ b/src/main/java/com/fr/plugin/jsd3998/sso/SsoRequestURLAliasBridge.java @@ -0,0 +1,19 @@ +package com.fr.plugin.jsd3998.sso; + +import com.fr.decision.fun.impl.AbstractURLAliasProvider; +import com.fr.decision.webservice.url.alias.URLAlias; +import com.fr.decision.webservice.url.alias.URLAliasFactory; + +/** + * @author luomiao + * @since 2020/08/28 + */ +public class SsoRequestURLAliasBridge extends AbstractURLAliasProvider { + @Override + public URLAlias[] registerAlias() { + return new URLAlias[]{ + URLAliasFactory.createPluginAlias("/sso", "/sso", true), + URLAliasFactory.createPluginAlias("/h5/callback", "/h5/callback", true) + }; + } +} diff --git a/sso.properties b/sso.properties new file mode 100644 index 0000000..f8be425 --- /dev/null +++ b/sso.properties @@ -0,0 +1,2 @@ +encrypt.key=1111111111111111111 +ticket.timeoutInSeconds=300 diff --git a/使用说明.txt b/使用说明.txt new file mode 100644 index 0000000..a4236b9 --- /dev/null +++ b/使用说明.txt @@ -0,0 +1,13 @@ + +此次完成功能如下 + a、单点登录 + +1、安装本插件,插件安装见连接http://help.finereport.com/doc-view-2198.html + +2、将sso.properties件复制到 %tomcat_home%/webapps/webroot/WEB-INF/classes下, 并修改文件中的ticket加密密钥和时间戳超时时间 + + +2、进入系统测试单点登录,访问地址为http://ip:port/webroot/decision/url/doH5Login,示例为本地地址http://localhost:8075/webroot/decision/url/h5/callback?ticket=XXXX 其中ticket的格式应为 用户名_时间戳 + +5、登录成功后会跳转到移动html5端首页。 + 如页面打印出“单点登录失败”可能原因是获取ticket失败或解析ticket中的用户名在系统中不存在 \ No newline at end of file