commit 81169dd7af8215384e98f8972602a25bfcc792a0 Author: pioneer Date: Thu Jul 14 11:42:56 2022 +0800 open diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d731e7 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# open-JSD-9713 + +JSD-9713 单点登录认证\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系【pioneer】处理。 \ No newline at end of file diff --git a/doc/JSD-9713-需求确认书V1.docx b/doc/JSD-9713-需求确认书V1.docx new file mode 100644 index 0000000..1f719cf Binary files /dev/null and b/doc/JSD-9713-需求确认书V1.docx differ diff --git a/doc/单点使用文档.docx b/doc/单点使用文档.docx new file mode 100644 index 0000000..99d2b9a Binary files /dev/null and b/doc/单点使用文档.docx differ diff --git a/doc/登录验证(外发).docx b/doc/登录验证(外发).docx new file mode 100644 index 0000000..72d55cb Binary files /dev/null and b/doc/登录验证(外发).docx differ diff --git a/lib/finekit-10.0-20200828.jar b/lib/finekit-10.0-20200828.jar new file mode 100644 index 0000000..47de3ae Binary files /dev/null and b/lib/finekit-10.0-20200828.jar differ diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..bc67bbe --- /dev/null +++ b/plugin.xml @@ -0,0 +1,22 @@ + + + com.eco.plugin.xxx.uasslogin + + yes + 1.0.0 + 10.0 + 2021-02-10 + fr.open + com.fr.plugin + + + + 2022-4-1 13:17:34 + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/FunctionRecoder.java b/src/main/java/com/fr/plugin/FunctionRecoder.java new file mode 100644 index 0000000..963ca84 --- /dev/null +++ b/src/main/java/com/fr/plugin/FunctionRecoder.java @@ -0,0 +1,12 @@ +package com.fr.plugin; + +import com.fr.plugin.transform.ExecuteFunctionRecord; +import com.fr.plugin.transform.FunctionRecorder; + +@FunctionRecorder +public class FunctionRecoder { + @ExecuteFunctionRecord + public void exe(){ + System.out.println("插件功能埋点,虽然不会执行,除非上架应用"); + } +} diff --git a/src/main/java/com/fr/plugin/UASSLOGINConfig.java b/src/main/java/com/fr/plugin/UASSLOGINConfig.java new file mode 100644 index 0000000..7bd67cd --- /dev/null +++ b/src/main/java/com/fr/plugin/UASSLOGINConfig.java @@ -0,0 +1,47 @@ +package com.fr.plugin; + +import com.fr.config.*; +import com.fr.config.holder.Conf; +import com.fr.config.holder.factory.Holders; + +@Visualization(category = "登录验证配置") +public class UASSLOGINConfig extends DefaultConfiguration { + + private static volatile UASSLOGINConfig config = null; + + public static UASSLOGINConfig getInstance() { + if (config == null) { + config = ConfigContext.getConfigInstance(UASSLOGINConfig.class); + } + return config; + } + @Identifier(value = "apiUrl", name = "接口地址前缀", description = "描述", status = Status.SHOW) + private Conf apiUrl = Holders.simple("http://*.*.*.*:88"); + @Identifier(value = "secret", name = "认证秘钥", description = "在配置main_config.properties的des3privacykey内容", status = Status.SHOW) + private Conf secret = Holders.simple(""); + + public String getApiUrl() { + return apiUrl.get(); + } + + public void setApiUrl( String apiUrl) { + this.apiUrl .set(apiUrl); + } + + public String getSecret() { + return secret.get(); + } + + public void setSecret( String secret) { + this.secret.set(secret); + } + + @Override + public Object clone() throws CloneNotSupportedException { + UASSLOGINConfig cloned = (UASSLOGINConfig) super.clone(); + cloned.apiUrl = (Conf) this.apiUrl.clone(); + cloned.secret = (Conf) this.secret.clone(); + return cloned; + } + +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/UASSLOGINLifeCycleMonitor.java b/src/main/java/com/fr/plugin/UASSLOGINLifeCycleMonitor.java new file mode 100644 index 0000000..5e83410 --- /dev/null +++ b/src/main/java/com/fr/plugin/UASSLOGINLifeCycleMonitor.java @@ -0,0 +1,18 @@ +package com.fr.plugin; + +import com.fr.plugin.context.PluginContext; +import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor; +import com.fr.stable.fun.Authorize; + +@Authorize +public class UASSLOGINLifeCycleMonitor extends AbstractPluginLifecycleMonitor { + @Override + public void afterRun(PluginContext pluginContext) { + UASSLOGINConfig.getInstance(); + } + + @Override + public void beforeStop(PluginContext pluginContext) { + + } +} diff --git a/src/main/java/com/fr/plugin/USAAPassport.java b/src/main/java/com/fr/plugin/USAAPassport.java new file mode 100644 index 0000000..841659d --- /dev/null +++ b/src/main/java/com/fr/plugin/USAAPassport.java @@ -0,0 +1,65 @@ +package com.fr.plugin; + +import com.fanruan.api.log.LogKit; +import com.fanruan.api.net.http.HttpKit; +import com.fr.decision.authorize.impl.AbstractPassport; +import com.fr.json.JSONObject; +import com.fr.plugin.tools.ToolDES; +import com.fr.stable.StringUtils; + +import java.io.IOException; + +public class USAAPassport extends AbstractPassport { + @Override + public boolean checkTicket(String username, String inputPassword, String savedPassword, String hashPassword) { + boolean checkTicket = false; + String st = null; + try { + st = getSt(username, inputPassword); + if (StringUtils.isBlank(st)) { + return false; + } + String un = getUserInfo(st); + if (StringUtils.equals(un,username)) { + return true; + } + } catch (IOException e) { + LogKit.error("认证失败:",e); + return false; + } + return checkTicket; + } + + @Override + public String markType() { + return "USAA"; + } + + private String getSt(String name, String pwd) throws IOException { + String encrypt = ToolDES.encrypt(pwd); + UASSLOGINConfig uassloginConfig = UASSLOGINConfig.getInstance(); + String url = String.format("%s/ssouas/ssologincheck?loginname=%s&password=%s", uassloginConfig.getApiUrl(), name, encrypt); + String resp = HttpKit.get(url); + LogKit.info("访问:{} 获取st 响应:{}", url, resp); + JSONObject entries = new JSONObject(resp); + String err = entries.getString("err"); + if (StringUtils.equals(err, "1")) { + String st = entries.getString("st"); + return st; + } + return resp; + } + private String getUserInfo(String st) throws IOException { + UASSLOGINConfig uassloginConfig = UASSLOGINConfig.getInstance(); + String userInfoUrl = String.format("%s/ssouas/loginuass?st=%s", uassloginConfig.getApiUrl(), st); + String resp = HttpKit.get(userInfoUrl); + LogKit.info("访问:{} 获取userInfo 响应:{}", userInfoUrl, resp); + JSONObject entries = new JSONObject(resp); + String err = entries.getString("err"); + if (StringUtils.equals(err, "1")) { + String userName = entries.getString("userName"); + return userName; + } + return ""; + } +} diff --git a/src/main/java/com/fr/plugin/filter/UASSLOGIN1Filter.java b/src/main/java/com/fr/plugin/filter/UASSLOGIN1Filter.java new file mode 100644 index 0000000..b9a8359 --- /dev/null +++ b/src/main/java/com/fr/plugin/filter/UASSLOGIN1Filter.java @@ -0,0 +1,104 @@ +package com.fr.plugin.filter; + +import com.fr.base.ServerConfig; +import com.fr.data.NetworkHelper; +import com.fr.decision.authority.base.constant.type.operation.ManualOperationType; +import com.fr.decision.authority.base.constant.type.operation.SyncOperationType; +import com.fr.decision.config.FSConfig; +import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; +import com.fr.decision.mobile.terminal.TerminalHandler; +import com.fr.decision.webservice.v10.login.LoginService; +import com.fr.decision.webservice.v10.login.TokenResource; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.USAAPassport; +import com.fr.stable.StringUtils; +import com.fr.stable.web.Device; +import com.fr.web.utils.WebUtils; +import com.fr.plugin.context.PluginContexts; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class UASSLOGIN1Filter extends AbstractGlobalRequestFilterProvider { + @Override + public String filterName() { + return "UASSLOGIN1Filter"; + } + + @Override + public String[] urlPatterns() { + return new String[]{ + "/decision/login", + }; + } + + @Override + public void init(FilterConfig filterConfig) { + super.init(filterConfig); + } + + @Override + public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + try { + if (PluginContexts.currentContext().isAvailable()) { + doLoginCheck(req, res, filterChain); + } else { + WebUtils.printAsString(res, "插件名:《xxxxx》插件id:com.eco.plugin.xxx.uasslogin 插件授权过期 请联系销售"); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + public void doLoginCheck(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws ServletException, IOException { + if (StringUtils.equals("POST", req.getMethod())) { + USAAPassport usaaPassport = new USAAPassport(); + FSConfig.getInstance().setPassport(ManualOperationType.KEY, usaaPassport); + usaaPassport = new USAAPassport(); + FSConfig.getInstance().setPassport(SyncOperationType.KEY, usaaPassport); + filterChain.doFilter(req, res); + } else { + filterChain.doFilter(req, res); + } + } + + private void login(HttpServletRequest req, HttpServletResponse res, String username) { + String token = null; + try { + token = LoginService.getInstance().login(req, res, username); + req.setAttribute("fine_auth_token", token); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + FineLoggerFactory.getLogger().error("login failed"); + } + FineLoggerFactory.getLogger().error("login success"); + } + + 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; + } + + private static void setCookie(HttpServletResponse response, String name, String value) { + Cookie cookie = new Cookie(name, value); + cookie.setPath("/"); + response.addCookie(cookie); + } + +} diff --git a/src/main/java/com/fr/plugin/tools/EncryptTool.java b/src/main/java/com/fr/plugin/tools/EncryptTool.java new file mode 100644 index 0000000..4ec8b1d --- /dev/null +++ b/src/main/java/com/fr/plugin/tools/EncryptTool.java @@ -0,0 +1,53 @@ +package com.fr.plugin.tools; + + + + +import javax.crypto.*; +import javax.crypto.spec.DESKeySpec; + +import sun.misc.BASE64Encoder; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; + +public class EncryptTool +{ + private byte[] encryptString(byte[] data, byte[] rawKeyData) + throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, IllegalStateException + { + SecureRandom sr = new SecureRandom(); + DESKeySpec dks = new DESKeySpec(rawKeyData); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey key = keyFactory.generateSecret(dks); + Cipher cipher = Cipher.getInstance("DES"); + cipher.init(1, key, sr); + byte[] encryptedData = cipher.doFinal(data); + return encryptedData; + } + + public String encryptString(String rawStr, String rawKey) + throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, IllegalStateException, IOException + { + byte[] rawStrData = rawStr.getBytes(); + byte[] rawKeyData = rawKey.getBytes(); + byte[] encryptData = encryptString(rawStrData, rawKeyData); + String encryptStr = new BASE64Encoder().encode(encryptData); + byte[] c = encryptStr.getBytes(); + StringBuffer strBuf = new StringBuffer(); + for (int i = 0; i < c.length; ++i) + { + int t = c[i]; + String str = Integer.toHexString(t); + if (str.length() == 1) + strBuf.append(0); + strBuf.append(Integer.toHexString(t)); + } + return strBuf.toString(); + } +} + + diff --git a/src/main/java/com/fr/plugin/tools/ToolDES.java b/src/main/java/com/fr/plugin/tools/ToolDES.java new file mode 100644 index 0000000..31f0595 --- /dev/null +++ b/src/main/java/com/fr/plugin/tools/ToolDES.java @@ -0,0 +1,120 @@ +package com.fr.plugin.tools; + + +import com.fanruan.api.log.LogKit; +import com.fr.plugin.UASSLOGINConfig; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; + +/** + * + */ + + +public class ToolDES { + //public final static String PRIVACY_KEY = WebContant.DES_PRIVACYKEY; + public final static String PRIVACY_KEY; + private static final String Algorithm = "DESede"; // 定义 + private static final String encoding = "UTF8"; + // 密钥 + private static byte[] keyBytes = null; + + static { + // String temp = "chinamobileimpgwandother" ; + PRIVACY_KEY = UASSLOGINConfig.getInstance().getSecret(); + keyBytes = UASSLOGINConfig.getInstance().getSecret().getBytes(StandardCharsets.UTF_8); + } + + // keybyte为加密密钥,长度为24字节 + // src为被加密的数据缓冲区(源) + public static byte[] encryptMode(byte[] keybyte, byte[] src) { + try { + // 生成密钥 + SecretKey deskey = new SecretKeySpec(keybyte, Algorithm); + + // 加密 + Cipher c1 = Cipher.getInstance(Algorithm); + c1.init(Cipher.ENCRYPT_MODE, deskey); + return c1.doFinal(src); + } catch (java.lang.Exception e) { + LogKit.error("", e); + } + return null; + } + + // keybyte为加密密钥,长度为24字节 + // src为加密后的缓冲区 + public static byte[] decryptMode(byte[] keybyte, byte[] src) throws Exception { + + // 生成密钥 + SecretKey deskey = new SecretKeySpec(keybyte, Algorithm); + + // 解密 + Cipher c1 = Cipher.getInstance(Algorithm); + c1.init(Cipher.DECRYPT_MODE, deskey); + return c1.doFinal(src); + } + + // 转换成十六进制字符串 + private static String byte2hexString(byte[] bytes) { + StringBuffer buf = new StringBuffer(bytes.length * 2); + for (int i = 0; i < bytes.length; i++) { + if (((int) bytes[i] & 0xff) < 0x10) { + buf.append("0"); + } + buf.append(Long.toString((int) bytes[i] & 0xff, 16)); + } + return buf.toString(); + } + + // 将十六进制字符串byte数组 + private static byte[] hexString2byte(String hexString) { + if (hexString == null || hexString.length() % 2 != 0) { + return null; + } + byte[] hanzi = new byte[hexString.length() / 2]; + for (int i = 0; i < hexString.length(); i += 2) { + hanzi[i / 2] = (byte) (Integer.parseInt(hexString.substring(i, i + 2), 16) & 0xff); + } + return hanzi; + } + + /** + * 加密字符串 + * + * @param src 源字符串 + * @return 加密后的字符串 + */ + public static String encrypt(String src) { + String key = PRIVACY_KEY; + String result = null; + if (src == null) { + return result; + } + try { + EncryptTool ent = new EncryptTool(); + // 加密字符串 + LogKit.debug("加密字符串:" + ent); + result = ent.encryptString(src, key); + + } catch (Exception e) { + LogKit.error("Exception occurred while encrypting [ " + src + " ] ,key[" + key + "],", e); + return null; + } + return result; + } + + public static byte[] getKeyBytes(String key) { + if (key != null && "".equals(key)) { + key = PRIVACY_KEY; + } + if (key != null) { + keyBytes = key.getBytes(); + } + return keyBytes; + } + +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/tools/img.png b/src/main/java/com/fr/plugin/tools/img.png new file mode 100644 index 0000000..4fe2222 Binary files /dev/null and b/src/main/java/com/fr/plugin/tools/img.png differ