diff --git a/JSD-7492 -需求确认书V1.docx b/JSD-7492 -需求确认书V1.docx new file mode 100644 index 0000000..5bd7b28 Binary files /dev/null and b/JSD-7492 -需求确认书V1.docx differ diff --git a/README.md b/README.md index 242d5c0..be60685 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # open-JSD-7942 -JSD-7942 开源任务材料 \ No newline at end of file +JSD-7942 开源任务材料\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 \ No newline at end of file diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..ce711ed --- /dev/null +++ b/plugin.xml @@ -0,0 +1,16 @@ + + + com.fr.plugin.ctfo.sso + + yes + 1.2 + 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..edf4a67 --- /dev/null +++ b/readme.txt @@ -0,0 +1,9 @@ + +此次完成功能如下 + a、单点登录 + +1、将压缩文件解压后的xplatform.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/ctfo/sso/Oauth2Filter.java b/src/main/java/com/fr/plugin/ctfo/sso/Oauth2Filter.java new file mode 100644 index 0000000..40fd5fd --- /dev/null +++ b/src/main/java/com/fr/plugin/ctfo/sso/Oauth2Filter.java @@ -0,0 +1,204 @@ +package com.fr.plugin.ctfo.sso; + +import com.fr.base.PropertiesUtils; +import com.fr.common.util.Assert; +import com.fr.data.NetworkHelper; +import com.fr.decision.authority.data.User; +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.decision.webservice.v10.login.TokenResource; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.general.http.HttpToolbox; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.transform.FunctionRecorder; +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.io.IOException; +import java.net.URLEncoder; +import java.time.Instant; +import java.util.Collections; +import java.util.Properties; +import java.util.stream.Stream; + +/** + * @author fr.open + * @since 2021/5/25 + */ +@FunctionRecorder +public class Oauth2Filter extends AbstractGlobalRequestFilterProvider { + + private static final String[] NOT_FILTER = { + "/decision/file", + "/decision/resources", + "/system", + "/materials.min.js.map", + "/remote", + "/login", + "/login/config", + }; + + private final String apiAuthorize; + + private final String apiAppKey; + + private final String apiAppSecret; + + private final String apiGetToken; + + private final String apiGetUser; + + public Oauth2Filter() { + Properties props = PropertiesUtils.getProperties("xplatform"); + apiAuthorize = getValue(props, "api.authorize"); + apiAppKey = getValue(props, "api.app-key"); + apiAppSecret = getValue(props, "api.app-secret"); + apiGetToken = getValue(props, "api.get-token"); + apiGetUser = getValue(props, "api.get-user"); + } + + private String getValue(Properties props, String key) { + String value = props.getProperty(key); + Assert.hasText(value, "配置项[" + key + "]不能为空"); + return value; + } + + @Override + public String filterName() { + return "ctfoOauth2"; + } + + @Override + public String[] urlPatterns() { + return new String[]{"/*"}; + } + + @Override + public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { + final String url = request.getRequestURL().toString(); + FineLoggerFactory.getLogger().info("sso >> URL of this request => {}", url); + try { + if (isLogin(request) || Stream.of(NOT_FILTER).anyMatch(url::contains)) { + FineLoggerFactory.getLogger().info("sso >> 已登录或默认可放行的请求地址"); + chain.doFilter(request, response); + return; + } + + String code = request.getParameter("code"); + if (StringUtils.isBlank(code)) { + FineLoggerFactory.getLogger().info("sso >> Parameter['code'] 值为空, 跳转到登录页"); + toAuthorize(request, response); + return; + } + + FineLoggerFactory.getLogger().info("sso >> Parameter['code'] => [{}]", code); + String token; + try { + token = getAccessToken(request, code); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("sso >> 获取access token失败", e); + chain.doFilter(request, response); + return; + } + + FineLoggerFactory.getLogger().info("sso >> AccessToken => [{}]", token); + String username; + try { + username = getUsername(token); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("sso >> 获取用户名失败", e); + chain.doFilter(request, response); + return; + } + + FineLoggerFactory.getLogger().info("sso >> username => [{}]", token); + login(username, request, response); + chain.doFilter(request, response); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void toAuthorize(HttpServletRequest request, HttpServletResponse response) throws IOException { + String urlPattern = "%s?response_type=code&client_id=%s&redirect_uri=%s&oauth_timestamp=%s"; + String rURI = request.getRequestURL().toString(); +// if (request.getQueryString() != null) { +// rURI += "?" + request.getQueryString(); +// } + rURI = URLEncoder.encode(rURI, "utf-8"); + String url = String.format(urlPattern, apiAuthorize, apiAppKey, rURI, Instant.now().toEpochMilli()); + FineLoggerFactory.getLogger().info("sso >> 登录认证地址 => [{}]", url); + response.sendRedirect(url); + } + + private String getAccessToken(HttpServletRequest request, String code) throws IOException, IllegalAccessException { + String api = "%s?grant_type=authorization_code&oauth_timestamp=%s&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s"; + String rURI = request.getRequestURL().toString(); + rURI = URLEncoder.encode(rURI, "utf-8"); +// if (request.getQueryString() != null) { +// rURI += "?" + request.getQueryString(); +// } + api = String.format(api, apiGetToken, Instant.now().toEpochMilli(), apiAppKey, apiAppSecret, code, rURI); + FineLoggerFactory.getLogger().info("sso >> CODE换取AccessToken的接口地址 => [{}]", api); + String response = HttpToolbox.post(api, Collections.emptyMap()); + FineLoggerFactory.getLogger().info("sso >> CODE换取AccessToken的接口返回内容 => [{}]", response); + JSONObject res = new JSONObject(response); + if (res.has("access_token") && StringUtils.isNotBlank(res.getString("access_token"))) { + return res.getString("access_token"); + } + throw new IllegalAccessException(); + } + + private String getUsername(String token) throws IOException, IllegalAccessException { + String api = String.format("%s?access_token=%s", apiGetUser, token); + FineLoggerFactory.getLogger().info("sso >> 获取用户信息接口地址 => [{}]", api); + String response = HttpToolbox.get(api); + FineLoggerFactory.getLogger().info("sso >> 获取用户信息接口地址返回内容 => [{}]", response); + JSONObject res = new JSONObject(response); + if (res.has("attributes")) { + JSONObject attrs = res.getJSONObject("attributes"); + if (attrs.has("account_no") && StringUtils.isNotBlank(attrs.getString("account_no"))) { + return attrs.getString("account_no"); + } + } + throw new IllegalAccessException(); + } + + private void login(String username, HttpServletRequest request, HttpServletResponse response) { + try { + User user = UserService.getInstance().getUserByUserName(username); + if (user == null) { + FineLoggerFactory.getLogger().error("sso >> User \"{}\" does not exist", username); + return; + } + FineLoggerFactory.getLogger().info("sso >> Getted user: {}", user); + String token = LoginService.getInstance().login(request, response, username); + FineLoggerFactory.getLogger().info("sso >> Getted login token: {}", token); + request.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token); + FineLoggerFactory.getLogger().info("sso >> User[{}] login successfully", username); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("sso >> Failed to login with[" + username + "]", e); + } + } + + private boolean isLogin(HttpServletRequest request) { + String oldToken = TokenResource.COOKIE.getToken(request); + return oldToken != null && checkTokenValid(request, (String) oldToken); + } + + private 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; + } +} diff --git a/src/main/resources/xplatform.properties b/src/main/resources/xplatform.properties new file mode 100644 index 0000000..013c523 --- /dev/null +++ b/src/main/resources/xplatform.properties @@ -0,0 +1,5 @@ +api.app-key=1238912731982 +api.app-secret=1293102832190380 +api.authorize=http://114.242.227.113:10000/login/esc-sso/oauth2.0/authorize +api.get-token=http://114.242.227.113:10000/login/esc-sso/oauth2.0/accessToken +api.get-user=http://114.242.227.113:10000/login/esc-sso/oauth2.0/profile \ No newline at end of file diff --git a/xplatform.properties b/xplatform.properties new file mode 100644 index 0000000..d14a7d5 --- /dev/null +++ b/xplatform.properties @@ -0,0 +1,5 @@ +api.app-key= +api.app-secret= +api.authorize=xxx/oauth2.0/authorize +api.get-token=xxx/oauth2.0/accessToken +api.get-user=xxxo/oauth2.0/profile \ No newline at end of file