LAPTOP-SB56SG4Q\86185
3 years ago
13 changed files with 571 additions and 2 deletions
Binary file not shown.
@ -1,3 +1,6 @@
|
||||
# open-JSD-8380 |
||||
# open-JSD-8380 单点登录 |
||||
|
||||
JSD-8380 |
||||
JSD-8380 任务开源材料\ |
||||
免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ |
||||
仅作为开发者学习参考使用!禁止用于任何商业用途!\ |
||||
为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 |
@ -0,0 +1,5 @@
|
||||
env=uat |
||||
devUser=1 |
||||
isLocal=true |
||||
appId=xxxxxxxxxxxxxxxx |
||||
appSecret=xxxxxxxxxxxxxxxxxxx |
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||
<plugin> |
||||
<id>com.fr.plugin.crc.sso</id> |
||||
<name><![CDATA[单点登录]]></name> |
||||
<active>yes</active> |
||||
<version>1.17</version> |
||||
<env-version>10.0</env-version> |
||||
<jartime>2018-07-31</jartime> |
||||
<vendor>fr.open</vendor> |
||||
<description><![CDATA[单点登录]]></description> |
||||
<change-notes><![CDATA[]]></change-notes> |
||||
<extra-decision> |
||||
<GlobalRequestFilterProvider class="com.fr.plugin.crc.sso.SsoFilter"/> |
||||
<GlobalRequestFilterProvider class="com.fr.plugin.crc.sso.RIGFilter"/> |
||||
<!-- <LogInOutEventProvider class="com.fr.plugin.crc.sso.CustomLogInOutEventProvider"/>--> |
||||
</extra-decision> |
||||
<function-recorder class="com.fr.plugin.crc.sso.SsoFilter"/> |
||||
</plugin> |
@ -0,0 +1,6 @@
|
||||
此次完成功能如下 |
||||
a、单点登录 |
||||
|
||||
1、将压缩文件解压后的crc.properties配置文件拷贝至 %部署路径%/WEB-INF/resources, 并修改相应的属性配置 |
||||
2、安装本插件,插件安装见连接http://help.finereport.com/doc-view-2198.html |
||||
3、进入系统测试单点登录,访问地址为http://ip:port/webroot/decision/crc/sso |
@ -0,0 +1,132 @@
|
||||
package com.fr.plugin.crc.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.utils.DecisionStatusService; |
||||
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 com.fr.web.utils.WebUtils; |
||||
|
||||
import javax.servlet.FilterChain; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
|
||||
/** |
||||
* @author fr.open |
||||
* @since 2021/7/29 |
||||
*/ |
||||
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) { |
||||
FineLoggerFactory.getLogger().warn("Property[" + key + "] value is blank."); |
||||
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 +"\"登录失败"); |
||||
} |
||||
} |
||||
|
||||
public static boolean isMobileDevice(HttpServletRequest request) { |
||||
if (WebUtils.getDevice(request).isMobile()) { |
||||
FineLoggerFactory.getLogger().info("current request is is mobile request ,url is {}", request.getRequestURI()); |
||||
return true; |
||||
} |
||||
String requestHeader = request.getHeader("user-agent"); |
||||
String[] deviceArray = new String[]{"android", "iphone", "ipad", "ios", "windows phone", "wechat"}; |
||||
if (requestHeader == null) { |
||||
return false; |
||||
} |
||||
requestHeader = requestHeader.toLowerCase(); |
||||
for (int i = 0; i < deviceArray.length; i++) { |
||||
if (requestHeader.toLowerCase().contains(deviceArray[i])) { |
||||
FineLoggerFactory.getLogger().info("current request:{} is mobile request!", request.getRequestURI()); |
||||
return true; |
||||
} |
||||
} |
||||
String op = WebUtils.getHTTPRequestParameter(request, "op"); |
||||
return StringUtils.isNotBlank(op) && StringUtils.equals("h5",op); |
||||
} |
||||
|
||||
public static void cacheParams(String key, Map<String, String> values) { |
||||
try { |
||||
DecisionStatusService.originUrlStatusService().put(key, values); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
public static String getCachedParam(String key, String name) { |
||||
try { |
||||
Map<String, String> values = DecisionStatusService.originUrlStatusService().get(key); |
||||
return values.get(name); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,11 @@
|
||||
package com.fr.plugin.crc.sso; |
||||
|
||||
/** |
||||
* @Author fr.open |
||||
* @Date 2021/9/24 |
||||
* @Description |
||||
**/ |
||||
public class PluginConstants { |
||||
|
||||
public static final String PLUGIN_ID = "com.fr.plugin.crc.sso"; |
||||
} |
@ -0,0 +1,173 @@
|
||||
package com.fr.plugin.crc.sso; |
||||
|
||||
import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; |
||||
import com.fr.decision.webservice.utils.WebServiceUtils; |
||||
import com.fr.general.PropertiesUtils; |
||||
import com.fr.general.http.HttpToolbox; |
||||
import com.fr.intelli.record.Focus; |
||||
import com.fr.intelli.record.Original; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.locale.InterProviderFactory; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.plugin.context.PluginContexts; |
||||
import com.fr.plugin.transform.FunctionRecorder; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.fun.Authorize; |
||||
import com.fr.third.org.apache.commons.codec.binary.Base64; |
||||
import com.fr.web.utils.WebUtils; |
||||
|
||||
import javax.servlet.FilterChain; |
||||
import javax.servlet.http.Cookie; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.PrintWriter; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
import java.util.stream.Stream; |
||||
|
||||
import static com.fr.plugin.crc.sso.CommonUtils.*; |
||||
|
||||
/** |
||||
* @author fr.open |
||||
* @since 2021/9/2 |
||||
*/ |
||||
@FunctionRecorder |
||||
@Authorize(callSignKey = PluginConstants.PLUGIN_ID) |
||||
public class RIGFilter extends AbstractGlobalRequestFilterProvider { |
||||
|
||||
private final static String[] NOT_FILTER = { |
||||
"/decision/file", |
||||
"/decision/resources", |
||||
"/system", |
||||
"/materials.min.js.map", |
||||
"/remote", |
||||
"/login", |
||||
"/url/mobile", |
||||
"/crc/sso", |
||||
"/login/config" |
||||
}; |
||||
|
||||
private String apiLogin; |
||||
|
||||
private String apiGetToken; |
||||
|
||||
private String clientId; |
||||
|
||||
private String clientSecret; |
||||
|
||||
private String redirectURI; |
||||
|
||||
private void initParams() { |
||||
Properties props = PropertiesUtils.getProperties("crc"); |
||||
apiLogin = getProperty(props, "rig.api.login", false); |
||||
apiGetToken = getProperty(props, "rig.api.get-token", false); |
||||
clientId = getProperty(props, "rig.api.client_id", false); |
||||
clientSecret = getProperty(props, "rig.api.client_secret", false); |
||||
redirectURI = getProperty(props, "rig.api.redirect_uri", false); |
||||
} |
||||
|
||||
@Override |
||||
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { |
||||
if (isAccept(request)) { |
||||
next(request, response, chain); |
||||
return; |
||||
} |
||||
try { |
||||
initParams(); |
||||
String ticket = request.getParameter("ticket"); |
||||
if (StringUtils.isBlank(ticket)) { |
||||
jumpAuthorize(request, response); |
||||
return; |
||||
} |
||||
String[] splitted = ticket.split("\\."); |
||||
String code = splitted[splitted.length - 1]; |
||||
login(getUsername(getToken(code, response)), request, response); |
||||
next(request, response, chain); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error("RIG sso >>> 处理单点登陆时发生错误", e); |
||||
setError(response, e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
private String getUsername(String token) { |
||||
try { |
||||
FineLoggerFactory.getLogger().info("RIG sso >>> 获取到的token值为 ==> \"{}\"", token); |
||||
String res = new String(Base64.decodeBase64(token.split("\\.")[1])); |
||||
FineLoggerFactory.getLogger().info("RIG sso >>> Base64 decode 后的token值为 ==> \"{}\"", res); |
||||
JSONObject body = new JSONObject(res); |
||||
if (body.has("user_name")) { |
||||
return body.getString("user_name"); |
||||
} |
||||
throw new RuntimeException("解析token获取用户名失败"); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException("获取token失败, " + e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
private String getToken(String code, HttpServletResponse response) throws IOException { |
||||
String api = String.format("%s?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s", apiGetToken, clientId, clientSecret, code); |
||||
FineLoggerFactory.getLogger().info("RIG sso >>> 获取 token 请求地址 ==> \"{}\"", api); |
||||
String res = HttpToolbox.post(api, Collections.emptyMap()); |
||||
JSONObject body = new JSONObject(res); |
||||
if (body.has("refresh_token")) { |
||||
Cookie c = new Cookie("RIG_REFRESH_TOKEN", body.getString("refresh_token")); |
||||
c.setPath("/"); |
||||
response.addCookie(c); |
||||
} |
||||
if (body.has("access_token")) { |
||||
Cookie c = new Cookie("RIG_ACCESS_TOKEN", body.getString("access_token")); |
||||
c.setPath("/"); |
||||
response.addCookie(c); |
||||
return body.getString("access_token"); |
||||
} |
||||
throw new RuntimeException("获取 token 接口请求失败, " + res); |
||||
} |
||||
|
||||
private void jumpAuthorize(HttpServletRequest request, HttpServletResponse response) throws IOException { |
||||
String login = String.format("%s?service=%s", apiLogin, redirectURI); |
||||
FineLoggerFactory.getLogger().info("RIG sso >>> 跳转到登陆页面 ==> \"{}\"", login); |
||||
response.sendRedirect(login); |
||||
} |
||||
|
||||
@Override |
||||
public String filterName() { |
||||
return "rigFilter"; |
||||
} |
||||
|
||||
@Override |
||||
@Focus(id = PluginConstants.PLUGIN_ID, text = "单点登录", source = Original.PLUGIN) |
||||
public String[] urlPatterns() { |
||||
if(!PluginContexts.currentContext().isAvailable()){ |
||||
return new String[0]; |
||||
} |
||||
return new String[]{"/*"}; |
||||
} |
||||
|
||||
public boolean isAccept(HttpServletRequest request) { |
||||
String url = request.getRequestURL().toString(); |
||||
if (Stream.of(NOT_FILTER).anyMatch(url::contains)) { |
||||
return true; |
||||
} |
||||
return isLogin(request); |
||||
} |
||||
|
||||
private void setError(HttpServletResponse res, String reason) { |
||||
try { |
||||
PrintWriter printWriter = WebUtils.createPrintWriter(res); |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("result", InterProviderFactory.getProvider().getLocText("Fine-Engine_Error_Page_Result")); |
||||
map.put("reason", reason); |
||||
map.put("solution", InterProviderFactory.getProvider().getLocText("Fine-Engine_Please_Contact_Platform_Admin")); |
||||
String page = WebServiceUtils.parseWebPageResourceSafe("com/fr/web/controller/decision/entrance/resources/unavailable.html", map); |
||||
printWriter.write(page); |
||||
printWriter.flush(); |
||||
printWriter.close(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,143 @@
|
||||
package com.fr.plugin.crc.sso; |
||||
|
||||
import com.fr.base.TemplateUtils; |
||||
import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; |
||||
import com.fr.decision.webservice.utils.WebServiceUtils; |
||||
import com.fr.general.PropertiesUtils; |
||||
import com.fr.intelli.record.Focus; |
||||
import com.fr.intelli.record.Original; |
||||
import com.fr.locale.InterProviderFactory; |
||||
import com.fr.log.FineLoggerFactory; |
||||
import com.fr.plugin.context.PluginContexts; |
||||
import com.fr.plugin.transform.FunctionRecorder; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.fun.Authorize; |
||||
import com.fr.web.utils.WebUtils; |
||||
|
||||
import javax.servlet.FilterChain; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.io.PrintWriter; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
import java.util.stream.Stream; |
||||
|
||||
import static com.fr.plugin.crc.sso.CommonUtils.*; |
||||
|
||||
/** |
||||
* @author fr.open |
||||
* @since 2021/7/29 |
||||
*/ |
||||
@FunctionRecorder |
||||
@Authorize(callSignKey = PluginConstants.PLUGIN_ID) |
||||
public class SsoFilter extends AbstractGlobalRequestFilterProvider { |
||||
|
||||
private static String[] NOT_FILTER = { |
||||
"/decision/file", |
||||
"/decision/resources", |
||||
"/system", |
||||
"/materials.min.js.map", |
||||
"/remote", |
||||
"/login", |
||||
"/url/mobile", |
||||
"/login/config" |
||||
}; |
||||
|
||||
private final String env; |
||||
|
||||
private final String devUser; |
||||
|
||||
private final boolean isLocal; |
||||
|
||||
private final String appId; |
||||
|
||||
private final String appSecret; |
||||
|
||||
public SsoFilter() { |
||||
Properties props = PropertiesUtils.getProperties("crc"); |
||||
this.env = getProperty(props, "env", true); |
||||
this.devUser = getProperty(props, "devUser", true); |
||||
this.isLocal = Boolean.parseBoolean(getProperty(props, "isLocal", true)); |
||||
this.appId = getProperty(props, "appId", true); |
||||
this.appSecret = getProperty(props, "appSecret", true); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { |
||||
if (request.getRequestURI().endsWith("/crc/sso")) { |
||||
FineLoggerFactory.getLogger().info("sso >> 进入Filter, 当前URL为: \"{}\"", request.getRequestURI() + "?" + request.getQueryString()); |
||||
try { |
||||
String root = TemplateUtils.render("${fineServletURL}"); |
||||
if (isAccept(request)) { |
||||
FineLoggerFactory.getLogger().info("sso >> 已登录, 跳转到 ==> {}", root + "/url/mobile"); |
||||
response.sendRedirect(root + "/url/mobile"); |
||||
return; |
||||
} |
||||
|
||||
String username = request.getParameter("username"); |
||||
if (StringUtils.isBlank(username)) { |
||||
FineLoggerFactory.getLogger().info("sso >> 未获取到用户名, 跳转到 ==> crc-sso.html"); |
||||
PrintWriter writer = WebUtils.createPrintWriter(response); |
||||
Map<String, Object> attributes = new HashMap<>(); |
||||
attributes.put("root", root); |
||||
attributes.put("env", env); |
||||
attributes.put("devUser", devUser); |
||||
attributes.put("appId", appId); |
||||
attributes.put("appSecret", appSecret); |
||||
attributes.put("isLocal", isLocal); |
||||
String page = WebServiceUtils.parseWebPageResource("com/fr/plugin/crc-sso.html", attributes); |
||||
writer.write(page); |
||||
writer.flush(); |
||||
writer.close(); |
||||
} else { |
||||
FineLoggerFactory.getLogger().info("sso >> 获取到有用户名, 用户名为: \"{}\" 执行登录并跳转到 ==> {}", username, root + "/url/mobile"); |
||||
login(username, request, response); |
||||
response.sendRedirect(root + "/url/mobile"); |
||||
} |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error("单点登录处理失败.", e); |
||||
setError(response, e.getMessage()); |
||||
} |
||||
} else { |
||||
next(request, response, chain); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public String filterName() { |
||||
return "sso"; |
||||
} |
||||
|
||||
@Override |
||||
@Focus(id = PluginConstants.PLUGIN_ID, text = "单点登录", source = Original.PLUGIN) |
||||
public String[] urlPatterns() { |
||||
if(!PluginContexts.currentContext().isAvailable()){ |
||||
return new String[0]; |
||||
} |
||||
return new String[]{"/decision/crc/sso"}; |
||||
} |
||||
|
||||
private boolean isAccept(HttpServletRequest request) { |
||||
return isLogin(request); |
||||
} |
||||
|
||||
private void setError(HttpServletResponse res, String reason) { |
||||
try { |
||||
PrintWriter printWriter = WebUtils.createPrintWriter(res); |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("result", InterProviderFactory.getProvider().getLocText("Fine-Engine_Error_Page_Result")); |
||||
map.put("reason", reason); |
||||
map.put("solution", InterProviderFactory.getProvider().getLocText("Fine-Engine_Please_Contact_Platform_Admin")); |
||||
String page = WebServiceUtils.parseWebPageResourceSafe("com/fr/web/controller/decision/entrance/resources/unavailable.html", map); |
||||
printWriter.write(page); |
||||
printWriter.flush(); |
||||
printWriter.close(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="zh"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>单点登录</title> |
||||
<script type="text/javascript" src="https://s0.pstatp.com/ee/lark/js_sdk/h5-js-sdk-1.4.11.js"></script> |
||||
<script type="text/javascript" src="${root}/file?path=com/fr/plugin/index.js&type=plain&parser=plain"></script> |
||||
<script> |
||||
window.onload = function() { |
||||
document.writeln("页面加载完成......, 当前时间为 ==>" + new Date().getTime()) |
||||
document.writeln("<br/>") |
||||
|
||||
var runworkH5 = new window.runworkHelp.RunWorkH5({ |
||||
env: "${env}", |
||||
isLocal: ${isLocal}, |
||||
devUser: "${devUser}", |
||||
appId: "${appId}", |
||||
appSecret: "${appSecret}", |
||||
isHrInfo: true |
||||
}); |
||||
document.writeln("runworkH5 js_sdk 初始化完成......<br/>") |
||||
|
||||
runworkH5.initReady().then(() => { |
||||
document.writeln("runworkH5获取用户信息成功, 用户名为 ==> " + runworkH5.ldap); |
||||
document.writeln("<br/>") |
||||
document.writeln("当前时间为 ==>" + new Date().getTime()); |
||||
document.writeln("<br/>") |
||||
location.href = "${root}/crc/sso?username=" + runworkH5.ldap; |
||||
}) |
||||
} |
||||
</script> |
||||
</head> |
||||
<body></body> |
||||
</html> |
File diff suppressed because one or more lines are too long
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="zh"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>单点登录-回填cookie</title> |
||||
<script> |
||||
window.onload = function() { |
||||
const token = "${token}"; |
||||
document.cookie = "fine_auth_token=" + token; |
||||
location.href = "${root}"; |
||||
} |
||||
</script> |
||||
</head> |
||||
<body></body> |
||||
</html> |
Loading…
Reference in new issue