diff --git a/JSD-7565需求确认书.docx b/JSD-7565需求确认书.docx new file mode 100644 index 0000000..1b2e293 Binary files /dev/null and b/JSD-7565需求确认书.docx differ diff --git a/README.md b/README.md index 344f3a2..df53d49 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # open-JSD-7565 -JSD-7565 第三方token交换fine_auth_token \ No newline at end of file +JSD-7565 第三方token交换fine_auth_token\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 \ No newline at end of file diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..4890a75 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,18 @@ + + + com.fr.plugin.nfsq.sso + + yes + 1.19 + 10.0 + 2018-07-31 + fr.open + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/nfsq/sso/DesECBUtil.java b/src/main/java/com/fr/plugin/nfsq/sso/DesECBUtil.java new file mode 100644 index 0000000..34afd35 --- /dev/null +++ b/src/main/java/com/fr/plugin/nfsq/sso/DesECBUtil.java @@ -0,0 +1,64 @@ +package com.fr.plugin.nfsq.sso; + +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 2019/1/18 + */ +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"); + + } +} + diff --git a/src/main/java/com/fr/plugin/nfsq/sso/HttpUtil.java b/src/main/java/com/fr/plugin/nfsq/sso/HttpUtil.java new file mode 100644 index 0000000..0b3f04f --- /dev/null +++ b/src/main/java/com/fr/plugin/nfsq/sso/HttpUtil.java @@ -0,0 +1,479 @@ +package com.fr.plugin.nfsq.sso; + +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; +import com.fr.third.org.apache.http.HttpResponse; +import com.fr.third.org.apache.http.HttpStatus; +import com.fr.third.org.apache.http.NameValuePair; +import com.fr.third.org.apache.http.client.HttpClient; +import com.fr.third.org.apache.http.client.entity.UrlEncodedFormEntity; +import com.fr.third.org.apache.http.client.methods.HttpPost; +import com.fr.third.org.apache.http.client.methods.HttpPut; +import com.fr.third.org.apache.http.config.Registry; +import com.fr.third.org.apache.http.config.RegistryBuilder; +import com.fr.third.org.apache.http.conn.socket.ConnectionSocketFactory; +import com.fr.third.org.apache.http.conn.socket.LayeredConnectionSocketFactory; +import com.fr.third.org.apache.http.conn.socket.PlainConnectionSocketFactory; +import com.fr.third.org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import com.fr.third.org.apache.http.conn.ssl.SSLContexts; +import com.fr.third.org.apache.http.conn.ssl.TrustStrategy; +import com.fr.third.org.apache.http.entity.StringEntity; +import com.fr.third.org.apache.http.impl.client.CloseableHttpClient; +import com.fr.third.org.apache.http.impl.client.HttpClientBuilder; +import com.fr.third.org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import com.fr.third.org.apache.http.message.BasicNameValuePair; +import com.fr.third.org.apache.http.util.EntityUtils; + +import javax.net.ssl.*; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author fr.open + * @date 2019/4/2 + */ +public class HttpUtil { + + private static HostnameVerifier hv = new HostnameVerifier() { + @Override + public boolean verify(String urlHostName, SSLSession session) { + System.out.println("Warning: URL Host: " + urlHostName + " vs. " + + session.getPeerHost()); + return true; + } + }; + + /** + * 发送get请求 + * + * @param url + * @param param + * @param header + * @return + * @throws IOException + */ + public static String sendGet(String url, Map param, Map header, String charset) { + String result = ""; + BufferedReader in = null; + String urlNameString = url; + try { + if (param != null && !param.isEmpty()) { + urlNameString += "?"; + urlNameString += param.entrySet() + .stream() + .map(entry -> entry.getKey() + "=" + entry.getValue().toString()) + .collect(Collectors.joining("&")); + } + + URL realUrl = new URL(urlNameString); + // 打开和URL之间的连接 + HttpURLConnection connection; + if (url.startsWith("https")) { + trustAllHttpsCertificates(); + HttpsURLConnection.setDefaultHostnameVerifier(hv); + connection = (HttpURLConnection) realUrl.openConnection(); + } else { + connection = (HttpURLConnection) realUrl.openConnection(); + } + //设置超时时间 + connection.setDoInput(true); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(5000); + connection.setReadTimeout(15000); + // 设置通用的请求属性 + if (header != null) { + Iterator> it = header.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + System.out.println(entry.getKey() + ":::" + entry.getValue()); + connection.setRequestProperty(entry.getKey(), entry.getValue().toString()); + } + } + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + // 建立实际的连接 + connection.connect(); + if(connection.getResponseCode() == 200){ + // 定义 BufferedReader输入流来读取URL的响应,设置utf8防止中文乱码 + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), charset == null ? "utf-8" : charset)); + String line; + while ((line = in.readLine()) != null) { + result += line; + } + if (in != null) { + in.close(); + } + }else { + in = new BufferedReader(new InputStreamReader(connection.getErrorStream(), charset == null ? "utf-8" : charset)); + String line; + while ((line = in.readLine()) != null) { + result += line; + } + if (in != null) { + in.close(); + } + FineLoggerFactory.getLogger().error("Http post form code is {},message is {}",connection.getResponseCode(),result); + return StringUtils.EMPTY; + } + + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, "get url error ,url is:{},error is {}", urlNameString, e.getMessage()); + } + return result; + } + + public static String sendPost(String url, Map header, JSONObject body) { + PrintWriter out = null; + BufferedReader in = null; + String result = null; + String res = null; + try { + String urlNameString = url; + + URL realUrl = new URL(urlNameString); + // 打开和URL之间的连接 + HttpURLConnection conn; + if (url.startsWith("https")) { + trustAllHttpsCertificates(); + HttpsURLConnection.setDefaultHostnameVerifier(hv); + conn = (HttpURLConnection) realUrl.openConnection(); + } else { + conn = (HttpURLConnection) realUrl.openConnection(); + } + // 设置通用的请求属性 + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); +// conn.setRequestProperty("user-agent", +// "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + if (header != null) { + header.forEach((k, v) -> { + conn.setRequestProperty(k, String.valueOf(v)); + }); + } + // 发送POST请求必须设置如下两行 + conn.setDoOutput(true); + conn.setDoInput(true); + //获取请求头 + + // 获取URLConnection对象对应的输出流 + out = new PrintWriter(conn.getOutputStream()); + // 发送请求参数 + if (body != null) { + FineLoggerFactory.getLogger().error("content data: {}", body.toString()); + FineLoggerFactory.getLogger().error("content cover data: {}", new String(body.toString().getBytes("UTF-8"), "UTF-8")); + out.print(new String(body.toString().getBytes("UTF-8"), "UTF-8")); + } + // flush输出流的缓冲 + out.flush(); + // 定义BufferedReader输入流来读取URL的响应 + in = new BufferedReader( + new InputStreamReader(conn.getInputStream())); + String line; + while ((line = in.readLine()) != null) { + result += line; + } + res = result; + if (res.startsWith("null")) { + res = res.replace("null", ""); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + //使用finally块来关闭输出流、输入流 + finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + return res; + } + + + public static String doJSONPost(String url, Map header, JSONObject json, Map param, String chartset) { + HttpClient client = getHttpsClient(); + /*if (url.startsWith("https")) { + SSLContext sslcontext = createIgnoreVerifySSL(); + Registry socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.INSTANCE) + .register("https", new SSLConnectionSocketFactory(sslcontext)) + .build(); + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + HttpClients.custom().setConnectionManager(connManager); + client = HttpClients.custom().setConnectionManager(connManager).build(); + }*/ + if (param != null && !param.isEmpty()) { + url += "?"; + url += param.entrySet() + .stream() + .map(entry -> entry.getKey() + "=" + entry.getValue()) + .collect(Collectors.joining("&")); + } + HttpPost post = new HttpPost(url); + post.setHeader("accept", "*/*"); + post.setHeader("connection", "Keep-Alive"); + post.setHeader("Content-Type", "application/json"); + if (header != null) { + header.forEach((k, v) -> { + post.setHeader(k, v.toString()); + }); + } + try { + StringEntity s = new StringEntity(json.toString(), chartset == null ? "UTF-8" : chartset); + s.setContentEncoding("UTF-8"); + s.setContentType("application/json; charset=UTF-8");//发送json数据需要设置contentType + post.setEntity(s); + HttpResponse res = client.execute(post); + if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + String result = EntityUtils.toString(res.getEntity());// 返回json格式: + return result; + } else { + FineLoggerFactory.getLogger().error("Http post form code is {},message is {}", res.getStatusLine().getStatusCode(), EntityUtils.toString(res.getEntity())); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + } + + public static String doJSONPut(String url, Map header, JSONObject json, Map param, String chartset) { + HttpClient client = getHttpsClient(); + /*if (url.startsWith("https")) { + SSLContext sslcontext = createIgnoreVerifySSL(); + Registry socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.INSTANCE) + .register("https", new SSLConnectionSocketFactory(sslcontext)) + .build(); + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + HttpClients.custom().setConnectionManager(connManager); + client = HttpClients.custom().setConnectionManager(connManager).build(); + }*/ + if (param != null && !param.isEmpty()) { + url += "?"; + url += param.entrySet() + .stream() + .map(entry -> entry.getKey() + "=" + entry.getValue()) + .collect(Collectors.joining("&")); + } + HttpPut post = new HttpPut(url); + post.setHeader("accept", "*/*"); + post.setHeader("connection", "Keep-Alive"); + post.setHeader("Content-Type", "application/json"); + if (header != null) { + header.forEach((k, v) -> { + post.setHeader(k, v.toString()); + }); + } + try { + StringEntity s = new StringEntity(json.toString(), chartset == null ? "UTF-8" : chartset); + s.setContentEncoding("UTF-8"); + s.setContentType("application/json; charset=UTF-8");//发送json数据需要设置contentType + post.setEntity(s); + HttpResponse res = client.execute(post); + if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + String result = EntityUtils.toString(res.getEntity());// 返回json格式: + return result; + } else { + FineLoggerFactory.getLogger().error("Http post form code is {},message is {}", res.getStatusLine().getStatusCode(), EntityUtils.toString(res.getEntity())); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + } + + + public static String doFormPost(String url,Map header, Map map, String chartset) { + //声明返回结果 + String result = ""; + UrlEncodedFormEntity entity = null; + HttpResponse httpResponse = null; + HttpClient httpClient = null; + try { + // 创建连接 + httpClient = getHttpsClient(); + ; + /*if (url.startsWith("https")) { + SSLContext sslcontext = createIgnoreVerifySSL(); + Registry socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.INSTANCE) + .register("https", new SSLConnectionSocketFactory(sslcontext)) + .build(); + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + HttpClients.custom().setConnectionManager(connManager); + httpClient = HttpClients.custom().setConnectionManager(connManager).build(); + }*/ + // 设置请求头和报文 + HttpPost httpPost = new HttpPost(url); + if (header != null) { + header.forEach((k, v) -> { + httpPost.setHeader(k, v.toString()); + }); + } + //设置参数 + List list = new ArrayList(); + Iterator iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry elem = (Map.Entry) iterator.next(); + list.add(new BasicNameValuePair(elem.getKey(), elem.getValue())); + } + entity = new UrlEncodedFormEntity(list, chartset == null ? "UTF-8" : chartset); + httpPost.setEntity(entity); + //执行发送,获取相应结果 + httpResponse = httpClient.execute(httpPost); + if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + result = EntityUtils.toString(httpResponse.getEntity()); + } else { + FineLoggerFactory.getLogger().error("Http post form code is {},message is {}", httpResponse.getStatusLine().getStatusCode(), EntityUtils.toString(httpResponse.getEntity())); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return result; + + } + + private static void trustAllHttpsCertificates() throws Exception { + TrustManager[] trustAllCerts = new TrustManager[1]; + TrustManager tm = new miTM(); + trustAllCerts[0] = tm; + SSLContext sc = SSLContext.getInstance("SSL", "SunJSSE"); + sc.init(null, trustAllCerts, null); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } + + + /** + * encode url by UTF-8 + * + * @param url url before encoding + * @return url after encoding + */ + public static String encodeUrl(String url) { + String eurl = url; + try { + eurl = URLEncoder.encode(url, "UTF-8"); + } catch (UnsupportedEncodingException e) { + } + return eurl; + } + + private static class miTM implements TrustManager, + X509TrustManager { + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + + public boolean isServerTrusted( + java.security.cert.X509Certificate[] certs) { + return true; + } + + public boolean isClientTrusted( + java.security.cert.X509Certificate[] certs) { + return true; + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, String authType) + throws CertificateException { + return; + } + + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, String authType) + throws CertificateException { + return; + } + } + + public static SSLContext createIgnoreVerifySSL() { + try { + SSLContext sc = SSLContext.getInstance("TLSv1.2"); + + // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法 + X509TrustManager trustManager = new X509TrustManager() { + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] paramArrayOfX509Certificate, + String paramString) throws CertificateException { + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] paramArrayOfX509Certificate, + String paramString) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + + sc.init(null, new TrustManager[]{trustManager}, null); + return sc; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + } + + private static CloseableHttpClient getHttpsClient() { + RegistryBuilder registryBuilder = RegistryBuilder.create(); + ConnectionSocketFactory plainSF = new PlainConnectionSocketFactory(); + registryBuilder.register("http", plainSF); + // 指定信任密钥存储对象和连接套接字工厂 + try { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + // 信任任何链接 + TrustStrategy anyTrustStrategy = new TrustStrategy() { + + @Override + public boolean isTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException { + // TODO Auto-generated method stub + return true; + } + }; + SSLContext sslContext = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore, anyTrustStrategy).build(); + LayeredConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + registryBuilder.register("https", sslSF); + } catch (KeyStoreException e) { + throw new RuntimeException(e); + } catch (KeyManagementException e) { + throw new RuntimeException(e); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + Registry registry = registryBuilder.build(); + // 设置连接管理器 + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry); + // 构建客户端 + return HttpClientBuilder.create().setConnectionManager(connManager).build(); + + } +} diff --git a/src/main/java/com/fr/plugin/nfsq/sso/Params.java b/src/main/java/com/fr/plugin/nfsq/sso/Params.java new file mode 100644 index 0000000..d38cf13 --- /dev/null +++ b/src/main/java/com/fr/plugin/nfsq/sso/Params.java @@ -0,0 +1,40 @@ +package com.fr.plugin.nfsq.sso; + +/** + * @Author fr.open + * @Date 2021/5/10 + * @Description + **/ +public class Params { + + private String username; + + private String accessToken; + + private String refreshToken; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + +} diff --git a/src/main/java/com/fr/plugin/nfsq/sso/SsoFilter.java b/src/main/java/com/fr/plugin/nfsq/sso/SsoFilter.java new file mode 100644 index 0000000..554d33e --- /dev/null +++ b/src/main/java/com/fr/plugin/nfsq/sso/SsoFilter.java @@ -0,0 +1,537 @@ +package com.fr.plugin.nfsq.sso; + +import com.fr.base.TemplateUtils; +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.bean.authentication.LoginRequestInfoBean; +import com.fr.decision.webservice.exception.user.UserNotExistException; +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.general.http.HttpRequest; +import com.fr.general.http.HttpToolbox; +import com.fr.io.utils.ResourceIOUtils; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.transform.FunctionRecorder; +import com.fr.stable.StringUtils; +import com.fr.stable.web.Device; +import com.fr.web.utils.WebUtils; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.time.Instant; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @Author fr.open + * @Date 2020/9/10 + * @Description + **/ +@FunctionRecorder +public class SsoFilter extends AbstractGlobalRequestFilterProvider { + + private final String CODE_KEY = "90bb6046"; + + @Override + public String filterName() { + return "nongfu"; + } + + @Override + public String[] urlPatterns() { + return new String[]{"/*"}; + } + + private static final String[] NOT_FILTER = { + "/decision/file", + "/decision/resources", + "/system", + "/materials.min.js.map", + "/remote", + "/login", + "/login/config", + "/getFineToken" + }; + + private String apiAuthorize; + + private String apiAuthorizeResponseType; + + private String apiClientId; + + private String apiGetUser; + + private String apiRefreshToken; + + private String state; + + public SsoFilter() { + InputStream in = ResourceIOUtils.read("/resources/xplatform.properties"); + Properties properties = new Properties(); + try { + properties.load(in); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(),e); + } + this.apiAuthorize = properties.getProperty("api.authorize"); + this.apiClientId = properties.getProperty("api.authorize.client_id"); + this.apiAuthorizeResponseType = properties.getProperty("api.authorize.response_type"); + this.apiGetUser = properties.getProperty("api.get-user"); + this.apiRefreshToken = properties.getProperty("api.refresh-token"); + this.state = properties.getProperty("api.authorize.state"); + } + + + @Override + public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + String thisUrl = req.getRequestURL().toString(); + FineLoggerFactory.getLogger().info("This request [{}] login status is [{}]", thisUrl, isLogin(req)); + if (req.getRequestURI().endsWith("auth")) { + executeGetAuth(req, res); + return; + } + String code = req.getParameter("sign"); + if (StringUtils.isNotBlank(code)) { + loginFromCode(code, req, res); + filter(req, res, filterChain); + return; + } + if (Stream.of(NOT_FILTER).anyMatch(thisUrl::contains) || isLogin(req) || isMobileDevice(req)) { + filter(req, res, filterChain); + return; + } + String prefix = null; + try { + prefix = TemplateUtils.render("${fineServletURL}"); + if (req.getRequestURI().endsWith(prefix + "/login")) { + if (handlerWeChat(req, res)) { + return; + } + filter(req, res, filterChain); + return; + } + + }catch (IllegalAccessException e){ + redirectAuth(req, res); + return; + }catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + Params params = null; + try { + params = getUsername(req); + } catch (IllegalAccessException e) { + redirectAuth(req, res); + return; + } + FineLoggerFactory.getLogger().info("Getted username is [{}]", params.getUsername()); + if (StringUtils.isNotBlank(params.getUsername())) { + Cookie at = new Cookie("access_token", params.getAccessToken()); + at.setDomain("xxxx"); + at.setMaxAge(7200); + Cookie rt = new Cookie("refresh_token", params.getRefreshToken()); + rt.setDomain("xxxx"); + rt.setMaxAge(7200); + res.addCookie(at); + res.addCookie(rt); + loginFromToken(req, res, params.getUsername()); + try { + res.sendRedirect(getRedirectUriWithCachedParams(req)); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return; + } + redirectAuth(req, res); + } + + private void redirectAuth(HttpServletRequest req, HttpServletResponse res) { + try { + res.sendRedirect(getAuthorizeUrl(req,res)); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(),e); + } + } + + private boolean handlerWeChat(HttpServletRequest request, HttpServletResponse res) throws IOException, IllegalAccessException { + // 有帆软的登录信息 + if (isLogin(request)) { + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 已登录"); + String token = getAccessToken(request); + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is [{}]", token); + // 没有获取到access_token + if (StringUtils.isBlank(token)) { + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is empty, 不做处理"); + return false; + } + // 获取到了access_token, 比对帆软已登录的用户名和access_token对应的用户名 + String username = getUsername(request, token).getUsername(); + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token 对应的用户名[{}]", username); + String username1 = LoginService.getInstance().getUserNameFromRequestCookie(request); + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 已登录的用户名[{}]", username1); + if (Objects.equals(username1, username)) { + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 两个用户名相同, 不做处理"); + return false; + } + // 不一样的话使用access_token对应的用户名重新登录 + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 两个用户名不相同, 使用[{}]重新登录", username); + loginFromToken(request, res, username); + return false; + } + + // 没有登录信息 + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 未登录"); + String token = getAccessToken(request); + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is [{}]", token); + if (StringUtils.isNotBlank(token)) { + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is not empty, 处理自动登录逻辑"); + Params params = getUsername(request, token); + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 获取到的用户名[{}]", params.getUsername()); + if (StringUtils.isNotBlank(params.getUsername()) && exist(params.getUsername())) { + Cookie at = new Cookie("access_token", params.getAccessToken()); + at.setDomain("yst.com.cn"); + Cookie rt = new Cookie("refresh_token", params.getRefreshToken()); + rt.setDomain("yst.com.cn"); + loginFromToken(request, res, params.getUsername()); + res.sendRedirect(getRedirectUriWithCachedParams(request)); + return true; + } + } + FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is empty, 跳转到sso登录页"); + res.sendRedirect(getAuthorizeUrl(request, res)); + return true; + } + + private String getAuthorizeUrl(HttpServletRequest request, HttpServletResponse res) throws UnsupportedEncodingException { + String urlPattern = "%s?response_type=%s&client_id=%s&redirect_uri=%s&_=%s&state=%s"; + String state = cacheParams(request); + res.addCookie(new Cookie("FINE_REDIRECT_PARAM",state)); + if (StringUtils.isNotBlank(state)) { + urlPattern += "&target=" + state; + } + String url = String.format(urlPattern, + apiAuthorize, + apiAuthorizeResponseType, + apiClientId, + URLEncoder.encode(request.getRequestURL().toString(), "utf-8"), + Instant.now().toEpochMilli(), + this.state + ); + FineLoggerFactory.getLogger().info("授权登录页面[{}]", url); + return url; + } + + private String cacheParams(HttpServletRequest request) { + Enumeration names = request.getParameterNames(); + Map value = new HashMap<>(); + while (names.hasMoreElements()) { + String name = names.nextElement(); + value.put(name, request.getParameter(name)); + } + if (value.isEmpty()) { + return StringUtils.EMPTY; + } + String key = UUID.randomUUID().toString(); + try { + DecisionStatusService.originUrlStatusService().put(key, value); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return key; + } + + /** + * 登录成功后将缓存中存储的url参数拼接入对应地址 + * + * @param request + * @return + */ + + + private String getRedirectUriWithCachedParams(HttpServletRequest request) { + String redirectUri = request.getRequestURL().toString(); + String key = request.getParameter("target"); + if(StringUtils.isBlank(key)){ + key = getCookieValue(request, "FINE_REDIRECT_PARAM"); + } + if(StringUtils.isBlank(key)){ + return redirectUri; + } + Map value = new HashMap<>(); + try { + value = DecisionStatusService.originUrlStatusService().get(key); + if (com.fr.stable.StringUtils.isNotBlank(key) && !value.isEmpty()) { + DecisionStatusService.originUrlStatusService().delete(key); + Map finalValue = value; + redirectUri += "?" + value.keySet().stream().map(k -> String.format("%s=%s", k, finalValue.get(k))).collect(Collectors.joining("&")); + } + } catch (Exception e) { + e.printStackTrace(); + } + return redirectUri; + } + + + private boolean exist(String username) { + User user = null; + try { + user = UserService.getInstance().getUserByUserName(username); + } catch (Exception e) { + e.printStackTrace(); + } + return user != null; + } + + private Params getUsername(HttpServletRequest request) throws IllegalAccessException { + String token = getAccessToken(request); + if(StringUtils.isBlank(token)){ + FineLoggerFactory.getLogger().info("token is null"); + return new Params(); + } + return getUsername(request, token); + } + + private Params getUsername(HttpServletRequest request, String accessToken) throws IllegalAccessException { + String url = apiGetUser + "?access_token=" + accessToken; + FineLoggerFactory.getLogger().info("Get user api address is [{}]", url); + try { + String res = HttpToolbox.executeAndParse(HttpRequest.custom().url(url) + .get() + .build()); + FineLoggerFactory.getLogger().info("获取用户信息接口返回内容 ==> {}", res); + JSONObject body = new JSONObject(res); + if (body.getBoolean("success") && body.has("data")) { + body = body.getJSONObject("data"); + if (body.has("account")) { + Params params = new Params(); + params.setRefreshToken(getRefreshToken(request)); + params.setUsername(body.getString("account")); + params.setAccessToken(accessToken); + return params; + } + } + if (body.has("message") && "token不合法".equals(body.getString("message"))) { + FineLoggerFactory.getLogger().info("Access Token [{}] 不合法, 使用Refresh Token[{}]重新获取", accessToken, getRefreshToken(request)); + accessToken = getAccessTokenWithRefreshToken(request); + return getUsername(request, accessToken); + } + throw new IllegalAccessException(); + }catch (IllegalAccessException e){ + throw new IllegalAccessException(); + }catch (Exception e) { + FineLoggerFactory.getLogger().error("获取用户名失败", e); + throw new RuntimeException(e); + } + } + + private String getAccessTokenWithRefreshToken(HttpServletRequest request) throws Exception { + String url = apiRefreshToken + String.format("?refresh_token=%s&client_id=%s&grant_type=refresh_token", getRefreshToken(request), apiClientId); + FineLoggerFactory.getLogger().info("Refresh Token api address is [{}]", url); + String res = HttpToolbox.executeAndParse(HttpRequest.custom().url(url) + .get() + .build()); + FineLoggerFactory.getLogger().info("刷新token接口返回内容 ==> {}", res); + JSONObject body = new JSONObject(res); + if (body.getBoolean("success") && body.has("data")) { + body = body.getJSONObject("data"); + if (body.has("access_token")) { + return body.getString("access_token"); + } + } + throw new IllegalAccessException(); + } + + private String getRefreshToken(HttpServletRequest request) { + return getValueFromRequest(request, "refresh_token"); + } + + private String getAccessToken(HttpServletRequest request) { + return getValueFromRequest(request, "access_token"); + } + + private String getValueFromRequest(HttpServletRequest request, String key) { + try { + String value = request.getParameter(key); + if (StringUtils.isEmpty(value)) { + value = getCookieValue(request, key); + } + if (StringUtils.isEmpty(value)) { + String params = request.getParameter("app"); + params = params.substring(params.indexOf("#") + 1); + Map values = Pattern.compile("&").splitAsStream(params).map(e -> e.split("=")).collect(Collectors.toMap(e -> e[0], e -> e[1])); + value = values.get(key); + } + return value; + } catch (Exception e) { + FineLoggerFactory.getLogger().error("Failed to get \"{}\" value from this request, cause by: ", key, e.getMessage()); + FineLoggerFactory.getLogger().error("", e); + } + return ""; + } + + private String getCookieValue(HttpServletRequest request, String key) { + Cookie[] cookies = request.getCookies(); + for (Cookie c : cookies) { + if (StringUtils.equals(c.getName(), key)) { + return c.getValue(); + } + } + return StringUtils.EMPTY; + } + + private void loginFromCode(String code, HttpServletRequest req, HttpServletResponse res) { + FineLoggerFactory.getLogger().info("ssoFilter >>> inside login code param is {}", code); + try { + code = DesECBUtil.decryptDES(code, CODE_KEY); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + FineLoggerFactory.getLogger().info("ssoFilter >>> inside login code decode is {}", code); + String[] arr = code.split("_"); + loginFromToken(req, res, arr[0]); + } + + private void executeGetAuth(HttpServletRequest req, HttpServletResponse res) { + String uri = WebUtils.getHTTPRequestParameter(req, "redirect_uri"); + String currentUsername = null; + try { + User user= null; + try { + user = UserService.getInstance().getUserByRequest(req); + }catch (Exception e){ + + } + if(user == null){ + user = UserService.getInstance().getUserByRequestCookie(req); + } + currentUsername = user.getUserName(); + String encryptDES = DesECBUtil.encryptDES(String.format("%s_%d", currentUsername, Instant.now().toEpochMilli()), CODE_KEY); + encryptDES = URLEncoder.encode(encryptDES, "UTF-8"); + uri = uri.indexOf("?") == -1 ? uri + "?sign=" + encryptDES : uri + "&sign=" + encryptDES; + res.sendRedirect(uri); + FineLoggerFactory.getLogger().info("ssoFilter >>> inside redirect url is {}", uri); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + private boolean notExistUser(String username) { + User user = null; + try { + user = UserService.getInstance().getUserByUserName(username); + if (user == null) { + return true; + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return false; + } + + private boolean hasCookie(HttpServletRequest req) { + if (req.getCookies() == null) { + return false; + } + return Stream.of(req.getCookies()).anyMatch(e -> "IAM_SESSION".equalsIgnoreCase(e.getName())); + } + + public LoginRequestInfoBean getLoginInfo(HttpServletRequest req) { + try { + BufferedReader br = req.getReader(); + String str = ""; + String listString = ""; + while ((str = br.readLine()) != null) { + listString += str; + } + JSONObject jsonObject = new JSONObject(listString); + LoginRequestInfoBean info = jsonObject.mapTo(LoginRequestInfoBean.class); + //info.setPassword(TransmissionTool.decrypt(info.isEncrypted(),info.getPassword())); + return info; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + + } + + private boolean loginFromToken(HttpServletRequest req, HttpServletResponse res, String username) { + try { + if (StringUtils.isNotEmpty(username)) { + FineLoggerFactory.getLogger().info("current username:" + username); + User user = UserService.getInstance().getUserByUserName(username); + FineLoggerFactory.getLogger().info("get user:" + user); + if (user == null) { + throw new UserNotExistException(); + } + 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 filter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) { + try { + filterChain.doFilter(req, res); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } catch (ServletException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + 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; + } + + public boolean isMobileDevice(HttpServletRequest request) { + String requestHeader = request.getHeader("user-agent"); + String[] deviceArray = new String[]{"android", "iphone", "ios", "windows phone"}; + if (requestHeader == null) { + return false; + } + requestHeader = requestHeader.toLowerCase(); + for (int i = 0; i < deviceArray.length; i++) { + if (requestHeader.contains(deviceArray[i]) && request.getRequestURI().endsWith("/login")) { + FineLoggerFactory.getLogger().info("current request:{} is mobile request!", request.getRequestURI()); + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/fr/plugin/nfsq/sso/SsoHttpHandler.java b/src/main/java/com/fr/plugin/nfsq/sso/SsoHttpHandler.java new file mode 100644 index 0000000..4ee2691 --- /dev/null +++ b/src/main/java/com/fr/plugin/nfsq/sso/SsoHttpHandler.java @@ -0,0 +1,103 @@ +package com.fr.plugin.nfsq.sso; + +import com.fr.decision.authority.data.User; +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.decision.webservice.v10.login.LoginService; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.general.PropertiesUtils; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.stable.StringUtils; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author fr.open + * @since 2020/08/28 + */ +@EnableMetrics +public class SsoHttpHandler extends BaseHttpHandler { + + private String apiUser = ""; + + public SsoHttpHandler() { + apiUser = PropertiesUtils.getProperties("xplatform").getProperty("api.get-user"); + } + + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return "/getFineToken"; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws Exception { + if (StringUtils.isBlank(apiUser)) { + sendError(response, "apiUser config is null"); + return; + } + String token = request.getParameter("access_token"); + if (StringUtils.isBlank(token)) { + sendError(response, "token is null"); + return; + } + String userName = getUsername(token); + if (StringUtils.isBlank(userName)) { + sendError(response, "get user is null"); + return; + } + User user = UserService.getInstance().getUserByUserName(userName); + FineLoggerFactory.getLogger().info("get user:" + user); + if (user == null) { + sendError(response, "user not exist"); + } + String fineToken = LoginService.getInstance().login(request, response, userName); + JSONObject jsonObject = new JSONObject("{\"codeDesc\":\"success\",\"success\":true,\"codeNum\":0}"); + jsonObject.put("value", JSONObject.create().put("fine_oath_token", fineToken)); + response.setContentType("application/json;charset=UTF-8"); + WebUtils.printAsJSON(response, jsonObject); + } + + private String getUsername(String accessToken) { + String url = apiUser + "?access_token=" + accessToken; + FineLoggerFactory.getLogger().info("Get user api address is [{}]", url); + try { + String res = HttpUtil.sendGet(url, null, null, null); + FineLoggerFactory.getLogger().info("获取用户信息接口返回内容 ==> {}", res); + JSONObject body = new JSONObject(res); + if (body.getBoolean("success") && body.has("data")) { + body = body.getJSONObject("data"); + if (body.has("account")) { + return body.getString("account"); + } + } + throw new IllegalAccessException(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("获取用户名失败", e); + throw new RuntimeException(e); + } + } + + protected void sendError(HttpServletResponse response, String errorCode) { + JSONObject jsonObject = new JSONObject("{\"codeDesc\":\"" + errorCode + "\",\"success\":false,\"codeNum\":70}"); + try { + response.setContentType("application/json;charset=UTF-8"); + WebUtils.printAsJSON(response, jsonObject); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("输出响应错误失败", e); + } + } +} diff --git a/src/main/java/com/fr/plugin/nfsq/sso/SsoRequestHandlerBridge.java b/src/main/java/com/fr/plugin/nfsq/sso/SsoRequestHandlerBridge.java new file mode 100644 index 0000000..2173b60 --- /dev/null +++ b/src/main/java/com/fr/plugin/nfsq/sso/SsoRequestHandlerBridge.java @@ -0,0 +1,19 @@ +package com.fr.plugin.nfsq.sso; + +import com.fr.decision.fun.HttpHandler; +import com.fr.decision.fun.impl.AbstractHttpHandlerProvider; +import com.fr.plugin.transform.FunctionRecorder; + +/** + * @author fr.open + * @since 2020/08/28 + */ +@FunctionRecorder +public class SsoRequestHandlerBridge extends AbstractHttpHandlerProvider { + @Override + public HttpHandler[] registerHandlers() { + return new HttpHandler[]{ + new SsoHttpHandler(), + }; + } +} diff --git a/src/main/java/com/fr/plugin/nfsq/sso/SsoRequestURLAliasBridge.java b/src/main/java/com/fr/plugin/nfsq/sso/SsoRequestURLAliasBridge.java new file mode 100644 index 0000000..78b9b71 --- /dev/null +++ b/src/main/java/com/fr/plugin/nfsq/sso/SsoRequestURLAliasBridge.java @@ -0,0 +1,18 @@ +package com.fr.plugin.nfsq.sso; + +import com.fr.decision.fun.impl.AbstractURLAliasProvider; +import com.fr.decision.webservice.url.alias.URLAlias; +import com.fr.decision.webservice.url.alias.URLAliasFactory; + +/** + * @author fr.open + * @since 2020/08/28 + */ +public class SsoRequestURLAliasBridge extends AbstractURLAliasProvider { + @Override + public URLAlias[] registerAlias() { + return new URLAlias[]{ + URLAliasFactory.createPluginAlias("/getFineToken", "/getFineToken", true), + }; + } +}