diff --git a/JSD-8277 -需求确认书V1.1.docx b/JSD-8277 -需求确认书V1.1.docx
new file mode 100644
index 0000000..22b11a7
Binary files /dev/null and b/JSD-8277 -需求确认书V1.1.docx differ
diff --git a/README.md b/README.md
index 624bf42..2dca85f 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
# open-JSD-8277
-JSD-8277 开源任务材料
\ No newline at end of file
+JSD-8277 开源任务材料\
+免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\
+仅作为开发者学习参考使用!禁止用于任何商业用途!\
+为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。
\ No newline at end of file
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..90f16e9
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,17 @@
+
+
+ com.fr.plugin.xxxx.sso
+
+ yes
+ 1.1
+ 10.0
+ 2018-07-31
+ fr.open
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..ae69d05
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,6 @@
+此次完成功能如下
+ a、单点登录
+
+1、将压缩文件解压后的tengyuejz.properties配置文件拷贝至 %部署路径%/WEB-INF/resources
+2、安装本插件,插件安装见连接http://help.finereport.com/doc-view-2198.html
+3、进入系统测试单点登录,访问地址为http://ip:port/webroot/decision
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/xxxx/sso/CommonUtils.java b/src/main/java/com/fr/plugin/xxxx/sso/CommonUtils.java
new file mode 100644
index 0000000..8521820
--- /dev/null
+++ b/src/main/java/com/fr/plugin/xxxx/sso/CommonUtils.java
@@ -0,0 +1,90 @@
+package com.fr.plugin.xxxx.sso;
+
+import com.fr.data.NetworkHelper;
+import com.fr.decision.authority.data.User;
+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.decision.webservice.v10.login.TokenResource;
+import com.fr.decision.webservice.v10.user.UserService;
+import com.fr.log.FineLoggerFactory;
+import com.fr.stable.StringUtils;
+import com.fr.stable.web.Device;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Properties;
+
+/**
+ * @author fr.open
+ * @since 2021/7/12
+ */
+public class CommonUtils {
+
+ public static String getProperty(Properties props, String key, String defaultValue, boolean allowBlank) {
+ String value = props.getProperty(key);
+ if (StringUtils.isNotBlank(value)) {
+ return value;
+ } else {
+ if (allowBlank) {
+ return defaultValue;
+ } else {
+ throw new IllegalArgumentException("Property[" + key + "] cann't be blank.");
+ }
+ }
+ }
+
+ public static String getProperty(Properties props, String key, boolean allowBlank) {
+ return getProperty(props, key, null, allowBlank);
+ }
+
+ public static String getProperty(Properties props, String key) {
+ return getProperty(props, key, null, true);
+ }
+
+ public static boolean isLogin(HttpServletRequest request) {
+ String oldToken = TokenResource.COOKIE.getToken(request);
+ return oldToken != null && checkTokenValid(request, (String) oldToken);
+ }
+
+ private static boolean checkTokenValid(HttpServletRequest req, String token) {
+ try {
+ Device device = NetworkHelper.getDevice(req);
+ LoginService.getInstance().loginStatusValid(token, TerminalHandler.getTerminal(req, device));
+ return true;
+ } catch (Exception ignore) {
+ }
+ return false;
+ }
+
+ /**
+ * 跳转到过滤器链中的下一个过滤器
+ *
+ * @param request
+ * @param response
+ * @param chain
+ */
+ public static void next(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
+ try {
+ chain.doFilter(request, response);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void login(String username, HttpServletRequest request, HttpServletResponse response) {
+ try {
+ User user = UserService.getInstance().getUserByUserName(username);
+ if (user == null) {
+ throw new RuntimeException("系统未授权, 当前用户是\"" + username + "\"");
+ }
+ String token = LoginService.getInstance().login(request, response, username);
+ request.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token);
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error("sso >> Failed to login with[" + username + "]", e);
+ throw new RuntimeException("用户\"" + username +"\"登录失败");
+ }
+ }
+
+}
diff --git a/src/main/java/com/fr/plugin/xxxx/sso/CustomLogInOutEventProvider.java b/src/main/java/com/fr/plugin/xxxx/sso/CustomLogInOutEventProvider.java
new file mode 100644
index 0000000..bd0f346
--- /dev/null
+++ b/src/main/java/com/fr/plugin/xxxx/sso/CustomLogInOutEventProvider.java
@@ -0,0 +1,41 @@
+package com.fr.plugin.xxxx.sso;
+
+import com.fr.decision.fun.impl.AbstractLogInOutEventProvider;
+import com.fr.decision.webservice.login.LogInOutResultInfo;
+import com.fr.general.PropertiesUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Properties;
+
+/**
+ * @Author fr.open
+ * @since 2021/7/12
+ **/
+public class CustomLogInOutEventProvider extends AbstractLogInOutEventProvider {
+
+ private final String apiClientId;
+
+ private final String logoutURL;
+
+ private final String logoutRedirectURL;
+
+ public CustomLogInOutEventProvider() {
+ Properties props = PropertiesUtils.getProperties("xxxx");
+ this.apiClientId = CommonUtils.getProperty(props, "api.client-id", false);
+ this.logoutURL = CommonUtils.getProperty(props, "api.logout", false);
+ this.logoutRedirectURL = CommonUtils.getProperty(props, "api.logout-redirct-url", "http://www.baidu.com", true);
+ }
+
+ @Override
+ public String logoutAction(LogInOutResultInfo result) {
+ try {
+ return String.format("%s?redirctToUrl=%s&redirectToLogin=true&entityId=%s",
+ logoutURL,
+ URLEncoder.encode(logoutRedirectURL, "utf-8"),
+ apiClientId);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/com/fr/plugin/xxxx/sso/SsoFilter.java b/src/main/java/com/fr/plugin/xxxx/sso/SsoFilter.java
new file mode 100644
index 0000000..f9b73a9
--- /dev/null
+++ b/src/main/java/com/fr/plugin/xxxx/sso/SsoFilter.java
@@ -0,0 +1,165 @@
+package com.fr.plugin.tengyuejz.sso;
+
+import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider;
+import com.fr.general.PropertiesUtils;
+import com.fr.general.http.HttpToolbox;
+import com.fr.json.JSONArray;
+import com.fr.json.JSONObject;
+import com.fr.log.FineLoggerFactory;
+import com.fr.plugin.transform.FunctionRecorder;
+import com.fr.stable.StringUtils;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Stream;
+
+import static com.fr.plugin.tengyuejz.sso.CommonUtils.*;
+
+/**
+ * @author fr.open
+ * @since 2021/7/12
+ */
+@FunctionRecorder
+public class SsoFilter extends AbstractGlobalRequestFilterProvider {
+
+ private static String[] NOT_FILTER = {
+ "/decision/file",
+ "/decision/resources",
+ "/system",
+ "/materials.min.js.map",
+ "/remote",
+ "/login",
+ "/login/config"
+ };
+
+ private final String apiClientId;
+
+ private final String apiClientSecret;
+
+ private final String apiAuthorize;
+
+ private final String apiGetToken;
+
+ private final String apiGetUser;
+
+ private final String errorOutput;
+
+ private final String systemName;
+
+ private final String bipName;
+
+ private final String telephone;
+
+ public SsoFilter() {
+ Properties props = PropertiesUtils.getProperties("xxxx");
+ this.apiClientId = getProperty(props, "api.client-id", false);
+ this.apiClientSecret = getProperty(props, "api.client-secret", false);
+ this.apiAuthorize = getProperty(props, "api.authorize", false);
+ this.apiGetToken = getProperty(props, "api.get-token", false);
+ this.apiGetUser = getProperty(props, "api.get-user", false);
+ this.errorOutput = getProperty(props, "api.error-output", false);
+ this.systemName = getProperty(props, "info.system-name", false);
+ this.bipName = getProperty(props, "info.bip-name", false);
+ this.telephone = getProperty(props, "info.telephone", false);
+ }
+
+ @Override
+ public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
+ if (isAccept(request)) {
+ next(request, response, chain);
+ return;
+ }
+
+ try {
+ String code = request.getParameter("code");
+ if (StringUtils.isBlank(code)) {
+ String redirectURL = URLEncoder.encode(request.getRequestURL().toString(), "utf-8");
+ String authorizeURL = String.format("%s?client_id=%s&redirect_uri=%s&response_type=code", apiAuthorize, apiClientId, redirectURL);
+ FineLoggerFactory.getLogger().info("没有在参数中找到code值,跳转到认证登录地址, 认证地址为\"{}\"", authorizeURL);
+ response.sendRedirect(authorizeURL);
+ return;
+ }
+ login(getUsername(getToken(code)), request, response);
+ next(request, response, chain);
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error("单点登录处理失败.", e);
+ setError(response, e.getMessage());
+ }
+ }
+
+ @Override
+ public String filterName() {
+ return "sso";
+ }
+
+ @Override
+ public String[] urlPatterns() {
+ return new String[]{"/*"};
+ }
+
+ private boolean isAccept(HttpServletRequest request) {
+ String url = request.getRequestURL().toString();
+ return isLogin(request) || Stream.of(NOT_FILTER).anyMatch(url::contains);
+ }
+
+ private String getToken(String code) throws IOException {
+ Map params = new HashMap<>();
+ params.put("client_id", apiClientId);
+ params.put("client_secret", apiClientSecret);
+ params.put("code", code);
+ params.put("grant_type", "authorization_code");
+ Map headers = new HashMap<>();
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+ String res = HttpToolbox.post(apiGetToken, params, headers);
+ FineLoggerFactory.getLogger().info("获取Token接口返回内容: \"{}\"", res);
+ JSONObject body = new JSONObject(res);
+ if (body.has("access_token")) {
+ String token = body.getString("access_token");
+ FineLoggerFactory.getLogger().info("获取到的token值为: \"{}\"", token);
+ return token;
+ }
+ throw new RuntimeException("获取token失败");
+ }
+
+ private String getUsername(String token) throws IOException {
+ String api = String.format("%s?access_token=%s&client_id=%s", apiGetUser, token, apiClientId);
+ FineLoggerFactory.getLogger().info("获取用户信息接口访问地址为: \"{}\"", api);
+ String res = HttpToolbox.get(api);
+ FineLoggerFactory.getLogger().info("获取用户信息接口返回内容: \"{}\"", res);
+ JSONObject body = new JSONObject(res);
+ if (body.has("spRoleList")) {
+ JSONArray roles = body.getJSONArray("spRoleList");
+ if (roles.size() > 0) {
+ String username = roles.getString(0);
+ FineLoggerFactory.getLogger().info("获取到的用户名为: \"{}\"", username);
+ return username;
+ } else {
+ throw new RuntimeException("系统未授权, 当前用户是\"" + body.getString("displayName") + "\"");
+ }
+ }
+ throw new RuntimeException("访问获取用户信息接口失败");
+ }
+
+ private void setError(HttpServletResponse res, String reason) {
+ try {
+ String URL = String.format(
+ "%s?systemName=%s&bipName=%s&telephone=%s&reason=%s",
+ errorOutput,
+ URLEncoder.encode(systemName, "utf-8"),
+ URLEncoder.encode(bipName, "utf-8"),
+ URLEncoder.encode(telephone, "utf-8"),
+ URLEncoder.encode(reason, "utf-8")
+ );
+ res.sendRedirect(URL);
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/src/main/resources/xxxx.properties b/src/main/resources/xxxx.properties
new file mode 100644
index 0000000..12cac66
--- /dev/null
+++ b/src/main/resources/xxxx.properties
@@ -0,0 +1,23 @@
+# ͳһ֤ƽ̨֤¼ַ
+api.authorize=https://xxx:8443/idp/oauth2/authorize
+# ȡaccess_tokenĽӿڵַ
+api.get-token=https://xxx:8443/idp/oauth2/getToken
+# ȡûϢĽӿڵַ
+api.get-user=https://xxx:8443/idp/oauth2/getUserInfo
+# ע¼Ľӿڵַ
+api.logout=https://xxx:8443/idp/profile/SAML2/Redirect/GLO
+# ע¼תĵַ
+api.logout-redirct-url=http://xxx
+# Ϣַ
+api.error-output=https://xxx:8443/idp/themes/error/defpage-403.html
+# ֤ƽ̨client_id
+api.client-id=xxx
+# ֤ƽ̨client_secret
+api.client-secret=xxxx
+
+# ϵͳ
+info.system-name=xxxx
+# άԱ
+info.bip-name=xxxx
+# άԱ绰
+info.telephone=12345678901
\ No newline at end of file
diff --git a/xxxx.properties b/xxxx.properties
new file mode 100644
index 0000000..12cac66
--- /dev/null
+++ b/xxxx.properties
@@ -0,0 +1,23 @@
+# ͳһ֤ƽ̨֤¼ַ
+api.authorize=https://xxx:8443/idp/oauth2/authorize
+# ȡaccess_tokenĽӿڵַ
+api.get-token=https://xxx:8443/idp/oauth2/getToken
+# ȡûϢĽӿڵַ
+api.get-user=https://xxx:8443/idp/oauth2/getUserInfo
+# ע¼Ľӿڵַ
+api.logout=https://xxx:8443/idp/profile/SAML2/Redirect/GLO
+# ע¼תĵַ
+api.logout-redirct-url=http://xxx
+# Ϣַ
+api.error-output=https://xxx:8443/idp/themes/error/defpage-403.html
+# ֤ƽ̨client_id
+api.client-id=xxx
+# ֤ƽ̨client_secret
+api.client-secret=xxxx
+
+# ϵͳ
+info.system-name=xxxx
+# άԱ
+info.bip-name=xxxx
+# άԱ绰
+info.telephone=12345678901
\ No newline at end of file