From ad33d94cafa561df7b3435decff55e9a418e1ba5 Mon Sep 17 00:00:00 2001 From: "LAPTOP-SB56SG4Q\\86185" Date: Tue, 31 May 2022 15:53:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E5=BC=80=E6=BA=90=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E6=9D=90=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JSD-9530需求确认书.docx | Bin 0 -> 60186 bytes README.md | 5 +- plugin.xml | 18 + .../xxhksso/config/InitializeMonitor.java | 21 ++ .../xxhksso/config/PluginSimpleConfig.java | 69 ++++ .../plugin/xxxx/xxhksso/filter/SSOFilter.java | 79 +++++ .../plugin/xxxx/xxhksso/utils/FRUtils.java | 320 +++++++++++++++++ .../plugin/xxxx/xxhksso/utils/RSAUtils.java | 47 +++ .../xxxx/xxhksso/utils/ResponseUtils.java | 108 ++++++ .../eco/plugin/xxxx/xxhksso/utils/Utils.java | 329 ++++++++++++++++++ 10 files changed, 995 insertions(+), 1 deletion(-) create mode 100644 JSD-9530需求确认书.docx create mode 100644 plugin.xml create mode 100644 src/main/java/com/eco/plugin/xxxx/xxhksso/config/InitializeMonitor.java create mode 100644 src/main/java/com/eco/plugin/xxxx/xxhksso/config/PluginSimpleConfig.java create mode 100644 src/main/java/com/eco/plugin/xxxx/xxhksso/filter/SSOFilter.java create mode 100644 src/main/java/com/eco/plugin/xxxx/xxhksso/utils/FRUtils.java create mode 100644 src/main/java/com/eco/plugin/xxxx/xxhksso/utils/RSAUtils.java create mode 100644 src/main/java/com/eco/plugin/xxxx/xxhksso/utils/ResponseUtils.java create mode 100644 src/main/java/com/eco/plugin/xxxx/xxhksso/utils/Utils.java diff --git a/JSD-9530需求确认书.docx b/JSD-9530需求确认书.docx new file mode 100644 index 0000000000000000000000000000000000000000..a17f1cbb1b9c44c30aedaf4ec5d2f2ad6482899d GIT binary patch literal 60186 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 W4FnJX0zwS@FogyINngJI?tcJ0>cpl1 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 840040f..c593bfe 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # open-JSD-9530 -JSD-9530 致远OA单点 \ No newline at end of file +JSD-9530 致远OA单点\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 \ No newline at end of file diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..bef17c3 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,18 @@ + + com.eco.plugin.xxxx.xxhksso + + yes + 1.0.1 + 10.0 + 2018-07-31 + fr.open + + + com.eco.plugin.xxxx.xxhksso + + + + + + \ No newline at end of file diff --git a/src/main/java/com/eco/plugin/xxxx/xxhksso/config/InitializeMonitor.java b/src/main/java/com/eco/plugin/xxxx/xxhksso/config/InitializeMonitor.java new file mode 100644 index 0000000..05b07ec --- /dev/null +++ b/src/main/java/com/eco/plugin/xxxx/xxhksso/config/InitializeMonitor.java @@ -0,0 +1,21 @@ +package com.eco.plugin.xxxx.xxhksso.config; + +import com.fr.plugin.context.PluginContext; +import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor; + +/** + * @author xxxx + * @version 10.0 + * Created by xxxx on 2021-12-03 + */ +public class InitializeMonitor extends AbstractPluginLifecycleMonitor { + @Override + public void afterRun(PluginContext pluginContext) { + PluginSimpleConfig.getInstance(); + } + + @Override + public void beforeStop(PluginContext pluginContext) { + + } +} diff --git a/src/main/java/com/eco/plugin/xxxx/xxhksso/config/PluginSimpleConfig.java b/src/main/java/com/eco/plugin/xxxx/xxhksso/config/PluginSimpleConfig.java new file mode 100644 index 0000000..82f32ab --- /dev/null +++ b/src/main/java/com/eco/plugin/xxxx/xxhksso/config/PluginSimpleConfig.java @@ -0,0 +1,69 @@ +package com.eco.plugin.xxxx.xxhksso.config; + +import com.fr.config.*; +import com.fr.config.holder.Conf; +import com.fr.config.holder.factory.Holders; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.record.analyzer.EnableMetrics; + +@Visualization(category = "单点登录配置") +@EnableMetrics +public class PluginSimpleConfig extends DefaultConfiguration { + + private static volatile PluginSimpleConfig config = null; + + @Focus(id="com.eco.plugin.xxxx.xxhksso.config", text = "单点登录配置", source = Original.PLUGIN) + public static PluginSimpleConfig getInstance() { + if (config == null) { + config = ConfigContext.getConfigInstance(PluginSimpleConfig.class); + } + return config; + } + + @Identifier(value = "pubkey", name = "公钥", description = "公钥", status = Status.SHOW) + private Conf pubkey = Holders.simple(""); + + @Identifier(value = "privatekey", name = "私钥", description = "私钥", status = Status.SHOW) + private Conf privatekey = Holders.simple(""); + + @Identifier(value = "timeout", name = "超时时间(s)", description = "超时时间(s)", status = Status.SHOW) + private Conf timeout = Holders.simple(""); + + + public String getPubkey() { + return pubkey.get(); + } + + public void setPubkey(String url) { + this.pubkey.set(url); + } + + public String getPrivatekey() { + return privatekey.get(); + } + + public void setPrivatekey(String url) { + this.privatekey.set(url); + } + + public String getTimeout() { + return timeout.get(); + } + + public void setTimeout(String url) { + this.timeout.set(url); + } + + @Override + public Object clone() throws CloneNotSupportedException { + PluginSimpleConfig cloned = (PluginSimpleConfig) super.clone(); +// cloned.text = (Conf) text.clone(); +// cloned.count = (Conf) count.clone(); +// cloned.price = (Conf) price.clone(); +// cloned.time = (Conf) time.clone(); +// cloned.student = (Conf) student.clone(); + return cloned; + } + +} diff --git a/src/main/java/com/eco/plugin/xxxx/xxhksso/filter/SSOFilter.java b/src/main/java/com/eco/plugin/xxxx/xxhksso/filter/SSOFilter.java new file mode 100644 index 0000000..5a12db7 --- /dev/null +++ b/src/main/java/com/eco/plugin/xxxx/xxhksso/filter/SSOFilter.java @@ -0,0 +1,79 @@ +package com.eco.plugin.xxxx.xxhksso.filter; + +import com.eco.plugin.xxxx.xxhksso.config.PluginSimpleConfig; +import com.eco.plugin.xxxx.xxhksso.utils.FRUtils; +import com.eco.plugin.xxxx.xxhksso.utils.RSAUtils; +import com.eco.plugin.xxxx.xxhksso.utils.ResponseUtils; +import com.eco.plugin.xxxx.xxhksso.utils.Utils; +import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; +import com.fr.plugin.context.PluginContexts; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.stable.fun.Authorize; + +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@EnableMetrics +@Authorize(callSignKey = "com.eco.plugin.xxxx.xxhksso") +public class SSOFilter extends AbstractGlobalRequestFilterProvider { + @Override + public String filterName() { + return "xxhkssoFilter"; + } + + @Override + public String[] urlPatterns() { + return new String[]{"/*"}; + } + + @Override + public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain ){ + + if(PluginContexts.currentContext().isAvailable()){ + PluginSimpleConfig psc = PluginSimpleConfig.getInstance(); + + String ticket = req.getParameter("ticket"); + boolean release = Utils.isNullStr(ticket); + + if(release){ + release(req,res,chain); + return; + } + + String token = null; + try { + token = RSAUtils.decrypt(ticket,psc.getPrivatekey()); + } catch (Exception e) { + ResponseUtils.failedResponse(res,"解析ticket失败!"); + return ; + } + + String[] userinfo = token.split("\\|"); + String username = userinfo[0]; + Long timestamp = Long.parseLong(userinfo[1]); + Long now = System.currentTimeMillis(); + if((now - timestamp)/1000 > Long.parseLong(psc.getTimeout())){ + ResponseUtils.failedResponse(res,"ticket超时,请重新生成!"); + return ; + } + + String url = Utils.getRedirectUrl(req,"ticket"); + + //登录 + FRUtils.login(req,res,username,url); + } + + release(req,res,chain); + } + + //放行拦截器 + private void release(HttpServletRequest req, HttpServletResponse res, FilterChain chain) { + try{ + chain.doFilter(req,res); + }catch (Exception e){ + FRUtils.FRLogInfo("拦截失败"); + } + } +} + diff --git a/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/FRUtils.java b/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/FRUtils.java new file mode 100644 index 0000000..52a424b --- /dev/null +++ b/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/FRUtils.java @@ -0,0 +1,320 @@ +package com.eco.plugin.xxxx.xxhksso.utils; + +import com.fr.base.ServerConfig; +import com.fr.base.TableData; +import com.fr.base.TemplateUtils; +import com.fr.decision.authority.AuthorityContext; +import com.fr.decision.authority.base.constant.type.operation.ManualOperationType; +import com.fr.decision.authority.data.User; +import com.fr.decision.base.util.UUIDUtil; +import com.fr.decision.privilege.encrpt.PasswordValidator; +import com.fr.decision.webservice.bean.authentication.OriginUrlResponseBean; +import com.fr.decision.webservice.interceptor.handler.ReportTemplateRequestChecker; +import com.fr.decision.webservice.login.LogInOutResultInfo; +import com.fr.decision.webservice.utils.DecisionServiceConstants; +import com.fr.decision.webservice.utils.DecisionStatusService; +import com.fr.decision.webservice.utils.UserSourceFactory; +import com.fr.decision.webservice.v10.login.LoginService; +import com.fr.decision.webservice.v10.login.event.LogInOutEvent; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.event.EventDispatcher; +import com.fr.file.TableDataConfig; +import com.fr.general.data.DataModel; +import com.fr.log.FineLoggerFactory; +import com.fr.script.Calculator; +import com.fr.stable.StringUtils; +import com.fr.stable.query.QueryFactory; +import com.fr.stable.query.restriction.RestrictionFactory; +import com.fr.third.springframework.web.method.HandlerMethod; +import com.fr.web.controller.ReportRequestService; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.List; + +public class FRUtils { + /** + * 判断用户是否存在 + * @param userName + * @return + */ + public static boolean isUserExist(String userName){ + if (StringUtils.isEmpty(userName)) { + return false; + } else { + try { + List var1 = AuthorityContext.getInstance().getUserController().find(QueryFactory.create().addRestriction(RestrictionFactory.eq("userName", userName))); + return var1 != null && !var1.isEmpty(); + } catch (Exception var2) { + FineLoggerFactory.getLogger().error(var2.getMessage()); + return false; + } + } + } + + /** + * 判断是否登录FR + * @param req + * @return + */ + public static boolean isLogin(HttpServletRequest req){ + return LoginService.getInstance().isLogged(req); + } + + /** + * 帆软登录 + * @param httpServletRequest + * @param httpServletResponse + * @param userName + * @param url + */ + public static void login(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,String userName,String url){ + + FineLoggerFactory.getLogger().info("FRLOG:用户名:"+userName); + FineLoggerFactory.getLogger().info("FRLOG:跳转链接:"+url); + + + //判断用户名是否为空 + if(!Utils.isNullStr(userName)){ + if(isUserExist(userName)){ + String FRToken = ""; + + try { + //HttpSession session = httpServletRequest.getSession(true); + + FRToken = LoginService.getInstance().login(httpServletRequest, httpServletResponse, userName); + + //httpServletRequest.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME,FRToken); + + //session.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, FRToken); + EventDispatcher.fire(LogInOutEvent.LOGIN,new LogInOutResultInfo(httpServletRequest,httpServletResponse,userName,true)); + FineLoggerFactory.getLogger().info("FRLOG:登陆成功!"); + + if(!Utils.isNullStr(url)){ + httpServletResponse.sendRedirect(url); + } + } catch (Exception e) { + ResponseUtils.failedResponse(httpServletResponse,"登录异常,请联系管理员!"); + FineLoggerFactory.getLogger().info("FRLOG:登录异常,请联系管理员!"); + FineLoggerFactory.getLogger().info("FRLOGException:"+e.getMessage()); + } + }else{ + ResponseUtils.failedResponse(httpServletResponse,"用户在报表系统中不存在!"); + FineLoggerFactory.getLogger().info("FRLOG:用户在报表系统中不存在!"); + } + }else{ + ResponseUtils.failedResponse(httpServletResponse,"用户名不能为空!"); + FineLoggerFactory.getLogger().info("FRLOG:用户名不能为空!"); + } + } + + /** + * 帆软登录 + * @param httpServletRequest + * @param httpServletResponse + * @param token + * @param url + */ + public static void loginByToken(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,String token,String url){ + + FineLoggerFactory.getLogger().info("FRLOG:token:"+token); + FineLoggerFactory.getLogger().info("FRLOG:跳转链接:"+url); + + + //判断用户名是否为空 + if(!Utils.isNullStr(token)){ + writeToken2Cookie(httpServletResponse,token,-1); + + HttpSession session = httpServletRequest.getSession(true); + + httpServletRequest.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME,token); + + session.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token); + + if(!Utils.isNullStr(url)){ + try { + httpServletResponse.sendRedirect(url); + } catch (IOException e) { + ResponseUtils.failedResponse(httpServletResponse,"跳转异常!"); + FineLoggerFactory.getLogger().info("FRLOG:跳转异常!"); + } + } + }else{ + ResponseUtils.failedResponse(httpServletResponse,"token不能为空!"); + FineLoggerFactory.getLogger().info("FRLOG:token不能为空!"); + } + } + + /** + * 获取token + * @param httpServletRequest + * @param httpServletResponse + * @param username + * @return + */ + public static String getToken(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,String username){ + String token = ""; + try { + token = LoginService.getInstance().login(httpServletRequest, httpServletResponse, username); + } catch (Exception e) { + FineLoggerFactory.getLogger().info("FRLOG:获取token失败"+e.getMessage()); + } + + return token; + } + + private static void writeToken2Cookie(HttpServletResponse var1, String var2, int var3) { + try { + if (StringUtils.isNotEmpty(var2)) { + Cookie var4 = new Cookie("fine_auth_token", var2); + long var5 = var3 == -2 ? 1209600000L : (long)var3; + var4.setMaxAge((int)var5); + var4.setPath(ServerConfig.getInstance().getCookiePath()); + var1.addCookie(var4); + Cookie var7 = new Cookie("fine_remember_login", String.valueOf(var3 == -2 ? -2 : -1)); + var7.setMaxAge((int)var5); + var7.setPath(ServerConfig.getInstance().getCookiePath()); + var1.addCookie(var7); + } else { + FineLoggerFactory.getLogger().error("empty token cannot save."); + } + } catch (Exception var8) { + FineLoggerFactory.getLogger().error(var8.getMessage(), var8); + } + + } + + /** + * + * @param httpServletRequest + * @param httpServletResponse + */ + public static void logout(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) + { + if(!isLogin(httpServletRequest)){ + return ; + } + + try { + LoginService.getInstance().logout(httpServletRequest,httpServletResponse); + } catch (Exception e) { + ResponseUtils.failedResponse(httpServletResponse,"登出异常,请联系管理员!"); + FineLoggerFactory.getLogger().info("FRLOG:登出异常,请联系管理员!"); + FineLoggerFactory.getLogger().info("FRLOGException:"+e.getMessage()); + } + } + + /** + * 打印FR日志 + * @param message + */ + public static void FRLogInfo(String message){ + FineLoggerFactory.getLogger().info("FRLOG:"+message); + } + + /** + * 打印FR日志-error + * @param message + */ + public static void FRLogError(String message){ + FineLoggerFactory.getLogger().error("FRLOG:"+message); + } + + + /** + * 根据用户名获取用户信息 + * @param userName + * @return + */ + public static User getFRUserByUserName(String userName){ + try { + return UserService.getInstance().getUserByUserName(userName); + } catch (Exception e) { + FRLogInfo("获取用户信息异常:"+e.getMessage()); + } + + return null; + } + + /** + * 解密FR密码 + * @param password + * @return + */ +// public static String decryptFRPsd(String password){ +// FRLogInfo("解密密码:"+password); +// return TransmissionTool.decrypt(password); +// } + + /** + * 根据明文密码生成数据库中的密码,用户密码校验用 + * @return + */ + public static String getDBPsd(String username,String password){ + PasswordValidator pv = UserSourceFactory.getInstance().getUserSource(ManualOperationType.KEY).getPasswordValidator(); + String uuid = UUIDUtil.generate(); + + return pv.encode(username, password, uuid); + } + + /** + * 获取带参数的访问链接 + * @return + */ + public static String getAllUrl(HttpServletRequest httpServletRequest){ + return WebUtils.getOriginalURL(httpServletRequest); + } + + /** + * 根据originKey获取源链接 + * @param originKey + * @return + * @throws Exception + */ + public static String getOriginUrl(String originKey) throws Exception { + if (StringUtils.isNotEmpty(originKey)) { + OriginUrlResponseBean originUrlResponseBean = (OriginUrlResponseBean) DecisionStatusService.originUrlStatusService().get(originKey); + DecisionStatusService.originUrlStatusService().delete(originKey); + if (originUrlResponseBean != null) { + return originUrlResponseBean.getOriginUrl(); + } + } + + return new OriginUrlResponseBean(TemplateUtils.render("${fineServletURL}")).getOriginUrl(); + } + + /** + * 判断是否开启模板认证 + * @param + * @return + * @throws Exception + */ + public static boolean isTempAuth(HttpServletRequest req,HttpServletResponse res) throws Exception { + ReportTemplateRequestChecker checker = new ReportTemplateRequestChecker(); + HandlerMethod hm = new HandlerMethod(new ReportRequestService(),ReportRequestService.class.getMethod("preview", HttpServletRequest.class, HttpServletResponse.class, String.class)); + return checker.checkRequest(req,res,hm); + } + + /** + * 获取数据集数据 + * @param serverDataSetName + * @return + */ + public static DataModel getTableData(String serverDataSetName){ + TableData userInfo = TableDataConfig.getInstance().getTableData(serverDataSetName); + DataModel userInfoDM = userInfo.createDataModel(Calculator.createCalculator()); +// userInfoDM.getRowCount(); +// userInfoDM.getColumnIndex(); +// userInfoDM.getValueAt() + return userInfoDM; + } + + public static String getIndex(HttpServletRequest req){ + String url = req.getScheme()+"://"+req.getServerName()+":"+String.valueOf(req.getServerPort())+req.getRequestURI(); + return url; + } +} diff --git a/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/RSAUtils.java b/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/RSAUtils.java new file mode 100644 index 0000000..38c9a7b --- /dev/null +++ b/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/RSAUtils.java @@ -0,0 +1,47 @@ +package com.eco.plugin.xxxx.xxhksso.utils; + +import javax.crypto.Cipher; +import java.security.KeyFactory; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +public class RSAUtils { + + /** + * RSA公钥加密 + * + * @param str 加密字符串 + * @param publicKey 公钥 + * @return 密文 + * @throws Exception 加密过程中的异常信息 + */ + public static String encrypt(String str, String publicKey) throws Exception { + byte[] decoded = Base64.getDecoder().decode(publicKey); + RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, pubKey); + String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8"))); + return outStr; + } + /** + * RSA私钥解密 + * + * @param str 加密字符串 + * @param privateKey 私钥 + * @return 明文 + * @throws Exception 解密过程中的异常信息 + */ + public static String decrypt(String str, String privateKey) throws Exception { + byte[] inputByte = Base64.getDecoder().decode(str); + byte[] decoded = Base64.getDecoder().decode(privateKey); + RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, priKey); + String outStr = new String(cipher.doFinal(inputByte)); + return outStr; + } + +} diff --git a/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/ResponseUtils.java b/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/ResponseUtils.java new file mode 100644 index 0000000..59fdf21 --- /dev/null +++ b/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/ResponseUtils.java @@ -0,0 +1,108 @@ +package com.eco.plugin.xxxx.xxhksso.utils; + +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.PrintWriter; + +public class ResponseUtils { + private static final int SUCCESS = 200; + private static final int FAILED = -1; + + public static void successResponse(HttpServletResponse res, String body) { + response(res, body, SUCCESS); + } + + public static void failedResponse(HttpServletResponse res, String body) { + response(res, body, FAILED); + } + + private static void response(HttpServletResponse res, String body, int code) { + JSONObject object = new JSONObject(); + PrintWriter pw; + try { + object.put("code", code); + object.put("data", body); + pw = WebUtils.createPrintWriter(res); + } catch (Exception e) { + FineLoggerFactory.getLogger().info(e.getMessage()); + return; + } + res.setContentType("application/json;charset=utf-8"); + String result = object.toString(); + pw.println(result); + pw.flush(); + pw.close(); + } + + public static void response(HttpServletResponse res,JSONObject json){ + PrintWriter pw; + try { + pw = WebUtils.createPrintWriter(res); + } catch (Exception e) { + FineLoggerFactory.getLogger().info(e.getMessage()); + return; + } + res.setContentType("application/json;charset=utf-8"); + String result = json.toString(); + pw.println(result); + pw.flush(); + pw.close(); + } + + public static void responseText(HttpServletResponse res,String text){ + PrintWriter pw; + try { + pw = WebUtils.createPrintWriter(res); + } catch (Exception e) { + FineLoggerFactory.getLogger().info(e.getMessage()); + return; + } + res.setContentType("text/html;charset=utf-8"); + pw.println(text); + pw.flush(); + pw.close(); + } + + public static void responseXml(HttpServletResponse res,String xml){ + PrintWriter pw; + try { + pw = WebUtils.createPrintWriter(res); + } catch (Exception e) { + FineLoggerFactory.getLogger().info(e.getMessage()); + return; + } + res.setContentType("text/xml;charset=utf-8"); + pw.println(xml); + pw.flush(); + pw.close(); + } + + public static void setCSRFHeader(HttpServletResponse httpServletResponse){ + httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); + httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,HEAD,PUT,PATCH"); + httpServletResponse.setHeader("Access-Control-Max-Age", "36000"); + httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization,authorization"); + } + + public static void responseJsonp(HttpServletRequest req, HttpServletResponse res, JSONObject json){ + PrintWriter pw; + try { + pw = WebUtils.createPrintWriter(res); + } catch (Exception e) { + FineLoggerFactory.getLogger().info(e.getMessage()); + return; + } + res.setContentType("text/javascript;charset=utf-8;charset=utf-8"); + String result = json.toString(); + + String jsonp=req.getParameter("callback"); + + pw.println(jsonp+"("+result+")"); + pw.flush(); + pw.close(); + } +} diff --git a/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/Utils.java b/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/Utils.java new file mode 100644 index 0000000..463f13d --- /dev/null +++ b/src/main/java/com/eco/plugin/xxxx/xxhksso/utils/Utils.java @@ -0,0 +1,329 @@ +package com.eco.plugin.xxxx.xxhksso.utils; + +import com.fr.base.TemplateUtils; +import com.fr.data.NetworkHelper; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.io.utils.ResourceIOUtils; +import com.fr.json.JSONObject; +import com.fr.stable.CodeUtils; +import com.fr.stable.StringUtils; +import com.fr.third.org.apache.commons.codec.digest.DigestUtils; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Utils { + + /** + * 判断字符串是否为空 + * @param str + * @return true 空字符串 false 非空字符串 + */ + public static boolean isNullStr(String str){ + return !(str != null && !str.isEmpty() && !"null".equals(str)); + } + + /** + * 判断字符串是否非空 + * @param str + * @return + */ + public static boolean isNotNullStr(String str){ + return !isNullStr(str); + } + + /** + * MD5加密 + * @param str + * @return + */ + public static String getMd5Str(String str) + { + return DigestUtils.md5Hex(str); + } + + /** + * 帆软shaEncode加密 + */ + + public static String shaEncode(String str){ + return CodeUtils.sha256Encode(str); + } + + /** + * 获取uuid + */ + public static String uuid(){ + return UUID.randomUUID().toString(); + } + + /** + * 替换空字符串 + * @param str + * @param replace + * @return + */ + public static String replaceNullStr(String str,String replace){ + if(isNullStr(str)){ + return replace; + } + + return str; + } + + /** + * 获取请求体 + * @param req + * @return + */ + public static JSONObject getRequestBody(HttpServletRequest req){ + StringBuffer sb = new StringBuffer(); + String line = null; + try { + BufferedReader reader = req.getReader(); + while ((line = reader.readLine()) != null) + sb.append(line); + } catch (Exception e) { + FRUtils.FRLogInfo("getRequestBody:exception:"+e.getMessage()); + } + //将空格和换行符替换掉避免使用反序列化工具解析对象时失败 + String jsonString = sb.toString().replaceAll("\\s","").replaceAll("\n",""); + + JSONObject json = new JSONObject(jsonString); + + return json; + } + + /** + * 获取ip + * @return + */ + public static String getIp(HttpServletRequest req){ + String realIp = req.getHeader("X-Real-IP"); + String fw = req.getHeader("X-Forwarded-For"); + if (StringUtils.isNotEmpty(fw) && !"unKnown".equalsIgnoreCase(fw)) { + int var3 = fw.indexOf(","); + return var3 != -1 ? fw.substring(0, var3) : fw; + } else { + fw = realIp; + if (StringUtils.isNotEmpty(realIp) && !"unKnown".equalsIgnoreCase(realIp)) { + return realIp; + } else { + if (StringUtils.isBlank(realIp) || "unknown".equalsIgnoreCase(realIp)) { + fw = req.getHeader("Proxy-Client-IP"); + } + + if (StringUtils.isBlank(fw) || "unknown".equalsIgnoreCase(fw)) { + fw = req.getHeader("WL-Proxy-Client-IP"); + } + + if (StringUtils.isBlank(fw) || "unknown".equalsIgnoreCase(fw)) { + fw = req.getHeader("HTTP_CLIENT_IP"); + } + + if (StringUtils.isBlank(fw) || "unknown".equalsIgnoreCase(fw)) { + fw = req.getHeader("HTTP_X_FORWARDED_FOR"); + } + + if (StringUtils.isBlank(fw) || "unknown".equalsIgnoreCase(fw)) { + fw = req.getRemoteAddr(); + } + + return fw; + } + } + } + + /** + * 根据key获取cookie + * @param req + * @return + */ + public static String getCookieByKey(HttpServletRequest req,String key){ + Cookie[] cookies = req.getCookies(); + String cookie = ""; + + if(cookies == null || cookies.length <=0){ + return ""; + } + + for(int i = 0; i < cookies.length; i++) { + Cookie item = cookies[i]; + if (item.getName().equalsIgnoreCase(key)) { + cookie = item.getValue(); + } + } + + FRUtils.FRLogInfo("cookie:"+cookie); + + return cookie; + } + + /** + * 判断是否是手机端的链接 + * @param req + * @return + */ + public static boolean isMobile(HttpServletRequest req) { + String[] mobileArray = {"iPhone", "iPad", "android", "windows phone", "xiaomi"}; + String userAgent = req.getHeader("user-agent"); + if (userAgent != null && userAgent.toUpperCase().contains("MOBILE")) { + for(String mobile : mobileArray) { + if(userAgent.toUpperCase().contains(mobile.toUpperCase())) { + return true; + } + } + } + return NetworkHelper.getDevice(req).isMobile(); + } + + /** + * 只编码中文 + * @param url + * @return + */ + public static String encodeCH(String url ){ + Matcher matcher = Pattern.compile("[\\u4e00-\\u9fa5]").matcher(url); + + while(matcher.find()){ + String chn = matcher.group(); + url = url.replaceAll(chn, URLEncoder.encode(chn)); + } + + return url; + } + + /** + * 获取web-inf文件夹下的文件 + * filename /resources/ip4enc.properties + */ + public static InputStream getResourcesFile(String filename){ + return ResourceIOUtils.read(filename); + } + + /** + * + * @param res + * @param path /com/fr/plugin/loginAuth/html/getMac.html + * @param parameterMap + */ + public static void toErrorPage(HttpServletResponse res,String path,Map parameterMap){ + if(parameterMap == null){ + parameterMap = new HashMap(); + } + + try { + String macPage = TemplateUtils.renderTemplate(path, parameterMap); + WebUtils.printAsString(res, macPage); + }catch (Exception e){ + FRUtils.FRLogError("跳转页面异常"); + } + + } + + /** + * 判断是否是管理员 + * @param username + * @return + */ + public static boolean isAdmin(String username) throws Exception{ + return UserService.getInstance().isAdmin(UserService.getInstance().getUserByUserName(username).getId()); + } + + /** + * 去掉浏览器中的参数 + * @param url + * @param param + * @return + */ + public static String removeParam(String url,String param){ + if(!url.contains("?"+param) && !url.contains("&"+param)){ + return url; + } + + return url.substring(0,url.indexOf(url.contains("?"+param) ? "?"+param : "&"+param)); + } + + /** + * 获取跳转链接 + * @param req + * @param param + * @return + */ + public static String getRedirectUrl(HttpServletRequest req,String param){ + String url = FRUtils.getAllUrl(req); + + if(isNotNullStr(param)){ + url = removeParam(url,param); + } + + url = encodeCH(url); + + return url; + } + + /** + * 去除空格换行 + * @param str + * @return + */ + public static String trim(String str){ + return str.trim().replaceAll("\n","").replaceAll("\r",""); + } + + /** + * list 转化为指定字符分割的字符串 + * @param list + * @param list + * @return + */ + public static String listToStr(List list, String split){ + String result = ""; + + if(list == null || list.size() <= 0){ + return result; + } + + for(String str : list){ + result+=","+str; + } + + result = result.substring(1); + + return result; + } + + /** + * array 转化为指定字符分割的字符串 + * @param list + * @param list + * @return + */ + public static String arrayToStr(String[] list, String split){ + String result = ""; + + if(list == null ||list.length <= 0){ + return result; + } + + for(int i=0;i