手机网站开发书籍新网站不被收录
手机网站开发书籍,新网站不被收录,工程建设网站怎么提交,品牌设计网站建设关于Galleria示例的先前文章#xff08; 第1 部分 | 第2部分 | 第3部分 | 第4部分 #xff09;指导您完成基础知识以及对GlassFish和WebLogic的初始部署。 从今天开始#xff0c;我尝试在其中添加一些企业级功能#xff0c;因为我发现他们在自己的项目中提出了很多要求。 我… 关于Galleria示例的先前文章 第1 部分 | 第2部分 | 第3部分 | 第4部分 指导您完成基础知识以及对GlassFish和WebLogic的初始部署。 从今天开始我尝试在其中添加一些企业级功能因为我发现他们在自己的项目中提出了很多要求。 我知道Vineet还将随着时间的推移添加更多功能 我希望这不会对读者造成混淆。 但是让我们看看这是如何实现的以及我的哪些功能被Vineet所采用而哪些不是:)。 让我知道您是否想添加任何特别的东西 会话固定 企业Java应用程序最热门的话题是安全性。 由于它具有许多不同的方面因此我决定从一个非常简单但经常需要的功能入手防止会话固定。 这不是Java或JSF特有的而是基于Web的应用程序的普遍问题。 当会话ID易于发现或猜测时就会出现会话固定。 攻击的主要方法是URL或响应的任何其他部分中存在会话ID。 攻击者可以捕获一个会话然后将链接嵌入到其页面中诱使用户访问该会话并成为其会话的一部分。 然后当用户认证时会话即被认证。 在这里使用Cookies只能提供一定的安全性因为大多数情况下还通过暗示保密性丢失的方法进行设置。 大多数应用服务器会根据第一个请求生成一个新的会话ID。 认证通过后可以再次使用。 防止这种情况的唯一方法是在成功的身份验证请求之后发出新的随机会话。 一般来说这很容易做到。 转到galleria-jsf项目并找到info.galleria.view.user.Authenticator bean。 将以下行添加到authenticate方法的开头 String result null;
ExternalContext externalContext FacesContext.getCurrentInstance().getExternalContext();// Session Fixation Prevention
HttpSession session (HttpSession) externalContext.getSession(false);if (logger.isDebugEnabled()) {logger.debug(Session before authentication request: session.getId());}session.invalidate();
session (HttpSession) externalContext.getSession(true);if (logger.isDebugEnabled()) {logger.debug(Session after authentication request: session.getId());} 就是这样 第一次接触代码库就很容易进行更改。 切换到软件包信息info.galleria的调试级别FINE应该在日志文件中揭示魔术 [#|2012-03-27T17:17:25.2980200|FINE|glassfish3.1.2|info.galleria.view.user.Authenticator|_ThreadID27;
_ThreadNameThread-4;ClassNameinfo.galleria.view.user.Authenticator;MethodName
authenticate;|Session before authentication request: 33b1205d7ad740631978ed211bce|#][#|2012-03-27T17:17:25.3010200|FINE|glassfish3.1.2|info.galleria.view.user.Authenticator
|_ThreadID27
;_ThreadNameThread-4;ClassNameinfo.galleria.view.user.Authenticator;MethodName
authenticate;|Session after authentication request: 33b1f344ad1730c69bccc35e752e|#] 如预期的那样我们在身份验证请求期间更改了http会话。 您也可以使用您选择的浏览器插件在本例中为“编辑此Cookie”进行检查 通过执行此操作Galleria应用程序变得更加安全。 如果您想了解有关会话固定的更多信息请阅读OWASP页面 。 防止多次登录 下一个要求要复杂一些。 我已经看过几次了即使对用户来说不方便出于安全原因也可能是必需的。 正如您可能已经猜到的没有一个单独的开关。 您必须持有会话图并检查用户是否已经登录。 在登录过程中应进行检查并显示有意义的错误消息。 其中有一些棘手的部分。 第一个是您需要一种方法来存储应用程序的所有用户和HttpSession信息。 第二个是您需要一个人来照顾它。 让我们从最新开始。 您在这里需要著名的辛格尔顿。 一个地方来存储相关的HttpSession信息。 首先想到的是使用.getExternalContext。getApplicationMap。 这可能有效。 我们在此处设置的登录限制有一些副作用。 想象一下一个用户没有登录就登录并崩溃了他/她的浏览器。 他/她最终将无法重新登录直到进行一些清理或重新启动应用程序为止。 因此在HttpSessionListener中访问它也至关重要。 鉴于事实即JSF ExternalContext是ServletContext我们在这里很安全。 在继续进行有关聚类的更多讨论之前。 我们将在这里构建一个非集群构造。 根据Servlet规范上下文属性对于创建它们的JVM是本地的。 因此如果您在集群环境中运行此命令将会失去保护因为您可以在集群的每个节点上进行会话。 使该群集安全将意味着使用数据库ejb组件或分布式缓存。 转到info.galleria.view.util并创建一个名为SessionConcierge的新最终类。 它需要添加和删除会话的方法。 我们显然需要一些东西来处理应用程序映射。 从addSession方法开始稍后将从info.galleria.view.user.Authenticator托管Bean中调用该方法 public static boolean addSession(HttpSession session) {String account FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();String sessionId session.getId();if (account ! null !getApplicationMap(session).containsKey(account)) {getApplicationMap(session).put(account, sessionId);if (logger.isDebugEnabled()) {logger.debug(Added Session with ID {} for user {}, sessionId, account);}return true;} else {logger.error(Cannot add sessionId, because current logged in account is NULL or session already assigned!);return false;}} 基本上这将检查我们是否在这里有登录用户以及该用户是否已经分配了会话。 如果有一个用户并且他没有正在使用的会话我们将把当前会话添加到该帐户下的应用程序映射中作为键。 接下来一点删除逻辑 public static void removeSession(HttpSession session) {String sessionId session.getId();String account getKeyByValue(getApplicationMap(session), sessionId);if (account ! null) {getApplicationMap(session).remove(account);if (logger.isDebugEnabled()) {logger.debug(Removed Session with ID {} for user {}, sessionId, account);}}} 这有点棘手。 您注意到我使用该帐户作为在地图中绑定会话的密钥。 因此我必须花一点点技巧来反转地图并通过值找到键。 这个小魔术在这里发生 private static T, E T getKeyByValue(MapT, E map, E value) {for (EntryT, E entry : map.entrySet()) {if (value.equals(entry.getValue())) {return entry.getKey();}}return null;} 做完了 一件事失踪。 getApplicationMapHttpSession session方法。 这不是很神奇。 它只是试图弄清楚我们是否需要通过FacesContext或ServletContext获取它。 如果您感到好奇请查看SessionConcierge源。 最后要做的是将SessionConcierge添加到Authenticator中 。 将此代码添加到try {request.login}中我为您的定位添加了前两行 request.login(userId, new String(password));result /private/HomePage.xhtml?faces-redirecttrue;// save sessionId to disable multiple sessions per userif (!SessionConcierge.addSession(session)) {request.logout();logger.error(User {} allready logged in with another session, userId);FacesMessage facesMessage new FacesMessage(FacesMessage.SEVERITY_ERROR, Messages.getString(Login.AllreadyLoggedIn, locale), null);FacesContext.getCurrentInstance().addMessage(null, facesMessage);} 如果通过SessionConcierge添加HttpSession失败则立即注销用户并添加FacesMessage。 请记住将其添加到galleria-jsf \ src \ main \ resources \ resources messages.properties及其翻译中。 并且不要忘记添加 SessionConcierge.removeSession(session); 到公共String logout。 精细。 就是这样不是吗 至少它现在正在工作。 但是我们仍然必须解决那些崩溃的浏览器问题。 如果某人未通过该应用程序注销会话超时或浏览器崩溃则在重新启动该应用程序之前您将无法再次登录。 那是不可思议的。 需要某种清理机制。 HttpSessionListener呢 听起来不错 将其添加到info.galleria.listeners中并将其命名为SessionExpirationListener 。 Overridepublic void sessionDestroyed(HttpSessionEvent se) {HttpSession session se.getSession();SessionConcierge.removeSession(session);if (logger.isDebugEnabled()) {logger.debug(Session with ID {} destroyed, session.getId());}} 精细。 现在应该可以了。 继续尝试一下。 打开两个不同的浏览器然后尝试同时登录。 只有一个可以让您访问该应用程序。 第二个应该以您放入messages.properties中的错误消息作为响应。 请注意这不是多窗口预防措施。 您仍然可以根据需要自由地为每个HttpSession打开尽可能多的窗口。 一个小的补充如果您严重依赖HttpSessionListener清理则应确保它具有正确的生存期。 通过产品特定的Web应用程序部署描述符例如weblogic.xml或glassfish-web.xml进行配置。 我建议将其设置为合理的较低值例如30分钟或更短以免用户等待太长时间。 这是Glassfishglassfish-web.xml的外观 session-configsession-propertiesproperty nametimeoutSeconds value1800 //session-properties/session-config 和用于WebLogicweblogic.xml session-descriptortimeout-secs180/timeout-secs/session-descriptor Galleria Java EE 6示例应用程序正在增长。 今天我将写关于如何优雅地处理错误的文章。 关于用户输入验证已经做了很多工作但是仍然有很多失败情况没有得到解决应该解决。 如果您对过去发生的事情感到好奇请查看本系列的第一部分 基础知识 在GlassFish上 运行在WebLogic 上 运行 测试和增强安全性 。 通用异常机制 应用程序使用检查的异常在层之间传递错误。 ApplicationException是所有可能的业务异常的根源。 这些业务异常在域和表示层之间传达验证冲突和所有已知错误。 galleria-jsf视图项目中的domain Manager例如AlbumManger类将其捕获并使用ExceptionPrecessor将错误消息填充到视图中。 在这两层之间可能发生的另一种异常是RuntimeExceptions。 那些被容器包装到EJBException中并且还被domain Manager类捕获。 这些会生成更一般的错误消息并显示给用户。 在这里我不会涉及检查与未检查的异常如果您好奇的话Google会介绍一下 。 当应用程序有机会从错误中恢复时我倾向于使用检查异常。 当某些事情无法恢复时将引发未经检查的检查。 这就是原因我对目前内置的异常处理机制不满意。 我稍后再讨论。 有什么不见了 ViewExpired等。 似乎现在一切都已处理。 但只有第一印象。 打开登录屏幕稍等片刻让您的http会话超时。 现在您会看到一个不太漂亮的ViewExpired异常屏幕。 如果您以登录用户的身份尝试登录则只需将其重定向到登录页面。 无论如何对于表示层中的一些其他意外情况可能会出现相同的错误页面。 因此让我们修复此问题。 最明显的事情是简单地引入专用的错误页面。 error-pageexception-typejavax.faces.application.ViewExpiredException/exception-typelocation/viewExpired.xhtml/location/error-page 现在您将用户重定向到专用页面该页面可以告诉他/她有关工作场所安全性的一些知识并且不会使应用长时间处于无人看管状态。 这适用于大多数应用程序。 如果您愿意在页面上获得一些其他信息或者只是想捕获多个异常并单独处理它们而不必静态配置它们则需要一种称为ExceptionHandler的东西。 这是JSF 2中的新功能您所需要做的就是实现ExceptionHandler并且它是工厂。 工厂本身在facex-config.xml中配置因为没有任何注释。 打开faces-config.xml并在底部添加以下几行 factoryexception-handler-factoryinfo.galleria.handlers.GalleriaExceptionHandlerFactory/exception-handler-factory/factory 现在我们将在专用包中实现GalleriaExceptionHandlerFactory 。 有趣的方法是 Overridepublic ExceptionHandler getExceptionHandler() {ExceptionHandler result parent.getExceptionHandler();result new GalleriaExceptionHandler(result);return result;} 每个请求调用一次每次调用必须返回一个新的ExceptionHandler实例。 在这里真正的ExceptionHandlerFactory被调用并被要求创建实例然后将该实例包装在自定义的GalleriaExceptionHandler类中。 这是真正有趣的事情发生的地方。 Overridepublic void handle() throws FacesException {for (IteratorExceptionQueuedEvent i getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {ExceptionQueuedEvent event i.next();ExceptionQueuedEventContext context (ExceptionQueuedEventContext) event.getSource();Throwable t context.getException();if (t instanceof ViewExpiredException) {ViewExpiredException vee (ViewExpiredException) t;FacesContext fc FacesContext.getCurrentInstance();MapString, Object requestMap fc.getExternalContext().getRequestMap();NavigationHandler nav fc.getApplication().getNavigationHandler();try {// Push some stuff to the request scope for later use in the pagerequestMap.put(currentViewId, vee.getViewId());nav.handleNavigation(fc, null, viewExpired);fc.renderResponse();} finally {i.remove();}}}// Let the parent handle all the remaining queued exception events.getWrapped().handle();} 使用从getUnhandledExceptionQueuedEvents。iterator返回的迭代器迭代非处理程序异常。 ExeceptionQueuedEvent是一个SystemEvent您可以从中获取实际的ViewExpiredException。 最后您从异常中提取了一些其他信息并将其放在请求范围内以便稍后通过页面中的EL进行访问。 ViewExpiredException要做的最后一件事是使用JSF隐式导航系统“ viewExpired”解析为“ viewExpired.xhtml”并通过NavigationHandler导航至“ viewExpired”页面。 不要忘记在finally块中删除已处理的异常。 您不希望父异常处理程序再次处理此问题。 现在我们必须创建viewExpired.xhtml页面。 在galleria-jsf \ src \ main \ webapp文件夹中执行此操作。 ?xml version1.0 encodingUTF-8 ?
!DOCTYPE composition PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
ui:composition xmlns:uihttp://java.sun.com/jsf/faceletstemplate./templates/defaultLayout.xhtmlxmlns:fhttp://java.sun.com/jsf/corexmlns:hhttp://java.sun.com/jsf/htmlui:define nametitleh:outputText value#{msg[Exception.page.title]} //ui:defineui:define namecontenth:formh:outputText value#{msg[Exception.page.message]} /pYou were on page #{currentViewId}. Maybe thats useful./ppPlease re-login via the h:outputLink styleClasshomepagelink value#{request.contextPath}/Index.xhtml h:outputText valueHomepage //h:outputLink./p/h:form/ui:define
/ui:composition 请注意我在此处添加了新的消息属性因此您需要确保将它们放在galleria-jsf \ src \ main \ resources \ resources \ messages.properties和翻译中。 到目前为止这显然只处理一种特殊的异常实例。 您可以将其扩展为也可以处理其他内容。 现在我们已经有了基本的机制您可以自由地执行此操作。 重构RuntimeException处理 如我所说我对应用程序处理RuntimeExceptions的方式不满意。 现在我们已经有了一个很好的中央异常处理我们可以将这些内容稍微移动一下并重构* Manager类。 从所有它们中删除所有这些catchEJBException ejbEx{块。 我们将在一分钟内在GalleriaExceptionHandler中进行处理。 只需将另一个检查添加到GalleriaExceptionHandler即可如果引发了ViewExpiredException以外的任何其他异常则将用户重定向到另一个页面。 // check for known Exceptionsif (t instanceof ViewExpiredException) {ViewExpiredException vee (ViewExpiredException) t;// Push some stuff to the request scope for later use in the pagerequestMap.put(currentViewId, vee.getViewId());} else {forwardView generalError;Locale locale fc.getViewRoot().getLocale();String key Excepetion.GeneralError;logger.error(Messages.getLoggerString(key), t);String message Messages.getString(key, locale);FacesMessage facesMessage new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null);fc.addMessage(null, facesMessage);} 这种方法具有一些优点。 它减少了* Manager类中所需的代码并且我们终于有了一个中心位置来处理那些不可恢复的异常。 这还是不是很像企业。 想象一下您的第一级支持团队需要照顾客户他们开始抱怨他们收到的唯一消息是“ GeneralError”。 那不是很有帮助。 您的支持团队将需要升级它第二或第三级需要检查日志和and and ..所有这些都是由于我们已知的错误。 首先要做的是找出导致错误的原因。 解析堆栈跟踪并不是很大的乐趣。 特别是不是包含在EJBExceptions中以及在FacesExceptions中的RuntimeExceptions中。 感谢上帝提供Apache Commons ExceptionUtils 。 打开您的galleria-jsf pom.xml并将其添加为依赖项 dependencygroupIdcommons-lang/groupIdartifactIdcommons-lang/artifactIdversion2.6/version/dependency 现在您可以开始检查根本原因 } else {forwardView generalError;// no known instance try to specifyThrowable causingEx ExceptionUtils.getRootCause(t);if (causingEx null) {causingEx t;}//...logger.error(Messages.getLoggerString(key), t);requestMap.put(errorCode, errorCode); 别忘了在这里也记录完整的堆栈跟踪t不仅是causeEx。 通常让用户知道异常是一件坏事。 没有人真正希望看到错误发生因为我们讨厌犯错误并且在所有异常之后堆栈跟踪都可以泄露您不希望在屏幕上某个地方看到的敏感信息。 因此您需要找到一种方法来显示对用户有意义的内容而又不会过多披露。 那就是著名的错误代码起作用的地方。 使用根本原因异常作为消息键或自行决定要为此付出的努力。 它可能是一个错误类别的系统数据库接口系统等它们为第一级支持提供了有关导致错误的原因的良好提示。 从一开始我就坚持一个简单的解决方案。 只需为每个捕获的异常生成一个UUID并将其跟踪到日志和UI。 以下是一个非常简单的示例。 String errorCode String.valueOf(Math.abs(new Date().hashCode())); 这也应该添加到消息属性中并且不要忘记您还需要另一个用于generalError模板。 如果slf4j将使用与jdk日志记录相同的消息格式那么您只需要一个属性.. Exception.generalError.logGeneral Error logged: {}.Exception.generalError.messageA general error with id {0} occured. Please call our hotline. 将此添加到generalError.xhtml并查看如何将错误代码传递到消息模板。 h:outputFormat value#{msg[Exception.generalError.message]} f:param value#{errorCode}//h:outputFormat 这里还有很多需要改进的地方。 您可以使用javax.faces.application.ProjectStage查找应用程序正在运行的当前模式。如果您在ProjectStage.Development中运行则还可以将完整的堆栈跟踪信息放到UI上并使调试工作变得容易一些。 以下代码段尝试从JNDI获取ProjectStage。 public static boolean isProduction() {ProjectStage stage ProjectStage.Development;String stageValue null;try {InitialContext ctx new InitialContext();stageValue (String) ctx.lookup(ProjectStage.PROJECT_STAGE_JNDI_NAME);stage ProjectStage.valueOf(stageValue);} catch (NamingException | IllegalArgumentException | NullPointerException e) {logger.error(Could not lookup JNDI object with name javax.faces.PROJECT_STAGE. Using default production);}return ProjectStage.Production stage;} 那三位数的Http错误页呢 那是另一件事。 其余所有3位http错误代码这些错误代码将返回看起来不太好看的错误页面之一。 唯一要做的就是将它们映射到web.xml中如下所示 error-pageerror-code404/error-codelocation/404.xhtml/location/error-page 您应该确保已放置这些映射并向用户显示有意义的错误。 始终提供一种从那里进一步导航的方法应该成为最佳实践。 参考 Java EE 6示例–使用Galleria增强安全性–第5部分 Java EE 6示例–优雅地处理Galleria中的错误–我们JCG合作伙伴 Markus Eisele在Java企业软件开发博客上的第6部分 。 翻译自: https://www.javacodegeeks.com/2012/04/java-ee-6-example-galleria-part-3.html
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/91991.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!