LAPTOP-SB56SG4Q\86185
3 years ago
10 changed files with 462 additions and 1 deletions
Binary file not shown.
@ -1,3 +1,6 @@
|
||||
# open-JSD-8928 |
||||
|
||||
JSD-8928 ADFS( saml模式 )单点 |
||||
JSD-8928 ADFS( saml模式 )单点\ |
||||
免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ |
||||
仅作为开发者学习参考使用!禁止用于任何商业用途!\ |
||||
为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 |
Binary file not shown.
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||
<plugin> |
||||
<id>com.fr.plugin.xxxx.tpyy.adfs</id> |
||||
<name><![CDATA[adfs单点]]></name> |
||||
<active>yes</active> |
||||
<version>1.2</version> |
||||
<env-version>10.0</env-version> |
||||
<jartime>2019-03-10</jartime> |
||||
<vendor>fr.open</vendor> |
||||
<description><![CDATA[adfs单点]]></description> |
||||
<change-notes> |
||||
[2021-11-10]【1.2】增加授权<br/> |
||||
[2021-11-08]【1.1】增加授权<br/> |
||||
[2021-10-29]【1.0】初始化插件。<br/> |
||||
</change-notes> |
||||
<function-recorder class="com.fr.plugin.xxxx.tpyy.adfs.SsoFilter"/> |
||||
<extra-decision> |
||||
<LogInOutEventProvider class="com.fr.plugin.xxxx.tpyy.adfs.CustomLogInOutEventProvider"/> |
||||
<GlobalRequestFilterProvider class="com.fr.plugin.xxxx.tpyy.adfs.SsoFilter"/> |
||||
</extra-decision> |
||||
</plugin> |
@ -0,0 +1,9 @@
|
||||
package com.fr.plugin.xxxx.tpyy.adfs; |
||||
|
||||
/** |
||||
* @author fr.open |
||||
* @date 2020/11/11 |
||||
*/ |
||||
public class Constants { |
||||
public static final String PLUGIN_ID = "com.fr.plugin.xxxx.tpyy.adfs"; |
||||
} |
@ -0,0 +1,19 @@
|
||||
package com.fr.plugin.xxxx.tpyy.adfs; |
||||
|
||||
import com.fr.decision.fun.impl.AbstractLogInOutEventProvider; |
||||
import com.fr.decision.webservice.login.LogInOutResultInfo; |
||||
import com.fr.general.PropertiesUtils; |
||||
import com.fr.log.FineLoggerFactory; |
||||
|
||||
/** |
||||
* @author fr.open |
||||
* @date 2020/11/11 |
||||
*/ |
||||
public class CustomLogInOutEventProvider extends AbstractLogInOutEventProvider { |
||||
@Override |
||||
public String logoutAction(LogInOutResultInfo result) { |
||||
String logout = PropertiesUtils.getProperties("conf").getProperty("logout"); |
||||
FineLoggerFactory.getLogger().info("get logout is {}",logout); |
||||
return logout; |
||||
} |
||||
} |
@ -0,0 +1,65 @@
|
||||
package com.fr.plugin.xxxx.tpyy.adfs; |
||||
|
||||
import com.fr.third.org.apache.commons.codec.binary.Base64; |
||||
|
||||
import javax.crypto.Cipher; |
||||
import javax.crypto.spec.SecretKeySpec; |
||||
import java.security.Key; |
||||
|
||||
/** |
||||
* @author fr.open |
||||
* @date 2020/11/11 |
||||
*/ |
||||
public class DesECBUtil { |
||||
/** |
||||
* 加密数据 |
||||
* |
||||
* @param encryptString |
||||
* @param encryptKey |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
public static String encryptDES(String encryptString, String encryptKey) throws Exception { |
||||
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); |
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(getKey(encryptKey), "DES")); |
||||
byte[] encryptedData = cipher.doFinal(encryptString.getBytes("UTF-8")); |
||||
return Base64.encodeBase64String(encryptedData); |
||||
} |
||||
|
||||
/** |
||||
* key 不足8位补位 |
||||
* |
||||
* @param |
||||
*/ |
||||
public static byte[] getKey(String keyRule) { |
||||
Key key = null; |
||||
byte[] keyByte = keyRule.getBytes(); |
||||
// 创建一个空的八位数组,默认情况下为0
|
||||
byte[] byteTemp = new byte[8]; |
||||
// 将用户指定的规则转换成八位数组
|
||||
for (int i = 0; i < byteTemp.length && i < keyByte.length; i++) { |
||||
byteTemp[i] = keyByte[i]; |
||||
} |
||||
key = new SecretKeySpec(byteTemp, "DES"); |
||||
return key.getEncoded(); |
||||
} |
||||
|
||||
/*** |
||||
* 解密数据 |
||||
* @param decryptString |
||||
* @param decryptKey |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
|
||||
public static String decryptDES(String decryptString, String decryptKey) throws Exception { |
||||
byte[] sourceBytes = Base64.decodeBase64(decryptString); |
||||
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); |
||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(getKey(decryptKey), "DES")); |
||||
byte[] decoded = cipher.doFinal(sourceBytes); |
||||
return new String(decoded, "UTF-8"); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,115 @@
|
||||
package com.fr.plugin.xxxx.tpyy.adfs; |
||||
|
||||
import javax.crypto.Cipher; |
||||
import javax.crypto.SecretKey; |
||||
import javax.crypto.SecretKeyFactory; |
||||
import javax.crypto.spec.DESKeySpec; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.security.SecureRandom; |
||||
|
||||
/** |
||||
* @author fr.open |
||||
* @date 2020/11/11 |
||||
*/ |
||||
public class KeysUtil { |
||||
/** 加密解密KEY */ |
||||
|
||||
/** 加密解密算法 */ |
||||
private final static String ALGORITHM = "DES"; |
||||
/** |
||||
* 加密 |
||||
* @param data 未加密数据 |
||||
* @param key 加密key |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
public static String encrypt(String data, String key) throws Exception { |
||||
|
||||
byte[] dataBytes = data.getBytes("UTF-8"); |
||||
|
||||
byte[] keyBytes = key.getBytes("UTF-8"); |
||||
|
||||
// DES算法要求有一个可信任的随机数源
|
||||
SecureRandom sr = new SecureRandom(); |
||||
|
||||
// 从原始密匙数据创建DESKeySpec对象
|
||||
DESKeySpec dks = new DESKeySpec(keyBytes); |
||||
|
||||
// 创建一个密匙工厂,然后用它把DESKeySpec转换成
|
||||
// 一个SecretKey对象
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM); |
||||
SecretKey securekey = keyFactory.generateSecret(dks); |
||||
|
||||
// Cipher对象实际完成加密操作
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM); |
||||
|
||||
// 用密匙初始化Cipher对象
|
||||
cipher.init(Cipher.ENCRYPT_MODE, securekey, sr); |
||||
|
||||
// 正式执行加密操作
|
||||
byte[] bytes = cipher.doFinal(dataBytes); |
||||
return byte2hex(bytes); |
||||
} |
||||
|
||||
/** |
||||
* 解密 |
||||
* @param data 加密后的数据 |
||||
* @param key 加密key |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
public static String decrypt(String data, String key) throws Exception { |
||||
|
||||
byte[] dataBytes = data.getBytes("UTF-8"); |
||||
byte[] hex2byte = hex2byte(dataBytes); |
||||
byte[] keyBytes = key.getBytes("UTF-8"); |
||||
|
||||
// DES算法要求有一个可信任的随机数源
|
||||
SecureRandom sr = new SecureRandom(); |
||||
|
||||
// 从原始密匙数据创建一个DESKeySpec对象
|
||||
DESKeySpec dks = new DESKeySpec(keyBytes); |
||||
|
||||
// 创建一个密匙工厂,然后用它把DESKeySpec对象转换成
|
||||
// 一个SecretKey对象
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM); |
||||
SecretKey securekey = keyFactory.generateSecret(dks); |
||||
|
||||
// Cipher对象实际完成解密操作
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM); |
||||
|
||||
// 用密匙初始化Cipher对象
|
||||
cipher.init(Cipher.DECRYPT_MODE, securekey, sr); |
||||
|
||||
// 正式执行解密操作
|
||||
byte[] doFinal = cipher.doFinal(hex2byte); |
||||
|
||||
return new String(doFinal,"UTF-8"); |
||||
} |
||||
|
||||
private static byte[] hex2byte(byte[] b) throws UnsupportedEncodingException { |
||||
if ((b.length % 2) != 0) { |
||||
throw new IllegalArgumentException("长度不是偶数"); |
||||
} |
||||
byte[] b2 = new byte[b.length / 2]; |
||||
for (int n = 0; n < b.length; n += 2) { |
||||
String item = new String(b, n, 2, "UTF-8"); |
||||
b2[n / 2] = (byte) Integer.parseInt(item, 16); |
||||
} |
||||
return b2; |
||||
} |
||||
|
||||
private static String byte2hex(byte[] b) { |
||||
StringBuilder bulid = new StringBuilder(); |
||||
String stmp = ""; |
||||
for (int n = 0; n < b.length; n++) { |
||||
stmp = (Integer.toHexString(b[n] & 0XFF)); |
||||
if (stmp.length() == 1) { |
||||
bulid.append("0").append(stmp); |
||||
} else { |
||||
bulid.append(stmp); |
||||
} |
||||
} |
||||
return bulid.toString().toUpperCase(); |
||||
} |
||||
} |
@ -0,0 +1,225 @@
|
||||
/* |
||||
* The contents of this file are subject to the Mozilla Public |
||||
* License Version 1.1 (the "License"); you may not use this |
||||
* file except in compliance with the License. You may obtain |
||||
* a copy of the License at http://www.mozilla.org/MPL/
|
||||
* |
||||
* Software distributed under the License is distributed on an |
||||
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express |
||||
* or implied. See the License for the specific language governing |
||||
* rights and limitations under the License. |
||||
* |
||||
* |
||||
* The Original Code is OIOSAML Java Service Provider. |
||||
* |
||||
* The Initial Developer of the Original Code is Trifork A/S. Portions |
||||
* created by Trifork A/S are Copyright (C) 2008 Danish National IT |
||||
* and Telecom Agency (http://www.itst.dk). All Rights Reserved.
|
||||
* |
||||
* Contributor(s): |
||||
* Joakim Recht <jre@trifork.com> |
||||
* Rolf Njor Jensen <rolf@trifork.com> |
||||
* Aage Nielsen <ani@openminds.dk> |
||||
* |
||||
*/ |
||||
package com.fr.plugin.xxxx.tpyy.adfs; |
||||
|
||||
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.utils.WebServiceUtils; |
||||
import com.fr.decision.webservice.v10.config.ConfigService; |
||||
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.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.record.analyzer.EnableMetrics; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.fun.Authorize; |
||||
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.io.IOException; |
||||
import java.io.PrintWriter; |
||||
import java.time.Instant; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author fr.open |
||||
* @date 2020/11/11 |
||||
*/ |
||||
@FunctionRecorder |
||||
@Authorize(callSignKey = Constants.PLUGIN_ID) |
||||
@EnableMetrics |
||||
public class SsoFilter extends AbstractGlobalRequestFilterProvider { |
||||
|
||||
@Override |
||||
public String filterName() { |
||||
return "tpyy"; |
||||
} |
||||
|
||||
@Override |
||||
@Focus(id = Constants.PLUGIN_ID, text = "adfs", source = Original.PLUGIN) |
||||
public String[] urlPatterns() { |
||||
if (PluginContexts.currentContext().isAvailable()) { |
||||
String servletPathName = "decision"; |
||||
try { |
||||
servletPathName = ConfigService.getInstance().getBasicParam().getServletPathName(); |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return new String[]{ |
||||
"/" + servletPathName, |
||||
"/" + servletPathName + "/view/report", |
||||
|
||||
}; |
||||
} else { |
||||
return new String[0]; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { |
||||
String samlToken = req.getParameter("samlToken"); |
||||
try { |
||||
if (isAccept(req) || isLogin(req)) { |
||||
next(req, res, filterChain); |
||||
return; |
||||
} |
||||
if (StringUtils.isNotBlank(samlToken)) { |
||||
String key = PropertiesUtils.getProperties("conf").getProperty("key"); |
||||
FineLoggerFactory.getLogger().info("get key config is {}", key); |
||||
String format = DesECBUtil.decryptDES(samlToken, key); |
||||
FineLoggerFactory.getLogger().info("decode format is {}", format); |
||||
String user = format.split("_")[0]; |
||||
String timeout = format.split("_")[1]; |
||||
String out = PropertiesUtils.getProperties("conf").getProperty("timeout"); |
||||
FineLoggerFactory.getLogger().info("get timeout config is {}", out); |
||||
if (StringUtils.isBlank(out)) { |
||||
out = "10"; |
||||
} |
||||
if (Instant.ofEpochMilli(Long.valueOf(timeout)).plusSeconds(Long.valueOf(out)).isBefore(Instant.now())) { |
||||
setError(res, "format timeout!"); |
||||
return; |
||||
} |
||||
if (!checkUser(user)) { |
||||
setError(res, "user: 【" + user + "】 not exist !"); |
||||
return; |
||||
} |
||||
loginFromToken(req, res, user); |
||||
} |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
FineLoggerFactory.getLogger().info("current request {} not login send redirect login", getUrl(req)); |
||||
String saml = PropertiesUtils.getProperties("conf").getProperty("saml"); |
||||
try { |
||||
res.sendRedirect(saml); |
||||
} catch (IOException e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
private String getUrl(HttpServletRequest req) { |
||||
String requestURL = req.getRequestURL().toString(); |
||||
String queryString = req.getQueryString(); |
||||
if (StringUtils.isNotBlank(queryString)) { |
||||
requestURL += "?" + queryString; |
||||
} |
||||
return requestURL; |
||||
} |
||||
|
||||
private boolean isLogin(HttpServletRequest request) throws Exception { |
||||
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; |
||||
} |
||||
|
||||
private boolean isAccept(HttpServletRequest req) { |
||||
if (req.getRequestURI().endsWith("/view/form") || req.getRequestURI().endsWith("/view/report")) { |
||||
return StringUtils.isBlank(WebUtils.getHTTPRequestParameter(req, "viewlet")) && StringUtils.isNotBlank(req.getParameter("samlToken")); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private void next(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { |
||||
try { |
||||
chain.doFilter(request, response); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
private boolean checkUser(String username) { |
||||
User user = null; |
||||
try { |
||||
user = UserService.getInstance().getUserByUserName(username); |
||||
FineLoggerFactory.getLogger().info("get user:" + user); |
||||
if (user != null) { |
||||
return true; |
||||
} |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
|
||||
|
||||
return false; |
||||
} |
||||
|
||||
private boolean loginFromToken(HttpServletRequest req, HttpServletResponse res, String username) throws Exception { |
||||
try { |
||||
if (StringUtils.isNotEmpty(username)) { |
||||
FineLoggerFactory.getLogger().info("current username:" + username); |
||||
String token = LoginService.getInstance().login(req, res, username); |
||||
FineLoggerFactory.getLogger().info("get login token:" + token); |
||||
req.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token); |
||||
FineLoggerFactory.getLogger().info("username:" + username + "login success"); |
||||
return true; |
||||
} else { |
||||
FineLoggerFactory.getLogger().warn("username is null!"); |
||||
return false; |
||||
} |
||||
} catch (Exception e) { |
||||
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
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); |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue