commit 28ec8e59952f0b469365695c6886fd086041939c Author: pioneer Date: Thu Nov 10 16:09:54 2022 +0800 open diff --git a/README.md b/README.md new file mode 100644 index 0000000..5ca48e4 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# open-JSD-9985 + +JSD-9985 bi登录超时后跳转到单点认证平台\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系【pioneer】处理。 \ No newline at end of file diff --git a/doc/JSD-9985-需求确认书V2.docx b/doc/JSD-9985-需求确认书V2.docx new file mode 100644 index 0000000..7511ab0 Binary files /dev/null and b/doc/JSD-9985-需求确认书V2.docx differ diff --git a/doc/JSD-9985配置使用文档.docx b/doc/JSD-9985配置使用文档.docx new file mode 100644 index 0000000..fa924c2 Binary files /dev/null and b/doc/JSD-9985配置使用文档.docx differ diff --git a/lib/cas/cas-client-core-3.2.1.jar b/lib/cas/cas-client-core-3.2.1.jar new file mode 100644 index 0000000..8fc3f3f Binary files /dev/null and b/lib/cas/cas-client-core-3.2.1.jar differ diff --git a/lib/cas/casclient.jar b/lib/cas/casclient.jar new file mode 100644 index 0000000..d071c99 Binary files /dev/null and b/lib/cas/casclient.jar differ diff --git a/lib/cas/commons-logging-1.1.jar b/lib/cas/commons-logging-1.1.jar new file mode 100644 index 0000000..2ff9bbd Binary files /dev/null and b/lib/cas/commons-logging-1.1.jar differ diff --git a/lib/finekit-10.0.jar b/lib/finekit-10.0.jar new file mode 100644 index 0000000..f4482fc Binary files /dev/null and b/lib/finekit-10.0.jar differ diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..df29235 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,28 @@ + + + com.fr.plugin.fbpa.sso + + yes + 1.0.2 + 11.0 + 2021-10-31 + fr.open + + + ]]> + com.fr.plugin.fbpa + + com.fanruan.api.* + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/fbpa/LocaleFinder.java b/src/main/java/com/fr/plugin/fbpa/LocaleFinder.java new file mode 100644 index 0000000..6c396d7 --- /dev/null +++ b/src/main/java/com/fr/plugin/fbpa/LocaleFinder.java @@ -0,0 +1,38 @@ + /* + * Copyright (C), 2018-2020 + * Project: starter + * FileName: LocaleFinder + * Author: xx + * Date: 2020/8/31 22:19 + */ + package com.fr.plugin.fbpa; + + import com.fr.intelli.record.Focus; + import com.fr.intelli.record.Original; + import com.fr.record.analyzer.EnableMetrics; + import com.fr.stable.fun.Authorize; + import com.fr.stable.fun.impl.AbstractLocaleFinder; + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + @Authorize(callSignKey = LocaleFinder.PLUGIN_ID) + @EnableMetrics + public class LocaleFinder extends AbstractLocaleFinder { + public static final String PLUGIN_ID = "com.fr.plugin.fbpa.sso"; + + @Override + @Focus(id = PLUGIN_ID, text = "Plugin-fbpa", source = Original.PLUGIN) + public String find() { + return "com/fr/plugin/fbpa/locale/lang"; + } + + @Override + public int currentAPILevel() { + return CURRENT_LEVEL; + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/fbpa/PluginMonitor.java b/src/main/java/com/fr/plugin/fbpa/PluginMonitor.java new file mode 100644 index 0000000..22caf2f --- /dev/null +++ b/src/main/java/com/fr/plugin/fbpa/PluginMonitor.java @@ -0,0 +1,34 @@ + /* + * Copyright (C), 2018-2021 + * Project: starter + * FileName: PluginMonitor + * Author: xx + * Date: 2021/3/30 15:10 + */ + package com.fr.plugin.fbpa; + + import com.fr.plugin.context.PluginContext; + import com.fr.plugin.fbpa.config.FbpaConfig; + import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor; + + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + public class PluginMonitor extends AbstractPluginLifecycleMonitor { + public PluginMonitor() { + } + + @Override + public void afterRun(PluginContext pluginContext) { + FbpaConfig.getInstance(); + } + + @Override + public void beforeStop(PluginContext pluginContext) { + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/fbpa/config/FbpaConfig.java b/src/main/java/com/fr/plugin/fbpa/config/FbpaConfig.java new file mode 100644 index 0000000..41371ef --- /dev/null +++ b/src/main/java/com/fr/plugin/fbpa/config/FbpaConfig.java @@ -0,0 +1,64 @@ + /* + * Copyright (C), 2018-2021 + * Project: starter + * FileName: FbpaConfig + * Author: xx + * Date: 2021/3/30 9:38 + */ + package com.fr.plugin.fbpa.config; + + import com.fr.config.*; + import com.fr.config.holder.Conf; + import com.fr.config.holder.factory.Holders; + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + @Visualization(category = "Plugin-fbpa_Group") + public class FbpaConfig extends DefaultConfiguration { + public static final String DEFAULT_LOGIN_URL = "https://xx.com.cn/login"; + public static final String DEFAULT_VALIDATE_URL = "https://xx.com.cn/serviceValidate"; + public static final String DEFAULT_SERVER_NAME = "xx.com.cn"; + private static volatile FbpaConfig config = null; + @Identifier(value = "loginUrl", name = "Plugin-fbpa_Config_LoginUrl", description = "Plugin-fbpa_Config_LoginUrl_Description", status = Status.SHOW) + private final Conf loginUrl = Holders.simple(DEFAULT_LOGIN_URL); + @Identifier(value = "validateUrl", name = "Plugin-fbpa_Config_ValidateUrl", description = "Plugin-fbpa_Config_ValidateUrl_Description", status = Status.SHOW) + private final Conf validateUrl = Holders.simple(DEFAULT_VALIDATE_URL); + @Identifier(value = "serverName", name = "Plugin-fbpa_Config_ServerName", description = "Plugin-fbpa_Config_ServerName_Description", status = Status.SHOW) + private final Conf serverName = Holders.simple(DEFAULT_SERVER_NAME); + + public static FbpaConfig getInstance() { + if (config == null) { + config = ConfigContext.getConfigInstance(FbpaConfig.class); + } + return config; + } + + public String getLoginUrl() { + return loginUrl.get(); + } + + public void setLoginUrl(String loginUrl) { + this.loginUrl.set(loginUrl); + } + + public String getValidateUrl() { + return validateUrl.get(); + } + + public void setValidateUrl(String validateUrl) { + this.validateUrl.set(validateUrl); + } + + public String getServerName() { + return serverName.get(); + } + + public void setServerName(String serverName) { + this.serverName.set(serverName); + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/fbpa/request/IgnoreFilter.java b/src/main/java/com/fr/plugin/fbpa/request/IgnoreFilter.java new file mode 100644 index 0000000..8561425 --- /dev/null +++ b/src/main/java/com/fr/plugin/fbpa/request/IgnoreFilter.java @@ -0,0 +1,133 @@ + /* + * Copyright (C), 2018-2022 + * Project: starterBI + * FileName: IgnoreFilter + * Author: xx + * Date: 2022/7/5 10:28 + */ + package com.fr.plugin.fbpa.request; + + import com.fanruan.api.i18n.I18nKit; + import com.fanruan.api.log.LogKit; + import com.fanruan.api.util.StringKit; + import com.fr.base.ServerConfig; + import com.fr.data.NetworkHelper; + import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; + import com.fr.decision.mobile.terminal.TerminalHandler; + import com.fr.decision.webservice.v10.login.LoginService; + import com.fr.general.ComparatorUtils; + import com.fr.log.FineLoggerFactory; + import com.fr.plugin.context.PluginContexts; + import com.fr.stable.StringUtils; + import com.fr.web.utils.WebUtils; + import edu.yale.its.tp.cas.client.filter.CASFilter; + + import javax.servlet.FilterChain; + import javax.servlet.FilterConfig; + import javax.servlet.ServletException; + import javax.servlet.http.HttpServletRequest; + import javax.servlet.http.HttpServletResponse; + import javax.servlet.http.HttpSession; + import java.io.IOException; + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + public class IgnoreFilter extends AbstractGlobalRequestFilterProvider { + public static final String VIEW_REPORT = "/view/report"; + public static final String VIEW_FORM = "/view/form"; + public static final String OP_H5 = "H5"; + public static final String MOBILE_PATH = "/url/mobile"; + public static final String IGNORE_REQUEST = "ignoreRequest"; + + /** + * 过滤器名称 + * + * @return + */ + @Override + public String filterName() { + return "A_IgnoreFilter"; + } + + /** + * 过滤规则 + * + * @return + */ + @Override + public String[] urlPatterns() { + if (PluginContexts.currentContext() == null || !PluginContexts.currentContext().isAvailable()) { + LogKit.error(I18nKit.getLocText("Plugin-fbpa_Licence_Expired")); + return new String[]{}; + } + return new String[]{"/" + ServerConfig.getInstance().getServletName()}; + } + + /** + * 过滤器初始化 + * + * @param filterConfig + */ + @Override + public void init(FilterConfig filterConfig) { + super.init(filterConfig); + } + + /** + * 过滤器处理 + * + * @param req + * @param res + * @param filterChain + */ + @Override + public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + try { + LogKit.info("fbpa-IgnoreFilter-doFilter-url:{}", WebUtils.getOriginalURL(req)); + HttpSession session = req.getSession(); + if (LoginService.getInstance().isLogged(req)) { + session.setAttribute(CASFilter.CAS_FILTER_USER, LoginService.getInstance().getCurrentUserNameFromRequestCookie(req)); + filterChain.doFilter(req, res); + return; + } + String pathInfo = (req.getPathInfo() != null) ? req.getPathInfo() : StringUtils.EMPTY; + String jsApiPath = NetworkHelper.getHTTPRequestParameter(req, "js_api_path"); + String sb = NetworkHelper.getHTTPRequestParameter(req, "sb"); + if (ComparatorUtils.equals(VIEW_REPORT, pathInfo) || ComparatorUtils.equals(VIEW_FORM, pathInfo) + || validateMobile(req, pathInfo)) { + session.setAttribute(CASFilter.CAS_FILTER_USER, IGNORE_REQUEST); + filterChain.doFilter(req, res); + return; + } + if (StringUtils.isNotBlank(jsApiPath) && StringUtils.isNotBlank(sb)) { + LogKit.info("fbpa-IgnoreFilter-doFilter-jsApiPath:{}, sb:{}", jsApiPath, sb); + session.setAttribute(CASFilter.CAS_FILTER_USER, IGNORE_REQUEST); + filterChain.doFilter(req, res); + return; + } + filterChain.doFilter(req, res); + } catch (Exception e) { + LogKit.error(e.getMessage(), e); + } + } + + /** + * 验证是否移动端请求 + * + * @param req + * @param pathInfo + * @return + */ + private Boolean validateMobile(HttpServletRequest req, String pathInfo) { + String op = WebUtils.getHTTPRequestParameter(req, "op"); + return (StringUtils.isNotEmpty(op) && StringUtils.equals(OP_H5, op.toUpperCase())) + || WebUtils.getDevice(req).isMobile() + || ComparatorUtils.equalsIgnoreCase(MOBILE_PATH, pathInfo) + || TerminalHandler.getTerminal(req, WebUtils.getDevice(req)) == TerminalHandler.H5; + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/fbpa/request/LoginFilter.java b/src/main/java/com/fr/plugin/fbpa/request/LoginFilter.java new file mode 100644 index 0000000..599cc26 --- /dev/null +++ b/src/main/java/com/fr/plugin/fbpa/request/LoginFilter.java @@ -0,0 +1,174 @@ + /* + * Copyright (C), 2018-2022 + * Project: starterBI + * FileName: LoginFilter + * Author: xx + * Date: 2022/7/5 10:28 + */ + package com.fr.plugin.fbpa.request; + + import com.fanruan.api.i18n.I18nKit; + import com.fanruan.api.log.LogKit; + import com.fanruan.api.util.StringKit; + import com.fr.base.ServerConfig; + import com.fr.data.NetworkHelper; + import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; + import com.fr.decision.mobile.terminal.TerminalHandler; + import com.fr.decision.webservice.utils.DecisionServiceConstants; + import com.fr.decision.webservice.v10.login.LoginService; + import com.fr.general.ComparatorUtils; + import com.fr.log.FineLoggerFactory; + import com.fr.plugin.context.PluginContexts; + import com.fr.security.JwtUtils; + import com.fr.stable.StringUtils; + import com.fr.stable.web.Device; + import com.fr.third.org.apache.http.NameValuePair; + import com.fr.third.org.apache.http.client.utils.URIBuilder; + import com.fr.web.utils.WebUtils; + import edu.yale.its.tp.cas.client.filter.CASFilter; + import org.jasig.cas.client.validation.Assertion; + + import javax.servlet.FilterChain; + import javax.servlet.FilterConfig; + import javax.servlet.http.HttpServletRequest; + import javax.servlet.http.HttpServletResponse; + import javax.servlet.http.HttpSession; + import java.net.URISyntaxException; + import java.util.List; + + import static com.fr.plugin.fbpa.request.IgnoreFilter.IGNORE_REQUEST; + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + public class LoginFilter extends AbstractGlobalRequestFilterProvider { + + /** + * 过滤器名称 + * + * @return + */ + @Override + public String filterName() { + return "D_LoginFilter"; + } + + /** + * 过滤规则 + * + * @return + */ + @Override + public String[] urlPatterns() { + if (PluginContexts.currentContext() == null || !PluginContexts.currentContext().isAvailable()) { + LogKit.error(I18nKit.getLocText("Plugin-fbpa_Licence_Expired")); + return new String[]{}; + } + return new String[]{"/" + ServerConfig.getInstance().getServletName()}; + } + + /** + * 过滤器初始化 + * + * @param filterConfig + */ + @Override + public void init(FilterConfig filterConfig) { + super.init(filterConfig); + } + + /** + * 过滤器处理 + * + * @param req + * @param res + * @param filterChain + */ + @Override + public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + try { + LogKit.info("fbpa-LoginFilter-doFilter-url:" + WebUtils.getOriginalURL(req)); + HttpSession session = req.getSession(true); + String username; + //获取cas传递过来的username + username = req.getRemoteUser(); +// Object object = req.getSession().getAttribute("_const_cas_assertion_"); +// if (StringUtils.isEmpty(username) && object != null) { +// Assertion assertion = (Assertion) object; +// username = assertion.getPrincipal().getName(); +// } + if (StringUtils.isEmpty(username)) { + username = (String) session.getAttribute(CASFilter.CAS_FILTER_USER); + } + + if (StringUtils.isEmpty(username)) { + LogKit.info("fbpa-LoginFilter-doFilter-username is Empty"); + filterChain.doFilter(req, res); + return; + } + LogKit.info("fbpa-LoginFilter-doFilter-username:{}", username); + // 放行请求 + if (StringKit.equalsIgnoreCase(username, IGNORE_REQUEST)) { + session.setAttribute(CASFilter.CAS_FILTER_USER, null); + filterChain.doFilter(req, res); + return; + } + // 决策系统已登录 + if (LoginService.getInstance().isLogged(req) && StringKit.equalsIgnoreCase(LoginService.getInstance().getCurrentUserNameFromRequestCookie(req), username)) { + filterChain.doFilter(req, res); + return; + } + login(req, res, session, username); + if (StringUtils.contains(getOriginalURL(req), "ticket")) { + res.sendRedirect(removeToken(req)); + return; + } + filterChain.doFilter(req, res); + } catch (Exception e) { + LogKit.error(e.getMessage(), e); + } + } + + /** + * 移除url的token参数 + * + * @param request + * @return + */ + private String removeToken(HttpServletRequest request) throws URISyntaxException { + URIBuilder uriBuilder = new URIBuilder(getOriginalURL(request)); + List params = uriBuilder.getQueryParams(); + params.removeIf(pair -> ComparatorUtils.equals(pair.getName(), "ticket")); + uriBuilder.clearParameters(); + if (!params.isEmpty()) { + uriBuilder.setParameters(params); + } + return uriBuilder.build().toString(); + } + + /** + * 得到请求url和参数 + * + * @param request + * @return + */ + private String getOriginalURL(HttpServletRequest request) { + StringBuffer url = request.getRequestURL(); + if (StringUtils.isNotBlank(request.getQueryString())) { + url.append("?").append(request.getQueryString()); + } + return url.toString(); + } + + /** + * 后台登录方法 + */ + private void login(HttpServletRequest req, HttpServletResponse res, HttpSession session, String username) throws Exception { + String token = LoginService.getInstance().login(req, res, username); + req.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token); + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/fbpa/request/SSOFilter.java b/src/main/java/com/fr/plugin/fbpa/request/SSOFilter.java new file mode 100644 index 0000000..423ff69 --- /dev/null +++ b/src/main/java/com/fr/plugin/fbpa/request/SSOFilter.java @@ -0,0 +1,94 @@ + /* + * Copyright (C), 2018-2021 + * Project: starter + * FileName: SSOFilter + * Author: xx + * Date: 2021/12/16 15:01 + */ + package com.fr.plugin.fbpa.request; + + import com.fanruan.api.i18n.I18nKit; + import com.fanruan.api.log.LogKit; + import com.fr.base.ServerConfig; + import com.fr.decision.fun.GlobalRequestFilterProvider; + import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; + import com.fr.plugin.context.PluginContexts; + import com.fr.plugin.fbpa.config.FbpaConfig; + import org.jetbrains.annotations.NotNull; + + import java.util.HashMap; + import java.util.Map; + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + public class SSOFilter extends AbstractGlobalRequestFilterProvider { + + public static final String LOGIN_URL = "edu.yale.its.tp.cas.client.filter.loginUrl"; + public static final String VALIDATE_URL = "edu.yale.its.tp.cas.client.filter.validateUrl"; + public static final String SERVER_NAME = "edu.yale.its.tp.cas.client.filter.serverName"; + + /** + * 过滤器名称 + * + * @return + */ + @Override + public String filterName() { + return "B_SSOFilter"; + } + + /** + * 过滤规则 + * + * @return + */ + @Override + public String[] urlPatterns() { + if (PluginContexts.currentContext() == null || !PluginContexts.currentContext().isAvailable()) { + LogKit.error(I18nKit.getLocText("Plugin-fbpa_Licence_Expired")); + return new String[]{}; + } + return new String[]{"/" + ServerConfig.getInstance().getServletName()}; + } + + /** + * 外部的过滤器类名,需要把相应的jar包放到web服务器的classpath中 + * + * @return 类名 + */ + @Override + public String externalFilterClassName() { + return "edu.yale.its.tp.cas.client.filter.CASFilter"; + } + + /** + * 过滤器的初始化参数 + * + * @return 参数集合 + */ + @Override + public Map initializationParameters() { + Map params = new HashMap<>(); + FbpaConfig config = FbpaConfig.getInstance(); + params.put(LOGIN_URL, config.getLoginUrl()); + params.put(VALIDATE_URL, config.getValidateUrl()); + params.put(SERVER_NAME, config.getServerName()); + return params; + } + + /** + * 可选实现的多个filter排序(执行顺序)的方法 + * + * @param other + * @return 0 相等,大于0是自身优先 小于0 是other优先 + */ + @Override + public int compareTo(@NotNull GlobalRequestFilterProvider other) { + return super.compareTo(other); + } + } \ No newline at end of file diff --git a/src/main/resources/com/fr/plugin/fbpa/locale/lang.properties b/src/main/resources/com/fr/plugin/fbpa/locale/lang.properties new file mode 100644 index 0000000..1c9f095 --- /dev/null +++ b/src/main/resources/com/fr/plugin/fbpa/locale/lang.properties @@ -0,0 +1,9 @@ +Plugin-fbpa=Sso Plugin +Plugin-fbpa_Group=Sso Plugin +Plugin-fbpa_Config_LoginUrl=CAS LoginUrl +Plugin-fbpa_Config_LoginUrl_Description=CAS LoginUrl +Plugin-fbpa_Config_ValidateUrl=CAS ValidateUrl +Plugin-fbpa_Config_ValidateUrl_Description=CAS ValidateUrl +Plugin-fbpa_Config_ServerName=FR url +Plugin-fbpa_Config_ServerName_Description=FR url +Plugin-fbpa_Licence_Expired=Sso Plugin Licence Expired \ No newline at end of file diff --git a/src/main/resources/com/fr/plugin/fbpa/locale/lang_zh_CN.properties b/src/main/resources/com/fr/plugin/fbpa/locale/lang_zh_CN.properties new file mode 100644 index 0000000..a6ddddc --- /dev/null +++ b/src/main/resources/com/fr/plugin/fbpa/locale/lang_zh_CN.properties @@ -0,0 +1,9 @@ +Plugin-fbpa=\u5355\u70B9\u767B\u9646\u63D2\u4EF6 +Plugin-fbpa_Group=\u5355\u70B9\u767B\u9646\u63D2\u4EF6 +Plugin-fbpa_Config_LoginUrl=CAS\u767B\u9646url +Plugin-fbpa_Config_LoginUrl_Description=CAS\u767B\u9646url +Plugin-fbpa_Config_ValidateUrl=CAS\u9A8C\u8BC1url +Plugin-fbpa_Config_ValidateUrl_Description=CAS\u9A8C\u8BC1url +Plugin-fbpa_Config_ServerName=\u5E06\u8F6F\u7CFB\u7EDFurl +Plugin-fbpa_Config_ServerName_Description=\u5E06\u8F6F\u7CFB\u7EDFurl +Plugin-fbpa_Licence_Expired=\u5355\u70B9\u767B\u9646\u63D2\u4EF6\u8BB8\u53EF\u8FC7\u671F \ No newline at end of file diff --git a/src/main/resources/com/fr/plugin/fbpa/web/unavailable.html b/src/main/resources/com/fr/plugin/fbpa/web/unavailable.html new file mode 100644 index 0000000..f28c623 --- /dev/null +++ b/src/main/resources/com/fr/plugin/fbpa/web/unavailable.html @@ -0,0 +1,97 @@ + + + + + + + + + + +
+
${result}
+
${reason}
+
${solution}
+
+ + + + \ No newline at end of file