没有比人更高的山

JSF中CommandButton与CommandLink传值0

f:param标签能够将一个参数添加到组件。需要注意的是f:param标签的不同表现依赖于它所关联的组件类型

【1】如果为 h:outputText添加f:param标签,那么JSF实现将使用参数来填充占位符,例如{0}、{1}等。

【2】如果添加f:param标签到h:commandLink,JSF实现会将参数值作为请求参数传递到服务器,如:

1
2
3
<h :commandLink actionListener="#{userListBean.checkUser}" value="审核通过">
    <f :param name="userId" value="#{user.userId}" />
</h>

在服务器端可以使用如下方法来获取传递到服务器端的值:

1
2
3
4
private void checkUser(ActionEvent actionEvent){
    String uid = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("userId");
    // other code
}

但是f:param的传值方式对于h:commandButton是没有作用的,(如果你是用搜索引擎搜到这篇文章的话,相信你肯定是遇到了这个问题),详情可以参考http://www.javaeye.com/topic/93388

如果是h:commandButton,那么可以使用f:attribute来进行传值,示例如下:

1
2
3
<h :commandButton actionListener="#{userListBean.resetPassword}" value="审核通过">
    <f :attribute name="userId" value="#{user.userId}"/>
</h>

在服务器端可以使用如下方法来获取传递到服务器端的值:

1
2
3
4
private void resetPassword(ActionEvent actionEvent){
    long userIdString = (Long) actionEvent.getComponent().getAttributes().get("userId");
    //other code ……
}
VN:F [1.7.5_995]
Rating: 0.0/10 (0 votes cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)

JSP中Session、Cookie的使用与“记住我的登录状态”示例程序2

本文为原创文章,转载请注明来源http://www.zhlwish.com

《session、cookie与“记住我的登录状态”的功能的实现》一文中详细讲解了Cookie和Session的原理以及“记住我的登录状态”功能的实现原理,本文以Servlet、JSP技术为例讲解Servlet中Cookie和Session的使用以及以此为基础的“记住我的登录状态”功能的实现方式,最后附上例子程序的源代码。

EL表达式

本文的样例源代码中用到了JSP EL表达式,JSP1.0中EL表达式是作为JSTL的一部分,即只能在JSTL标签中使用。JSP2.0中EL表达式可以在文本模板中使用,即可以直接在JSP页面中使用,本文的样例源代码使用${pageContext.request.contextPath}在JSP页面中获取当前Web应用的路径(context path),关于EL表达式的使用请参见《JSTL入门:表达式语言》,关于Web应用的路径请参见《Servlet、JSP中获取Web应用路径(context path)的方法》

Servlet中的Session

javax.servlet.http.HttpSession是Servlet API中定义的一个接口,其中比较重要的方法有:
将一个对象实例存放在Session中:

1
public void setAttribute(String name, Object value);

通过名称获取Session中的对象实例:

1
public Object getAttribute(String name);

Session对象可以用过HttpServletRequest对象获取,一般在Servlet中通过request.getSession();获取一个Session对象。

Web服务器每接收到一个新的请求,服务器就会新建一个Session对象,一般会将当前用户帐号存放于Session中,当然你可以放更多的对象,这样会增加服务器的负担。Session会在一定时间后由Servlet容器销毁,这个时间可以在Web应用的web.xml里面进行设置。

Servlet中的Cookie

javax.servlet.http.Cookie是一个类,有一个构造函数public Cookie(String name, String value),一般用下面的方式创建一个Cookie对象:

1
2
3
4
5
String cookieName = "userinfo";
String cookieValue = "admin,123";
Cookie cookie = new Cookie(cookieName, cookieValue);
cookie.setMaxAge(14 * 24 * 3600);//设置Cookie的过期时间
cookie.setPath(request.getContextPath());//设置路径

上面仅仅创建了一个Cookie,要使其产生作用,还需要使用response.addCookie(cookie);将Cookie发送到用户浏览器。

一般使用下面的方法将一个Cookie删除,也就是将Cookie设成过期并发送到客户端:

1
2
3
cookie.setMaxAge(0);
cookie.setPath(request.getContextPath());
response.addCookie(cookie);

记住我的登录状态的实现

Servlet中一般使用Filter来保护需要经过认证才能访问的资源,当Filter被调用时对Session以及Cookie中的数据进行检查,如果通过了认证,则放行这次请求;如果未通过认证,则强制跳转到登录页面。具体流程如下图所示:

记住我的登录状态序列图

图中的ForceLoginFilter和DoLoginServlet的实现请参见示例代码。

示例代码中,受保护的资源是admin文件夹下的所有内容,所以web.xml中ForceLoginFilter的url-pattern设置为/admin/*,如果你需要更灵活的资源控制,可以将url-pattern设置为/*,然后在ForceLoginFilter通过检查请求的URL来实现更为详细的控制。

示例代码中,密码使用明文存储在Cookie中,这是不可取的,需要加密。

示例代码使用eclipse开发,直接可以导入到eclipse工程中,在JDK5.0、Tomcat 6.0上测试通过,如果你在运行过程中发现bug,请留言提出,谢谢。

示例代码下载

VN:F [1.7.5_995]
Rating: 7.8/10 (4 votes cast)
VN:F [1.7.5_995]
Rating: -1 (from 1 vote)

Servlet、JSP中获取Web应用路径(context path)的方法0

在Servlet中可以直接使用request.getContextPath()获取当前web应用的路径(context path)

在JSP 2.0页面中可以使用el表达式${pageContext.request.contextPath}

如果是JSF页面,因为JSF默认使用#作为el表达式的起始符,所以应该写成#{pageContext.request.contextPath}

因为JSP2.0之前的版本不支持文本模板中的el表达式,故可以使用嵌入java代码来实现,和Servlet中一样

如果在JSP2.0之前的版本中使用了JSTL标签(el表达式的概念是JSTL 1.0推出来的),所以仍然可以配合JSTL的标签使用el表达式输出当前Web应用路径(context path)

VN:F [1.7.5_995]
Rating: 0.0/10 (0 votes cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)

JSF异常消息机制及应用0

JSF有两种异常消息:

[1] JSF提供的标准异常信息,如标准的验证器和转换器生成的异常信息等

[2] 自定义的异常信息

与消息相关的类是javax.faces.application.FacesMessage,他封装了单一的、本地化的、人类可以理解的消息,除了消息字符串本身外,FacesMessage还有三个属性:severity(严重性),summary(摘要),detail(详细信息)

serverity被定义成FacesMessage的内部类,有四种类型:Info、Warn、Error、Fatal
FacesContext负责维护FacesMessage的两个逻辑集合,一个和组件相关的消息集合、一个不与组件相关的消息集合,定义了如下的和消息相关的方法
public Iterator<FacesMessage> getMessages();
public Iterator<FacesMessage> getMessages(String clientId);
public Iterator<String> getClientIdsWithMessages();//返回有消息绑定的组件id
public void addMessage(String clientId, javax.faces.application.FacesMessage message);//向FacesContext中加入一条FacesMessage
public FacesMessage.Severity getMaximumSeverity();//返回最严重的问题的严重性

FacesMessage的部分源代码如下:


private FacesMessage.Severity _severity;
private String _summary;
private String _detail;

public FacesMessage()
{
    _severity = SEVERITY_INFO;
}

public FacesMessage(String summary)
{
    _summary = summary;
    _severity = SEVERITY_INFO;
}

public FacesMessage(String summary, String detail)
{
    _summary = summary;
    _detail = detail;
    _severity = SEVERITY_INFO;
}

public FacesMessage(FacesMessage.Severity severity, String summary, String detail)
{
    if(severity == null) throw new NullPointerException("severity");
    _severity = severity;
    _summary = summary;
    _detail = detail;
}

和消息相关的两个标签是<h:message>与<h:messages>:
<h:message> 有一个必设属性for,标示消息的绑定组件id;showDetail属性默认为false,showSummary属性默认为true
<h:messages>渲染所有的排队消息,在globalOnly设为true时只渲染哪些没有组件标识符的消息。

我一般会在项目中新建一个MessageUtil类,处理消息,其代码如下:

public class MessageUtil {

    public static void addErrorMessage(FacesContext context, String clientId, String message){
        context.addMessage(clientId, new FacesMessage(message));
    }

    public static void addErrorMessage(FacesContext context, String message) {
        addErrorMessage(context, null, message);
    }

    /**
    * Add a global message, the detail is null and the servity is info by default
    * @param message message summary
    */
    public static void addErrorMessage(String message){
        addErrorMessage(FacesContext.getCurrentInstance(), message);
    }

    /**
    * Binding messages to a ui component
    * @param clientId The ui component id
    * @param message message summary
    */
    public static void addErrorMessage(String clientId, String message){
        addErrorMessage(FacesContext.getCurrentInstance(), clientId, message);
    }
}

如 我们在登录时需要向用户提示“用户名或者密码错误”,可以调用public static void addErrorMessage(String message),然后在页面上使用<h:messages globalOnly=”true”/>来显示这个信息。

VN:F [1.7.5_995]
Rating: 0.0/10 (0 votes cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)

session、cookie与“记住我的登录状态”的功能的实现1

Cookie的机制

     Cookie是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能。

     Cookie的Domain和Path属性标识了这个Cookie是哪一个网站发送给浏览器的;Cookie的Expires属性标识了Cookie的有效时间,当Cookie的有效时间过了之后,这些数据就被自动删除了。

     如果不设置过期时间,则表示这个Cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,Cookie就消失了。这种生命期为浏览会话期的Cookie被称为会话Cookie。会话Cookie一般不保存在硬盘上而是保存在内存里。如果设置了过期时间,浏览器就会把Cookie保存到硬盘上,关闭后再次打开浏览器,这些Cookie依然有效直到超过设定的过期时间。存储在硬盘上的Cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存的Cookie,不同的浏览器有不同的处理方式。

Session的机制

     Session是存放在服务器端的类似于HashTable结构(每一种Web开发技术的实现可能不一样,下文直接称之为HashTable)来存放用户数据,当浏览器第一次发送请求时,服务器自动生成了一个HashTable和一个Session ID用来唯一标识这个HashTable,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的HashTable。

     一般情况下,服务器会在一定时间内(默认20分钟)保存这个HashTable,过了时间限制,就会销毁这个HashTable。在销毁之前,程序员可以将用户的一些数据以Key和Value的形式暂时存放在这个HashTable中。当然,也有使用数据库将这个HashTable序列化后保存起来的,这样的好处是没了时间的限制,坏处是随着时间的增加,这个数据库会急速膨胀,特别是访问量增加的时候。一般还是采取前一种方式,以减轻服务器压力。

Session的客户端实现形式(即Session ID的保存方法)

一般浏览器提供了两种方式来保存,还有一种是程序员使用HTML隐藏域的方式自定义实现:

[1] 使用Cookie来保存,这是最常见的方法,本文“记住我的登录状态”功能的实现正式基于这种方式的。服务器通过设置Cookie的方式将Session ID发送到浏览器。如果我们不设置这个过期时间,那么这个Cookie将不存放在硬盘上,当浏览器关闭的时候,Cookie就消失了,这个Session ID就丢失了。如果我们设置这个时间为若干天之后,那么这个Cookie会保存在客户端硬盘中,即使浏览器关闭,这个值仍然存在,下次访问相应网站时,同样会发送到服务器上。

[2] 使用URL附加信息的方式,也就是像我们经常看到JSP网站会有aaa.jsp?JSESSIONID=*一样的。这种方式和第一种方式里面不设置Cookie过期时间是一样的。

[3] 第三种方式是在页面表单里面增加隐藏域,这种方式实际上和第二种方式一样,只不过前者通过GET方式发送数据,后者使用POST方式发送数据。但是明显后者比较麻烦。

实现“记住我的登录状态”的功能

     前面我们了解到,如果我们将Session ID通过Cookie发送到客户端的时候设置其过期时间为1年,那么在今后的一年时间内,客户端访问我的网站的时候都回将这个Session ID值发送到服务器上,服务器根据这个Session ID从内存或者数据库里面恢复存放Key-Value对的Hashtable。

     其实这已经很好的实现了我们的功能了。但是,前面也提到了,实际上Session并不会一直都存在的,过了一定的时间之后,服务器上的Session就被销毁了,以减轻服务器的访问压力。当服务器上的数据被销毁后,即使客户端上存放了Cookie也没有办法“记住我的登录状态”了。

     通用的实现办法是,将用户的用户名和加密之后的密码也通过Cookie的方式存放在客户端,当服务器上的Session销毁以后,使用Cookie里面存放的用户名和加密之后的密码重新执行一次登录操作,重建Session,并更新客户端上Cookie中存放的的Session ID,而这个操作是发生在用户请求一个需要身份验证的页面资源的背后,对于用户来讲是透明的,于是就达到了“记住我的登录状态”的目的了。

到目前为止,已经基本明确了“记住我的登录状态”的实现方式和理论依据,后文将使用jsp和php两种Web开发技术来具体实现这个功能。

VN:F [1.7.5_995]
Rating: 7.5/10 (4 votes cast)
VN:F [1.7.5_995]
Rating: +2 (from 2 votes)