commit df53a1bbdbd2cfd949ab4f7ef380d007ae17ecfa Author: pioneer Date: Mon Dec 12 15:35:26 2022 +0800 open diff --git a/README.md b/README.md new file mode 100644 index 0000000..c286aba --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# open-JSD-10334 + +JSD-10334 h5单点集成\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系【pioneer】处理。 \ No newline at end of file diff --git a/lib/bcprov-jdk15on-1.56.jar b/lib/bcprov-jdk15on-1.56.jar new file mode 100644 index 0000000..ffd08d6 Binary files /dev/null and b/lib/bcprov-jdk15on-1.56.jar differ diff --git a/lib/finekit-10.0.jar b/lib/finekit-10.0.jar new file mode 100644 index 0000000..f4482fc Binary files /dev/null and b/lib/finekit-10.0.jar differ diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..872d29c --- /dev/null +++ b/plugin.xml @@ -0,0 +1,24 @@ + + + com.fr.plugin.cmd.sso + + yes + 1.2 + 10.0 + 2018-07-31 + fr.open + + + com.fr.plugin.cmd + + com.fanruan.api + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/cmd/LocaleFinder.java b/src/main/java/com/fr/plugin/cmd/LocaleFinder.java new file mode 100644 index 0000000..7cf7441 --- /dev/null +++ b/src/main/java/com/fr/plugin/cmd/LocaleFinder.java @@ -0,0 +1,37 @@ + /* + * Copyright (C), 2018-2020 + * Project: starter + * FileName: LocaleFinder + * Author: xx + * Date: 2020/8/31 22:19 + */ + package com.fr.plugin.cmd; + + import com.fr.intelli.record.Focus; + import com.fr.intelli.record.Original; + import com.fr.record.analyzer.EnableMetrics; + import com.fr.stable.fun.impl.AbstractLocaleFinder; + + import static com.fr.plugin.cmd.config.CmdConfig.PLUGIN_ID; + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + @EnableMetrics + public class LocaleFinder extends AbstractLocaleFinder { + + @Override + @Focus(id = PLUGIN_ID, text = "Plugin-cmd", source = Original.PLUGIN) + public String find() { + return "com/fr/plugin/cmd/locale/lang"; + } + + @Override + public int currentAPILevel() { + return CURRENT_LEVEL; + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/cmd/PluginMonitor.java b/src/main/java/com/fr/plugin/cmd/PluginMonitor.java new file mode 100644 index 0000000..6a27288 --- /dev/null +++ b/src/main/java/com/fr/plugin/cmd/PluginMonitor.java @@ -0,0 +1,34 @@ + /* + * Copyright (C), 2018-2021 + * Project: starter + * FileName: PluginMonitor + * Author: xx + * Date: 2021/3/30 15:10 + */ + package com.fr.plugin.cmd; + + import com.fr.plugin.cmd.config.CmdConfig; + import com.fr.plugin.context.PluginContext; + import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor; + + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + public class PluginMonitor extends AbstractPluginLifecycleMonitor { + public PluginMonitor() { + } + + @Override + public void afterRun(PluginContext pluginContext) { + CmdConfig.getInstance(); + } + + @Override + public void beforeStop(PluginContext pluginContext) { + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/cmd/config/CmdConfig.java b/src/main/java/com/fr/plugin/cmd/config/CmdConfig.java new file mode 100644 index 0000000..e6ac69d --- /dev/null +++ b/src/main/java/com/fr/plugin/cmd/config/CmdConfig.java @@ -0,0 +1,44 @@ + /* + * Copyright (C), 2018-2021 + * Project: starter + * FileName: IbahConfig + * Author: xx + * Date: 2021/3/30 9:38 + */ + package com.fr.plugin.cmd.config; + + import com.fr.config.*; + import com.fr.config.holder.Conf; + import com.fr.config.holder.factory.Holders; + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + @Visualization(category = "Plugin-cmd_Group") + public class CmdConfig extends DefaultConfiguration { + public static final String PLUGIN_ID = "com.fr.plugin.cmd.sso"; + public static final String MOBILE_INTERFACE = "https://xx/MobileInterface.aspx?Action=Authorization"; + + private static volatile CmdConfig config = null; + @Identifier(value = "uriBase", name = "Plugin-cmd_Config_UriBase", description = "Plugin-cmd_Config_UriBase_Description", status = Status.SHOW) + private final Conf uriBase = Holders.simple(MOBILE_INTERFACE); + + public static CmdConfig getInstance() { + if (config == null) { + config = ConfigContext.getConfigInstance(CmdConfig.class); + } + return config; + } + + public String getUriBase() { + return uriBase.get(); + } + + public void setUriBase(String uriBase) { + this.uriBase.set(uriBase); + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/cmd/request/OAuthLogin.java b/src/main/java/com/fr/plugin/cmd/request/OAuthLogin.java new file mode 100644 index 0000000..140a5d8 --- /dev/null +++ b/src/main/java/com/fr/plugin/cmd/request/OAuthLogin.java @@ -0,0 +1,244 @@ + /* + * Copyright (C), 2018-2021 + * Project: starter + * FileName: OAuthLogin + * Author: xx + * Date: 2021/3/30 22:09 + */ + package com.fr.plugin.cmd.request; + + import com.fanruan.api.decision.login.LoginKit; + import com.fanruan.api.decision.user.UserKit; + import com.fanruan.api.i18n.I18nKit; + import com.fanruan.api.log.LogKit; + import com.fanruan.api.net.NetworkKit; + import com.fanruan.api.net.http.HttpKit; + import com.fanruan.api.util.StringKit; + import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; + import com.fr.decision.webservice.utils.DecisionServiceConstants; + import com.fr.decision.webservice.v10.login.LoginService; + import com.fr.general.ComparatorUtils; + import com.fr.json.JSONObject; + import com.fr.plugin.cmd.config.CmdConfig; + import com.fr.plugin.cmd.utils.AESUtil; + import com.fr.plugin.context.PluginContexts; + import com.fr.stable.fun.Authorize; + import com.fr.third.org.apache.http.NameValuePair; + import com.fr.third.org.apache.http.client.utils.URIBuilder; + import com.fr.third.org.apache.http.entity.StringEntity; + import com.fr.web.utils.WebUtils; + + import javax.servlet.FilterChain; + import javax.servlet.FilterConfig; + import javax.servlet.http.HttpServletRequest; + import javax.servlet.http.HttpServletResponse; + import java.net.URI; + import java.net.URISyntaxException; + import java.util.HashMap; + import java.util.List; + import java.util.Map; + + import static com.fr.plugin.cmd.config.CmdConfig.PLUGIN_ID; + + /** + *
+ * + * + * @author xx + * @since 1.0.0 + */ + @Authorize(callSignKey = PLUGIN_ID) + public class OAuthLogin extends AbstractGlobalRequestFilterProvider { + public static final String REFRESH_AUTH = "/refreshMobileAuth"; + private CmdConfig config; + + /** + * 过滤器名称 + * + * @return + */ + @Override + public String filterName() { + return "cmdFilter"; + } + + /** + * 过滤规则 + * + * @return + */ + @Override + public String[] urlPatterns() { + return new String[]{"/decision", "/decision/url/mobile", "/decision/view/form", "/decision/view/report", "/decision/v10/entry/access/*"}; + } + + /** + * 过滤器初始化 + * + * @param filterConfig + */ + @Override + public void init(FilterConfig filterConfig) { + this.config = CmdConfig.getInstance(); + super.init(filterConfig); + } + + /** + * 过滤器处理 + * + * @param request + * @param response + * @param filterChain + */ + @Override + public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { + try { + if (operation(request, response)) { + filterChain.doFilter(request, response); + } + } catch (Exception e) { + LogKit.error(e.getMessage(), e); + } + } + + /** + * 用户验证登陆操作 + * + * @param req + * @param res + * @throws Exception + */ + private boolean operation(HttpServletRequest req, HttpServletResponse res) throws Exception { + if (LoginService.getInstance().isLogged(req)) { + return true; + } + String mobileToken = decodeStr(NetworkKit.getHTTPRequestParameter(req, "Token")); + String mobileUserName = NetworkKit.getHTTPRequestParameter(req, "UserAccount"); + LogKit.info("cmd-OAuthLogin-operation-mobileToken:{}, mobileUserName:{}", mobileToken, mobileUserName); + if (StringKit.isBlank(mobileToken) || StringKit.isBlank(mobileUserName)) { + return true; + } + String username = getUsername(mobileToken, mobileUserName); + if (StringKit.isEmpty(username) || !UserKit.existUsername(username)) { + return true; + } + if (!PluginContexts.currentContext().isAvailable()) { + LogKit.error(I18nKit.getLocText("Plugin-cmd_Licence_Expired")); + return true; + } + String tokenFR = LoginKit.login(req, res, username); + req.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, tokenFR); + String refreshMobileUrl = resolveURL(req.getRequestURL().toString(), WebUtils.createServletURL(req) + REFRESH_AUTH); + refreshMobileAuth(mobileToken, mobileUserName, refreshMobileUrl); + res.sendRedirect(removeToken(req)); + return false; + } + + private String resolveURL(String url, String path) throws Exception { + URI uri = new URI(url); + return uri.resolve(path).toString(); + } + + /** + * 处理加号和等号特殊字符 + * + * @param str + * @return + */ + private String decodeStr(String str) { + if (StringKit.isBlank(str)) { + return str; + } + return str.replaceAll(" ", "+"); + } + + /** + * 通过凭证获得username + * + * @param mobileToken + * @param mobileUserName + * @return + */ + private String getUsername(String mobileToken, String mobileUserName) throws Exception { + String username = StringKit.EMPTY; + if (isValidToken(mobileToken, mobileUserName)) { + username = UserKit.existUsername(mobileUserName) ? mobileUserName : UserKit.getUserNamesFromEmail(mobileUserName).get(0); + } + return username; + } + + /** + * 验证移动端token + * + * @param token + * @param userAccount + * @return + */ + private boolean authToken(String token, String userAccount) throws Exception { + String msgDecrypt = AESUtil.decrypt(token, AESUtil.sKey); + assert msgDecrypt != null; + String tmpUserName = msgDecrypt.substring(0, msgDecrypt.indexOf("&")); + return ComparatorUtils.equals(tmpUserName, userAccount); + } + + /** + * 验证移动端token + * + * @param token + * @param userAccount + * @return + */ + private boolean isValidToken(String token, String userAccount) throws Exception { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + JSONObject params = new JSONObject(); + params.put("UserAccount", userAccount); + params.put("Token", token); + LogKit.info("cmd-OAuthLogin-isValidToken-params:{}", params.encode()); + StringEntity stringEntity = new StringEntity(params.encode(), "UTF-8"); + String res = HttpKit.executeAndParse(com.fanruan.api.net.http.rs.HttpRequest.custom() + .url(this.config.getUriBase()).post(stringEntity).headers(headers).build()); + LogKit.info("cmd-OAuthLogin-isValidToken-res:{}", res); + if (StringKit.isBlank(res)) { + return false; + } + String resultCode = new JSONObject(res).getString("Code"); + return ComparatorUtils.equals(resultCode, "200"); + } + + /** + * 刷新移动端权限 + * + * @param token + * @param userAccount + * @return + */ + private boolean refreshMobileAuth(String token, String userAccount, String refreshMobileUrl) throws Exception { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + JSONObject params = new JSONObject(); + params.put("username", userAccount); + params.put("token", token); + LogKit.info("cmd-OAuthLogin-refreshMobileAuth-params:{}, refreshMobileUrl:{}", params.encode(), refreshMobileUrl); + StringEntity stringEntity = new StringEntity(params.encode(), "UTF-8"); + String res = HttpKit.executeAndParse(com.fanruan.api.net.http.rs.HttpRequest.custom() + .url(refreshMobileUrl).post(stringEntity).headers(headers).build()); + LogKit.info("cmd-OAuthLogin-refreshMobileAuth-res:{}", res); + if (StringKit.isBlank(res)) { + return false; + } + String resultCode = new JSONObject(res).getString("code"); + return ComparatorUtils.equals(resultCode, "200"); + } + + private String removeToken(HttpServletRequest request) throws URISyntaxException { + URIBuilder uriBuilder = new URIBuilder(WebUtils.getOriginalURL(request).replaceAll(" ", "%2B")); + List params = uriBuilder.getQueryParams(); + params.removeIf(pair -> ComparatorUtils.equals(pair.getName(), "token")); + uriBuilder.clearParameters(); + if (!params.isEmpty()) { + uriBuilder.setParameters(params); + } + return uriBuilder.build().toString(); + } + } \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/cmd/utils/AESUtil.java b/src/main/java/com/fr/plugin/cmd/utils/AESUtil.java new file mode 100644 index 0000000..179c1b7 --- /dev/null +++ b/src/main/java/com/fr/plugin/cmd/utils/AESUtil.java @@ -0,0 +1,93 @@ +package com.fr.plugin.cmd.utils; + +import com.fanruan.api.log.LogKit; +import com.fr.third.org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.Security; + +public class AESUtil { + /** + * 密钥算法 + */ + private static final String KEY_ALGORITHM = "AES"; + + /** + * 加密/解密算法 / 工作模式 / 填充方式 + * Java 6支持PKCS5Padding填充方式 + * Bouncy Castle支持PKCS7Padding填充方式 + */ + private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding"; + + /** + * 偏移量,只有CBC模式才需要 + */ + private final static String ivParameter = "xxx"; + + /** + * AES要求密钥长度为128位或192位或256位,java默认限制AES密钥长度最多128位 + */ + public static String sKey = "xxx"; + + /** + * 编码格式 + */ + public static final String ENCODING = "utf-8"; + + + static { + //如果是PKCS7Padding填充方式,则必须加上下面这行 + Security.addProvider(new BouncyCastleProvider()); + } + + /** + * AES加密 + * + * @param source 源字符串 + * @param key 密钥 + * @return 加密后的密文 + * @throws Exception + */ + public static String encrypt(String source, String key) { + try { + byte[] sourceBytes = source.getBytes(ENCODING); + byte[] keyBytes = key.getBytes(ENCODING); + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC"); + IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes(ENCODING)); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM), iv); + byte[] decrypted = cipher.doFinal(sourceBytes); + return Base64.encodeBase64String(decrypted); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * AES解密 + * + * @param encryptStr 加密后的密文 + * @param key 密钥 + * @return 源字符串 + * @throws Exception + */ + public static String decrypt(String encryptStr, String key) { + try { + byte[] sourceBytes = Base64.decodeBase64(encryptStr); + byte[] keyBytes = key.getBytes(ENCODING); + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, "BC"); + IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes(ENCODING)); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, KEY_ALGORITHM), iv); + byte[] decoded = cipher.doFinal(sourceBytes); + return new String(decoded, ENCODING); + } catch (Exception e) { + LogKit.error(e.getMessage(), e); + } + return null; + } + + +} diff --git a/src/main/resources/com/fr/plugin/cmd/locale/lang.properties b/src/main/resources/com/fr/plugin/cmd/locale/lang.properties new file mode 100644 index 0000000..09a8d34 --- /dev/null +++ b/src/main/resources/com/fr/plugin/cmd/locale/lang.properties @@ -0,0 +1,5 @@ +Plugin-cmd=Sso Plugin +Plugin-cmd_Group=Sso Plugin +Plugin-cmd_Config_UriBase=Uri Base +Plugin-cmd_Config_UriBase_Description=Uri Base +Plugin-cmd_Licence_Expired=Sso Plugin Licence Expired \ No newline at end of file diff --git a/src/main/resources/com/fr/plugin/cmd/locale/lang_zh_CN.properties b/src/main/resources/com/fr/plugin/cmd/locale/lang_zh_CN.properties new file mode 100644 index 0000000..4d67f68 --- /dev/null +++ b/src/main/resources/com/fr/plugin/cmd/locale/lang_zh_CN.properties @@ -0,0 +1,5 @@ +Plugin-cmd=\u5355\u70B9\u767B\u9646\u63D2\u4EF6 +Plugin-cmd_Group=\u5355\u70B9\u767B\u9646\u63D2\u4EF6 +Plugin-cmd_Config_UriBase=Token\u63A5\u53E3\u5730\u5740 +Plugin-cmd_Config_UriBase_Description=Token\u63A5\u53E3\u5730\u5740 +Plugin-cmd_Licence_Expired=\u5355\u70B9\u767B\u9646\u63D2\u4EF6\u8BB8\u53EF\u8FC7\u671F \ No newline at end of file