9 changed files with 369 additions and 1 deletions
Binary file not shown.
@ -1,3 +1,6 @@ |
|||||||
# open-JSD-8277 |
# open-JSD-8277 |
||||||
|
|
||||||
JSD-8277 开源任务材料 |
JSD-8277 开源任务材料\ |
||||||
|
免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ |
||||||
|
仅作为开发者学习参考使用!禁止用于任何商业用途!\ |
||||||
|
为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 |
@ -0,0 +1,17 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||||
|
<plugin> |
||||||
|
<id>com.fr.plugin.xxxx.sso</id> |
||||||
|
<name><![CDATA[xxxx单点登录]]></name> |
||||||
|
<active>yes</active> |
||||||
|
<version>1.1</version> |
||||||
|
<env-version>10.0</env-version> |
||||||
|
<jartime>2018-07-31</jartime> |
||||||
|
<vendor>fr.open</vendor> |
||||||
|
<description><![CDATA[xxxx单点登录]]></description> |
||||||
|
<change-notes><![CDATA[]]></change-notes> |
||||||
|
<extra-decision> |
||||||
|
<GlobalRequestFilterProvider class="com.fr.plugin.xxxx.sso.SsoFilter"/> |
||||||
|
<LogInOutEventProvider class="com.fr.plugin.xxxx.sso.CustomLogInOutEventProvider"/> |
||||||
|
</extra-decision> |
||||||
|
<function-recorder class="com.fr.plugin.xxxx.sso.SsoFilter"/> |
||||||
|
</plugin> |
@ -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 |
@ -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 +"\"登录失败"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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<String, Object> params = new HashMap<>(); |
||||||
|
params.put("client_id", apiClientId); |
||||||
|
params.put("client_secret", apiClientSecret); |
||||||
|
params.put("code", code); |
||||||
|
params.put("grant_type", "authorization_code"); |
||||||
|
Map<String, String> 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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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 |
@ -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 |
Loading…
Reference in new issue