15.3. 验证
Seam安全中的验证特性是基于JAAS (Java Authentication and Authorization Service)开发的,它提供了用来进行用户身份认证的高度可配置的接口。然而,针对复杂多变的验证需求,Seam提供了一套非常简单的验证方法来隐藏JAAS的复杂性。
15.3.1. 配置一个验证组件
注意: 如果你使用Seam的身份管理功能(稍后介绍),那么就不用特地建立一个验证组件(意味着你可以跳过这一章)。
这种简单的验证方法由Seam的一个内置的JAAS登录组件提供,叫做SeamLoginModule,它将验证功能转移到你自己编写的一个Seam组件之中。该登录模块已经作为Seam的默认程序规则设置好了,你不需要额外的配置文件。你可以在一个编写一个你自己的方法来进行验证,稍经修改也可以用来结合其他第三方程序进行验证。这些简单的配置需要在components.xml中添加一个identity组件:
<!----><components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:security="http://jboss.com/products/seam/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd
http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.1.xsd
">
<security:identity authenticate-method="#{authenticator.authenticate}"/>
components>
EL表达式#{authenticator.authenticate}绑定到验证组件的验证方法上,该方法被用来对登录的用户进行验证。
15.3.2. 编写验证方法
components.xml文档中identity的authenticate-method属性指出SeamLoginModule将使用哪个方法来进行用户验证。该方法没有参数,并且返回值为boolean类型,用于判断登录是否成功。用户名和密码可以分别从Credentials.getUsername()和Credentials.getPassword()得到。用户所属的角色通过Identity.addRole()来添加。下面就是一个写在POJO组件中的完整验证方法:
<!---->@Name("authenticator")
public class Authenticator {
@In EntityManager entityManager;
@In Credentials credentials;
@In Identity identity;
public boolean authenticate() {
try {
User user = (User) entityManager.createQuery(
"from User where username = :username and password = :password")
.setParameter("username", credentials.getUsername())
.setParameter("password", credentials.getPassword())
.getSingleResult();
if (user.getRoles() != null) {
for (UserRole mr : user.getRoles())
identity.addRole(mr.getName());
}
return true;
} catch (NoResultException ex) {
return false;
}
}
}
在上面的例子中,User和UserRole都是实体。 roles属性包含了用户所拥有的角色,这个属性必须是一个字符串组成的Set,例如“admin”、“user”等。上面的程序中,如果符合条件的用户记录没有找到,那么抛出NoResultException 异常,验证失败,登录失败。
提示:
当编写验证方法的时候,必须保证这个方法为最简的,因为Seam安全无法保证这个方法会被调用多少次,同一个请求中它可能被调用多次。因此,任一判断成功或失败之外的代码,应该写成实现an event observer。想要知道Seam安全所提交事件的更多信息,请参考后面的安全事件一章。
15.3.2.1. Identity.addRole()
Identity.addRole()方法会根据当前session是否有效来执行不同的任务。如果当前session未经过验证,那么
addRole()只会在验证过程中被调用。该方法被调用的时候,角色名称会被加入pre-authenticated的临时角色列表。一旦验证成功,pre-authenticated中的临时角色会变为真正的角色,并且在调用
Identity.hasRole()的时候,会返回
true。The following sequence diagram represents the list of pre-authenticated roles as a first class object to show more clearly how it fits in to the authentication process.
如果当前session通过验证,然后调用Identity.addRole(),就会立即将指定的角色赋予当前用户。
15.3.2.2. 编写一个与安全事件相关的observer
让我们来讨论一个实际例子。当登录成功以后,一些用户数据需要被更新。这个功能可以通过编写一个事件观察器的方式来实现,通过监听org.jboss.seam.security.loginSuccessful事件,像这样:
<!---->@In UserStats userStats;
@Observer("org.jboss.seam.security.loginSuccessful")
public void updateUserStats() {
userStats.setLastLoginDate(new Date());
userStats.incrementLoginCount();
}
这个观察器方法可以放在任何地方,甚至是验证组件里面。你可以在后面的章节中找到更多关于安全事件的内容。
15.3.3. 编写一个登录表单
credentials组件里面提供了
username
与password
属性,符合绝大多数常用的用户登录验证情况。这些属性可以直接被绑定到登录表单的相关输入区上。一旦对这些属性进行了相关设定,那么调用identity.login()就会使用
credentials对用户进行权限验证。下面就是一个简单的登录表单例子:
<!----><div>
<h:outputLabel for="name" value="Username"/>
<h:inputText id="name" value="#{credentials.username}"/>
div>
<div>
<h:outputLabel for="password" value="Password"/>
<h:inputSecret id="password" value="#{credentials.password}"/>
div>
<div>
<h:commandButton value="Login" action="#{identity.login}"/>
div>
和登录类似,退出登录可以调用#{identity.logout}。调用这个方法会清除当前登录用户的登录状态,并且让当前用户的session失效。
15.3.4. 配置概述
总的来说,配置登录验证,只需要三个步骤:
15.3.5. 记住我
Seam安全框架提供了一个类似“记住我”的功能,这个功能我们经常能在其他网站上看见。这个功能实际上是提供了两种不同的习惯或者模式。
第一种模式,浏览器将用户名通过cookie的方式保存下来,只保留密码文本框让用户填写(市面上常见的浏览器都提供了保存密码的功能)。
第二种模式,在cookie中保留用户的唯一性认证信息,允许用户下次登录网站的时候不用输入密码就能够自动通过登录权限验证。
警告
使用客户端的cookie记录用户信息,从而实现自动登录的方式是非常危险的。虽然你方便了客户,但是同时,你站点上的任何一个跨站点脚本安全漏洞都可能会造成严重的后果。如果没有登录验证cookie,那么黑客唯一能够通过XSS偷取的信息就只剩下当前用户的session了。这就意味着,只有当用户登录网站创建了一个session的一小段时间才可能被攻击。如果黑客能够通过“记住我”这个功能从保存在客户端的cookie中得到用户的帐号信息,那么他以后随时都能够以客户的身份通过登录验证。当然,你需要知道,前面说的这些攻击能否实现,也取决于你对XSS攻击做了哪些防范。如果你能够确保你的网站100%防御了XSS攻击,你完全可以让用户输入的数据全部都显示在页面上。只要你能够实现,这绝对是意见了不起的成就。
现在,几乎所有浏览器厂商都认识到了这个问题,并且提供了一个“保存密码”的功能。浏览器会为网站的一些页面,或者是一个domain保存用户的用户名和密码。这样用户在登录网站的时候,即使session未经过登录验证,浏览器也会自动将用户名密码填入指定的登录表单。网站的设计者可以放一个登录快捷键在页面上,然后让浏览器去记录用户名和密码,这样实现类似“记住我”的功能会更加安全。一些浏览器(例如Safari和OS X)甚至能够将用户的登录数据加密并保存在操作系统的密码表中。如果是在网络环境下,这个密码表还可以跟随用户到任何地方(例如从用户的台式机到笔记本)。而cookie在通常情况下是无法实现同步的。
总结:你最好在任何情况下都不要使用cookie来实现自动登录验证。不过,仅仅使用cookie来记住用户名,并且自动将保存的用户名填写进入表单中,就不会有太大的问题,同时也能在一定程度上方便用户。
如果想要默认打开“记住我”的功能(仅仅记住用户名),你不需要做任何额外的配置。只需要在登录表单中将记住我的复选框绑定到rememberMe.enabled,就像下面这个例子里面写的:
<!----><div>
<h:outputLabel for="name" value="User name"/>
<h:inputText id="name" value="#{credentials.username}"/>
div>
<div>
<h:outputLabel for="password" value="Password"/>
<h:inputSecret id="password" value="#{credentials.password}" redisplay="true"/>
div>
<div class="loginRow">
<h:outputLabel for="rememberMe" value="Remember me"/>
<h:selectBooleanCheckbox id="rememberMe" value="#{rememberMe.enabled}"/>
div>
15.3.5.1. 基于特征的记住我验证
如果你需要使用记住我功能中基于特征的自动验证模式,你首先需要配置一个特征仓库。最常见的方式就是将验证特征存储在一个数据库中(Seam支持的数据库)。你也可以通过实现org.jboss.seam.security.TokenStore接口的方式实现特征仓库。本节假设你使用使用一个虚拟的
JpaTokenStore实现将验证需要用到的所有特征数据存储在数据库的一个表中。
首先你需要创建一个新的包含了tokens的Entity。下面这个例子展现了一种你可能用到的结构:
<!---->@Entity
public class AuthenticationToken implements Serializable {
private Integer tokenId;
private String username;
private String value;
@Id @GeneratedValue
public Integer getTokenId() {
return tokenId;
}
public void setTokenId(Integer tokenId) {
this.tokenId = tokenId;
}
@TokenUsername
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@TokenValue
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
在上面的例子中,我们使用了一对特殊的注解,@TokenUsername
和@TokenValue,它们用来指出实体的
username和特征属性。这些注解标识了实体中需要用来验证的所有特征数据。
下一步就是配置JpaTokenStore,使用
entity bean来存储或读取验证特征。具体的操作为,在components.xml中加入一个
token-class
属性:
<!----><security:jpa-token-store token-class="org.jboss.seam.example.seamspace.AuthenticationToken"/>
然后,在components.xml
加入RememberMe设置,并且将模式设置为
autoLogin:
<!----><security:remember-me mode="autoLogin"/>
通过上面这些设置,用户每次访问你的网站的时候就可以自动登录了(只要用户点击了“记住我”复选框)。
15.3.6. 处理安全异常
在登录的时候难免出现异常,为了防止用户看见默认的错误页面,我们建议你在pages.xml里面配置一下页面自动跳转设置,这样就能够在出错的时候自动跳转到一些比较“漂亮”的页面上。最常见的两种安全异常为:
在出现NotLoggedInException异常的情况下,我们建议将页面跳转到登陆页面或者注册页面
在出现
AuthorizationException异常的时候,可以跳转到一个提示用户没有相应权限的页面。
下面是一个
pages.xml文件的片段,这里面配置了这两种安全异常出现时的跳转规则:
<!----><pages>
...
<exception class="org.jboss.seam.security.NotLoggedInException">
<redirect view-id="/login.xhtml">
<message>You must be logged in to perform this actionmessage>
redirect>
exception>
<exception class="org.jboss.seam.security.AuthorizationException">
<end-conversation/>
<redirect view-id="/security_error.xhtml">
<message>You do not have the necessary security privileges to perform this action.message>
redirect>
exception>
pages>
大部分web应用程序会需要更多的跳转规则,用以处理各种实际业务逻辑。所以Seam包含了一些特殊的功能用来处理这些问题。
15.3.7. 登录重定向
当用户在没有登录的情况下试图访问某个需要登录才可以访问的页面(或者是一组用通配符指定的页面)的时候,Seam会将用户转到一个登录页面:
<!----><pages login-view-id="/login.xhtml">
<page view-id="/members/*" login-required="true"/>
...
pages>
当用户登录了以后,我们需要自动跳转到用户刚才未登录时尝试访问的页面。通过在components.xml中进行一下配置,就可以实现。并且跳转回刚才的页面的时候,所有的请求参数都会被保留。
<!----><event type="org.jboss.seam.security.notLoggedIn">
<action execute="#{redirect.captureCurrentView}"/>
event>
<event type="org.jboss.seam.security.postAuthenticate">
<action execute="#{redirect.returnToCapturedView}"/>
event>
需要注意一下,登录跳转功能是基于会话范围实现的,所以不要在authenticate()方法中结束当前会话。
15.3.8. HTTP验证
Seam也提供了HTTP Basic或HTTP Digest (RFC 2617)方式的验证。不过除非必要,我们不建议使用这些方式。在components.xml文件中,我们需要进行以下配置:
<!----><web:authentication-filter url-pattern="*.seam" auth-type="basic"/>
如果需要使用digest验证方式,key
和realm
也需要设置一下:
<!----><web:authentication-filter url-pattern="*.seam" auth-type="digest" key="AA3JK34aSDlkj" realm="My App"/>
key
属性可以是任意值。realm为用户在登录的时候需要告诉用户的realm名称。
15.3.8.1. 编写Digest验证器
当使用digest验证方式的时候,你的验证类需要集成自抽象类org.jboss.seam.security.digest.DigestAuthenticator,并且使用
validatePassword()方法来验证用户
的纯文本密码。例如:
<!---->public boolean authenticate() {
try {
User user = (User) entityManager.createQuery(
"from User where username = :username")
.setParameter("username", identity.getUsername())
.getSingleResult();
return validatePassword(user.getPassword());
} catch (NoResultException ex) {
return false;
}
}
15.3.9. 高级验证特性
本节将探索一些安全API中提供的高级验证特性,用来满足各种复杂的验证需求。
15.3.9.1. 使用容器的JAAS配置
如果你不需要使用Seam Security API提供的简化了的JAAS配置,那么你可以将其委托给系统默认的JAAS配置。你可以通过在components.xml
配置一个jaas-config-name
来实现。例如,如果你使用JBoss AS,并且希望使用其他的验证策略(例如使用JBoss AS提供的 UsersRolesLoginModule
登录模块)。你需要这么设置:
<!----><security:identity jaas-config-name="other"/>
需要记住的是,通过上面这些配置,并不是让用户验证交给了部署Seam应用程序的容器。它仅仅是告诉Seam安全框架使用在容器中配置的JAAS安全规则来验证用户。
分享到:
相关推荐
Seam 2.1 安全模块框架是一个强大的工具,为使用 Seam 开发的应用程序提供了全面的安全管理和认证机制。框架的核心特性包括: 1. **安全验证**:Seam 提供了一个可扩展的安全验证层,基于 Java Authentication and ...
Seam 2.1 是一款强大的企业级 Java 开发框架,它为开发人员提供了一种简单而高效的方式来构建复杂的 Web 应用程序。相较于 Seam 2.0 版本,Seam 2.1 在权限验证方面进行了大量的增强,引入了更多的功能和改进,以...
Seam 2.1 GA版本是Seam框架的一个重要更新,该版本带来了许多新的特性和改进,使得Seam更加适合于现代的企业级应用开发。 #### 二、支持Wicket ##### 技术细节 - **Seam组件与Wicket的整合**:Seam 2.1允许开发者...
**2.1.2版本** 是Seam框架的一个重要更新,它引入了许多新特性与改进,包括但不限于更好的性能优化、增强的安全性支持以及对现代Web开发实践的支持。此外,此版本还包含了对Seam核心功能的改进,例如对页面流控制的...
- 提供了全面的Seam框架技术文档和实例代码。 - 强调实践操作,适合自学和教学使用。 - 能够帮助开发者快速掌握Seam框架的关键技术和应用场景。 - **适用对象**: - Java EE开发者,特别是那些希望提高自己在...
Seam 是一种业级 企 Java 的应规用程序框架。它的灵感源自下列原 : 只有一种“工具” Seam为 应 业务业业 义 种统 组 你的 用程序中所有的 定 了一 一的 件模型。 Seam组件可能是 态义 关关 态 有状 的,包含与几...
### 基于Seam2.1的最新力作《Seam Framework: Experience the Evolution of Java EE, 2nd Edition》摘要分析 #### 核心概念:Seam框架概述 Seam框架是一款革命性的Web应用开发框架,它将标准的Java EE技术与一系列...
Seam Security 中的验证特性是基于JAAS (Java Authentication and Authorization Service)开发的,它提供了用来进行用户身份认证的高度可配置的接口。然而,针对复杂多变的验证需求,Seam ... 标签:安全相关框架
### Seam框架核心知识点详解 #### 一、Seam框架简介 Seam,全称为JBoss Seam,是一款基于Java EE 5的技术栈构建的应用框架。它通过整合JSF(JavaServer Faces)与EJB 3.0(Enterprise JavaBeans 3.0)组件,并充分...
整理自jboss seam 中文站,压缩为chm格式,便于广大jboss seam爱好者阅读,所有版权归jboss seam中文站所有。
Seam是一个针对Java开发的全栈式框架,旨在简化开发过程,提高开发效率。它适应了快速开发和简化架构的需求,与传统的Java框架相比,Seam具有更简洁的层次结构和强大的组件机制。 首先,Seam的核心设计理念在于减少...
seam+richfaces环境框架的配置文件,经过我多次尝试才配置好的环境配置文件,很有用,适合刚接触这些技术的初学者,希望大家支持!
中文版的Seam_2.0_Reference_zh_CN.pdf为开发者提供了详细的框架功能和用法,覆盖了组件、事件处理、安全、国际化等各个方面,是学习和理解Seam不可或缺的资源。 **2. RichFaces** RichFaces 是一套基于JSF的组件...
【JBoss Seam 2.0文档详解】 JBoss Seam 是一个开源的企业级开发框架,它旨在简化Java EE应用的开发过程,特别是在Web和富互联网应用程序(Rich Internet Applications, RIA)领域。Seam 2.0是其重要的版本,提供了...
4. **安全性和身份验证**:Seam提供了内置的安全框架,可以轻松集成到JSF应用中,实现用户身份验证和授权。 5. **国际化和本地化**:Seam与JSF结合,使得应用的国际化和本地化变得更加简单,开发者可以通过Seam的...
`seam-gen`命令的工作原理类似于Ruby语言中的“scaffold”概念,它通过一系列预定义的模板和规则,自动生成包含基本功能模块的代码框架,从而显著提高开发效率。在使用`seam-gen`之前,需要确保系统中已安装了Ant ...
JBoss Seam 是一款基于 Java 的企业级应用开发框架,它简化了应用程序的开发过程,特别是那些涉及复杂的用户交互、业务逻辑以及数据处理的应用。Seam 结合了 JavaServer Faces (JSF)、Java Persistence API (JPA)、...
文档提供了丰富的教程内容,覆盖了Seam框架的基本使用方法到高级特性,适合不同程度的学习者进行参考学习。 ##### 1. 使用Seam示例 - **在JBoss AS上运行示例**:这部分介绍了如何在JBoss Application Server上...
5. **Seam框架结构**:Seam框架包含了多个模块,如Seam Core、Seam Faces、Seam Security等,了解这些模块的功能和它们之间的关系是准备工作的关键。 6. **Seam配置**:`seam-getting-started-build.xml`文件很可能...