|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
java是一种面向对象的编程语言,优点是可移植性比较高,最初设计时就是本着一次编写到处执行设计的。可以开发各种应用程序和游戏,不过速度没有c++快,所以一般是不用java来编写应用程序和电脑游戏。js|办理择要
在一个有暗码回护的Web使用中,准确处置用户加入历程其实不仅仅只需挪用HttpSession的invalidate()办法。如今年夜部分扫瞄器上都有前进和行进按钮,同意用户前进或行进到一个页面。假如在用户在加入一个Web使用后按了前进按钮扫瞄器把缓存中的页面出现给用户,这会利用户发生困惑,他们会入手下手忧虑他们的团体数据是不是平安。很多Web使用强制用户加入时封闭全部扫瞄器,如许,用户就没法点击前进按钮了。另有一些利用javascript,但在某些客户端扫瞄器这却纷歧定起感化。这些办理计划都很愚笨且不克不及包管在任一情形下100%无效,同时,它也请求用户有必定的操纵履历。
这篇文章以示例论述了准确办理用户加入成绩的计划。作者KevinLe起首形貌了一个暗码回护Web使用,然后以示例程序注释成绩怎样发生并会商办理成绩的计划。文章固然是针对JSP页面举行论述,但作者所论述的观点很简单了解切可以为其他Web手艺所接纳。最初作者展现了怎样用JakartaStruts文雅地办理这一成绩。
年夜部分Web使用不会包括象银行账户或信誉卡材料那样秘密的信息,但一旦触及到敏感数据,我们就必要供应一类暗码回护机制。举例来讲,一个工场中工人经由过程Web会见他们的工夫布置、进进他们的练习课程和检察他们的薪金等等。此时使用SSL(SecureSocketLayer)有点杀鸡用牛刀的感到,但不成否定,我们又必需为这些使用供应暗码回护,不然,工人(也就是Web使用的利用者)能够窥伺到工场中其他雇员的公家秘密信息。
与上述情况类似的另有位处藏书楼、病院等公开场合的盘算机。在这些中央,很多用户配合利用几台盘算机,此时回护用户的团体数据就显得相当主要。计划优秀编写优异的使用对用户专业常识的请求少之又少。
我们来看一下实际天下中一个完善的Web使用是怎样体现的:一个用户经由过程扫瞄器会见一个页面。Web使用展示一个上岸页面请求用户输出无效的考证信息。用户输出了用户名和暗码。此时我们假定用户供应的身份考证信息是准确的,经由了考证历程,Web使用同意用户扫瞄他有权会见的地区。用户想加入时,点击加入按钮,Web使用请求用户确认他是不是则真的必要加入,假如用户断定加入,Session停止,Web使用从头定位到上岸页面。用户能够宁神的分开而不必忧虑他的信息会保守。另外一个用户坐到了统一台电脑前,他点击前进按钮,Web使用不该该呈现上一个用户会见过的任何一个页面。现实上,Web使用在第二个用户供应准确的考证信息之前应该一向停止在上岸页面上。
经由过程示例程序,文章向您论述了怎样在一个Web使用中完成这一功效。
JSP示例
为了更加无效地论述完成计划,本文将从展现一个示例使用logoutSampleJSP1中碰着的成绩入手下手。这个示例代表了很多没有准确办理加入历程的Web使用。logoutSampleJSP1包括了下述jsp页面:login.jsp,home.jsp,secure1.jsp,secure2.jsp,logout.jsp,loginAction.jsp,andlogoutAction.jsp。个中页面home.jsp,secure1.jsp,secure2.jsp,和logout.jsp是不同意未经认证的用户会见的,也就是说,这些页面包括了主要信息,在用户上岸之前大概加入以后都不该该呈现在扫瞄器中。login.jsp包括了用于用户输出用户名和暗码的form。logout.jsp页包括了请求用户确认是不是加入的form。loginAction.jsp和logoutAction.jsp作为把持器分离包括了上岸和加入代码。
第二个示例使用logoutSampleJSP2展现了怎样办理示例logoutSampleJSP1中的成绩。但是,第二个使用本身也是有疑问的。在特定的情形下,加入成绩仍是会呈现。
第三个示例使用logoutSampleJSP3在第二个示例长进行了改善,对照完美地办理了加入成绩。
最初一个示例logoutSampleStruts展现了Struts怎样幽美地办理上岸成绩。
注重:本文所附示例在最新版本的MicrosoftInternetExplorer(IE),NetscapeNavigator,Mozilla,FireFox和Avant扫瞄器上测试经由过程。
Loginaction
BrianPontarelli的典范文章《J2EESecurity:ContainerVersusCustom》会商了分歧的J2EE认证路子。文章同时指出,HTTP协定和基于form的认证并未供应处置用户加入的机制。因而,办理路子即是引进自界说的平安完成机制。
自界说的平安认证机制广泛接纳的办法是从form中取得用户输出的认证信息,然后到诸如LDAP(lightweightdirectoryaccessprotocol)或干系数据库的平安域中举行认证。假如用户供应的认证信息是无效的,上岸举措往HttpSession对象中注进某个对象。HttpSession存在着注进的对象则暗示用户已上岸。为了便利读者了解,本文所附的示例只往HttpSession中写进一个用户名以标明用户已上岸。清单1是从loginAction.jsp页面中节选的一段代码以此论述上岸举措:
Listing1
//...
//initializeRequestDispatcherobject;setforwardtohomepagebydefault
RequestDispatcherrd=request.getRequestDispatcher("home.jsp");
//Prepareconnectionandstatement
rs=stmt.executeQuery("selectpasswordfromUSERwhereuserName="+userName+"");
if(rs.next()){//Queryonlyreturns1recordintheresultset;only1
passwordperuserNamewhichisalsotheprimarykey
if(rs.getString("password").equals(password)){//Ifvalidpassword
session.setAttribute("User",userName);//Savesusernamestringinthesessionobject
}
else{//Passworddoesnotmatch,i.e.,invaliduserpassword
request.setAttribute("Error","Invalidpassword.");
rd=request.getRequestDispatcher("login.jsp");
}
}//Norecordintheresultset,i.e.,invalidusername
else{
request.setAttribute("Error","Invalidusername.");
rd=request.getRequestDispatcher("login.jsp");
}
}
//Asacontroller,loginAction.jspfinallyeitherforwardsto"login.jsp"or"home.jsp"
rd.forward(request,response);
//...
本文所附示例均以干系型数据库作为平安域,但本文所论述的概念对任何范例的平安域都是合用的。
Logoutaction
加入举措就包括了复杂的删除用户名和对用户的HttpSession对象挪用invalidate()办法。清单2是从loginoutAction.jsp页面中节选的一段代码以此论述加入举措:
Listing2
//...
session.removeAttribute("User");
session.invalidate();
//...
制止未经认证会见受回护的JSP页面
从form中猎取用户提交的认证信息并经由考证后,上岸举措复杂地往HttpSession对象中写进一个用户名,加入举措则做相反的事情,它从用户的HttpSession对象中删除用户名并挪用invalidate()办法烧毁HttpSession。为了使上岸和加入举措真正发扬感化,一切受回护的JSP页面都应当起首考证HttpSession中是不是包括了用户名以确认以后用户是不是已上岸。假如HttpSession中包括了用户名,也就是说用户已上岸,Web使用则将残剩的JSP页发送给扫瞄器,不然,JSP页将跳转到上岸页login.jsp。页面home.jsp,secure1.jsp,secure2.jsp和logout.jsp均包括清单3中的代码段:
Listing3
//...
StringuserName=(String)session.getAttribute("User");
if(null==userName){
request.setAttribute("Error","Sessionhasended.Pleaselogin.");
RequestDispatcherrd=request.getRequestDispatcher("login.jsp");
rd.forward(request,response);
}
//...
//AllowtherestofthedynamiccontentinthisJSPtobeservedtothebrowser
//...
在这个代码段中,程序从HttpSession中减缩username字符串。假如字符串为空,Web使用则主动中断实行以后页面并跳转到上岸页,同时给出Sessionhasended.Pleaselogin.的提醒;假如不为空,Web使用则持续实行,也就是把残剩的页面供应给用户。
运转logoutSampleJSP1
运转logoutSampleJSP1将会呈现以下几种情况:
?假如用户没有上岸,Web使用将会准确中断受回护页面home.jsp,secure1.jsp,secure2.jsp和logout.jsp的实行,也就是说,假设用户在扫瞄器地点栏中间接敲进受回护JSP页的地点试图会见,Web使用将主动跳转到上岸页并提醒Sessionhasended.Pleaselogin.
?一样的,当一个用户已加入,Web使用也会准确中断受回护页面home.jsp,secure1.jsp,secure2.jsp和logout.jsp的实行
?用户加入后,假如点击扫瞄器上的前进按钮,Web使用将不克不及准确回护受回护的页面――在Session烧毁后(用户加入)受回护的JSP页从头在扫瞄器中显现出来。但是,假如用户点击前往页面上的任何链接,Web使用将会跳转到上岸页面并提醒Sessionhasended.Pleaselogin.
制止扫瞄器缓存
上述成绩的本源在于年夜部分扫瞄器都有一个前进按钮。当点击前进按钮时,默许情形下扫瞄器不是从Web服务器上从头猎取页面,而是从扫瞄器缓存中载进页面。基于Java的Web使用并未限定这一功效,在基于PHP、ASP和.NET的Web使用中也一样存在这一成绩。
在用户点击前进按钮后,扫瞄器到服务器再从服务器到扫瞄器如许一般意义上的HTTP回路并没有创建,仅仅只是用户,扫瞄器弛缓存举行了交互。以是,即便包括了清单3上的代码来回护JSP页面,当点击前进按钮时,这些代码是不会实行的。
缓存的优劣,真是仁者见仁智者见智。缓存切实其实供应了一些便当,但一般只在利用静态的HTML页面或基于图形或影响的页面你才干感觉到。而另外一方面,Web使用一般是基于数据的,数据一般是频仍变动的。与从缓存中读取并显现过时的数据比拟,供应最新的数据才是更主要的!
侥幸的是,HTTP头信息“Expires”和“Cache-Control”为使用程序服务器供应了一个把持扫瞄器和代办署理服务器上缓存的机制。HTTP头信息Expires告知代办署理服务器它的缓存页面什么时候将过时。HTTP1.1标准中新界说的头信息Cache-Control能够关照扫瞄器不缓存任何页面。当点击前进按钮时,扫瞄重视新会见服务器已猎取页面。以下是利用Cache-Control的基础办法:
?no-cache:强迫缓存从服务器上猎取新的页面
?no-store:在任何情况下缓存不保留任何页面
HTTP1.0标准中的Pragma:no-cache同等于HTTP1.1标准中的Cache-Control:no-cache,一样能够包括在头信息中。
经由过程利用HTTP头信息的cache把持,第二个示例使用logoutSampleJSP2办理了logoutSampleJSP1的成绩。logoutSampleJSP2与logoutSampleJSP1分歧体现在以下代码段中,这一代码段到场进一切受回护的页面中:
//...
response.setHeader("Cache-Control","no-cache");//Forcescachestoobtainanewcopyofthepagefromtheoriginserver
response.setHeader("Cache-Control","no-store");//Directscachesnottostorethepageunderanycircumstance
response.setDateHeader("Expires",0);//Causestheproxycachetoseethepageas"stale"
response.setHeader("Pragma","no-cache");//HTTP1.0backwardcompatibility
StringuserName=(String)session.getAttribute("User");
if(null==userName){
request.setAttribute("Error","Sessionhasended.Pleaselogin.");
RequestDispatcherrd=request.getRequestDispatcher("login.jsp");
rd.forward(request,response);
}
//...
经由过程设置头信息和反省HttpSession中的用户名确保了扫瞄器不缓存页面,同时,假如用户未上岸,受回护的JSP页面将不会发送到扫瞄器,取而代之的将是上岸页面login.jsp。
运转logoutSampleJSP2
运转logoutSampleJSP2后将回看到以下了局:
?当用户加入后试图点击前进按钮,扫瞄器其实不会显现受回护的页面,它只会实际上岸页login.jsp同时给出提醒信息Sessionhasended.Pleaselogin.
?但是,当按了前进按钮前往的页是处置用户提交数据的页面时,IE和Avant扫瞄器将弹出以下信息提醒:
告诫:页面已过时……(你一定见过)
选择革新后前一个JSP页面将从头显现在扫瞄器中。很明显,这不是我们所想看到的由于它违反了logout举措的目标。产生这一征象时,极可能是一个歹意用户在实验猎取其他用户的数据。但是,这个成绩仅仅呈现在前进按钮对应的是一个处置POST哀求的页面。
纪录最初上岸工夫
上述成绩之以是呈现是由于扫瞄器将其缓存中的数据从头提交了。这本文的例子中,数据包括了用户名和暗码。不管是不是给出平安告诫信息,扫瞄器此时起到了负面感化。
为懂得决logoutSampleJSP2中呈现的成绩,logoutSampleJSP3的login.jsp在包括username和password的基本上还包括了一个称作lastLogon的埋没表单域,此表单域静态的用一个long型值初始化。这个long型值是挪用System.currentTimeMillis()猎取到的自1970年1月1日以来的毫秒数。当login.jsp中的form提交时,loginAction.jsp起首将埋没域中的值与用户数据库中的值举行对照。只要当lastLogon表单域中的值年夜于数据库中的值时Web使用才以为这是个无效的上岸。
为了考证上岸,数据库中lastLogon字段必需以表单中的lastLogon值举行更新。上例中,当扫瞄重视复提交数据时,表单中的lastLogon值不比数据库中的lastLogon值年夜,因而,loginAction转到login.jsp页面,并提醒Sessionhasended.Pleaselogin.清单5是loginAction中节选的代码段:
清单5
//...
RequestDispatcherrd=request.getRequestDispatcher("home.jsp");//Forwardtohomepagebydefault
//...
if(rs.getString("password").equals(password)){//Ifvalidpassword
longlastLogonDB=rs.getLong("lastLogon");
if(lastLogonForm>lastLogonDB){
session.setAttribute("User",userName);//Savesusernamestringinthesessionobject
stmt.executeUpdate("updateUSERsetlastLogon="+lastLogonForm+"whereuserName="+userName+"");
}
else{
request.setAttribute("Error","Sessionhasended.Pleaselogin.");
rd=request.getRequestDispatcher("login.jsp");}
}
else{//Passworddoesnotmatch,i.e.,invaliduserpassword
request.setAttribute("Error","Invalidpassword.");
rd=request.getRequestDispatcher("login.jsp");
}
//...
rd.forward(request,response);
//...
为了完成上述办法,你必需纪录每一个用户的最初上岸工夫。关于接纳干系型数据库平安域来讲,这点能够能够经由过程在某个表中加上lastLogin字段轻松完成。LDAP和其他的平安域必要略微动下头脑,但很明显是能够完成的。
暗示最初上岸工夫的办法有良多。示例logoutSampleJSP3使用了自1970年1月1日以来的毫秒数。这个办法在很多人在分歧扫瞄器顶用一个用户账号上岸时也是可行的。
运转logoutSampleJSP3
运转示例logoutSampleJSP3将展现怎样准确处置加入成绩。一旦用户加入,点击扫瞄器上的前进按钮在任何情形下都不会是受回护的页面在扫瞄器上显现出来。这个示例展现了怎样准确处置加入成绩而不必要分外的培训。
为了使代码更简洁无效,一些冗余的代码能够剔撤除。一种路子就是把清单4中的代码写到一个独自的JSP页中,经由过程标签<jsp:include>其他页面也能够援用。
Struts框架下的加入完成
与间接利用JSP或JSP/servlets比拟,另外一个可选的计划是利用Struts。为一个基于Struts的Web使用增加一个处置加入成绩的框架能够文雅地不费力气的完成。这部分回功于Struts是接纳MVC计划形式的因而将模子和视图明晰的分隔。别的,Java是一个面向对象的言语,其撑持承继,能够比JSP中的剧本更加简单地完成代码重用。在Struts中,清单4中的代码能够从JSP页面中移植到Action类的execute()办法中。
别的,我们还能够界说一个承继StrutsAction类的基础类,其execute()办法中包括了清单4中的代码。经由过程利用类承继机制,其他类能够承继基础类中的通用逻辑来设置HTTP头信息和检索HttpSession对象中的username字符串。这个基础类是一个笼统类并界说了一个笼统办法executeAction()。一切承继自基类的子类都应完成exectuteAction()办法而不是掩盖它。清单6是基类的部分代码:
清单6
publicabstractclassBaseActionextendsAction{
publicActionForwardexecute(ActionMappingmapping,ActionFormform,
HttpServletRequestrequest,HttpServletResponseresponse)
throwsIOException,ServletException{
response.setHeader("Cache-Control","no-cache");//Forcescachestoobtainanewcopyofthepagefromtheoriginserver
response.setHeader("Cache-Control","no-store");//Directscachesnottostorethepageunderanycircumstance
response.setDateHeader("Expires",0);//Causestheproxycachetoseethepageas"stale"
response.setHeader("Pragma","no-cache");//HTTP1.0backwardcompatibility
if(!this.userIsLoggedIn(request)){
ActionErrorserrors=newActionErrors();
errors.add("error",newActionError("logon.sessionEnded"));
this.saveErrors(request,errors);
returnmapping.findForward("sessionEnded");
}
returnexecuteAction(mapping,form,request,response);
}
protectedabstractActionForwardexecuteAction(ActionMappingmapping,
ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse)
throwsIOException,ServletException;
privatebooleanuserIsLoggedIn(HttpServletRequestrequest){
if(request.getSession().getAttribute("User")==null){
returnfalse;
}
returntrue;
}
}
清单6中的代码与清单4中的很相像,仅仅只是用ActionMappingfindForward替换了RequestDispatcherforward。清单6中,假如在HttpSession中未找到username字符串,ActionMapping对象将找到名为sessionEnded的forward元素并跳转到对应的path。假如找到了,子类将实行实在现了executeAction()办法的营业逻辑。因而,在设置文件struts-web.xml中为一切子类声明个一位为sessionEnded的forward元素是必需的。清单7以secure1action分析了如许一个声明:
清单7
<actionpath="/secure1"
type="com.kevinhle.logoutSampleStruts.Secure1Action"
scope="request">
<forwardname="success"path="/WEB-INF/jsps/secure1.jsp"/>
<forwardname="sessionEnded"path="/login.jsp"/>
</action>
承继自BaseAction类的子类Secure1Action完成了executeAction()办法而不是掩盖它。Secure1Action类不实行任何加入代码,如清单8:
publicclassSecure1ActionextendsBaseAction{
publicActionForwardexecuteAction(ActionMappingmapping,ActionFormform,
HttpServletRequestrequest,HttpServletResponseresponse)
throwsIOException,ServletException{
HttpSessionsession=request.getSession();
return(mapping.findForward("success"));
}
}
只必要界说一个基类而不必要分外的代码事情,上述办理计划是文雅而无效的。不论如何,将通用的举动办法写成一个承继StrutsAction的基类是很多Struts项目标配合履历,值得保举。
范围性
上述办理计划对JSP或基于Struts的Web使用都长短常复杂而有用的,但它仍是有某些范围。在我看来,这些范围并非相当紧急的。(范围性未翻译,请拜见原文)
结论
本文论述懂得决加入成绩的计划,只管计划复杂的使人惊奇,但却在一切情形下都能无效地事情。不管是对JSP仍是Struts,所要做的不外是写一段不凌驾50行的代码和一个纪录用户最初上岸工夫的办法。在Web使用中夹杂利用这些计划可以使反对的公家数据不致保守,同时,也能增添用户的履历。
多谢指点,其实我对.net和ruby也不是很了解,对与java也只是刚起步的阶段,只是在学习中有了点想法就写出来了,现在俺本科还没毕业,所以对大型项目基本上也没有什么经验。 |
|