Seam的安全框架身份管理(Identity Management)(译)

15.4. 身份管理

身份管理提供了一个Seam应用程序的用户和角色管理的标准API,而且不用理会后端使用的是什么身份存储机制(数据库,LDAP等)。身份管理API的核心是identityManager组件,它提供了包括创建、修改和删除用户,授权和吊销角色,修改密码,启用和禁用用户账户,验证用户以及列出所有用户和角色的所有方法。

在使用身份管理组件之前,identityManager必须首先配置一个或多个IdentityStore。这些组件承担了与后端安全提供者进行交互的实际工作,不管是一个数据库、LDAP服务器还是其他的什么安全提供者。

15.4.1. 配置IdentityManager

identityManager组件允许为验证和授权操作配置为独立的身份存储。这意味着通过一个身份存储(例如一个LDAP目录)进行用户验证,而从另外一个身份存储(例如关系型数据库)取得它的角色。

Seam提供开箱即用两个IdentityStore实现;JpaIdentityStore使用关系数据库来存储用户和角色信息,如果在identityManager组件配置中没有显式说明,那么它是默认的身份存储机制。另外提供的实现是LdapIdentityStore,它使用一个LDAP目录来存储用户和角色。

identityManager组件拥有两个配置属性-identityStoreroleIdentityStore。这俩个属性的值必须是一个引用实现了IdentityStore接口的Seam组件的EL表达式。如上所述,如果不配置则假设JpaIdentityStore为默认值。如果只配置了identityStore属性,那么roleIdentityStore属性也使用相同的值。例如以下的在components.xml中的配置将指定使用LdapIdentityStore的相关用户和角色的操作:
<security:identity-manager identity-store="#{ldapIdentityStore}"/>
接下来的例子中的identityManager配置中,使用LdapIdentityStore的相关的用户操作,而使用JpaIdentityStore的相关的角色操作:
 <security:identity-manager 
    
identity-store="#{ldapIdentityStore}" 
    role-identity-store
="#{jpaIdentityStore}"/>
接下来的章节中将更详细的解释这些身份存储的实现

该身份存储允许将用户和角色存储在关系型数据库中。它被设计成尽可能的不受数据库模式设计的限制,在表结构上具有很大的灵活性。这是通过使用一套特别的注释实现的,实体Bean通过配置这些注释而能存储用户和角色纪录。

JpaIdentityStore需要配置user-classrole-class这俩个属性。它们将各自引用用来存储用户和角色纪录的实体类。接下来的例子来自SeamSpace,演示了在components.xml中的配置:
<security:jpa-identity-store 
    
user-class="org.jboss.seam.example.seamspace.MemberAccount"
    role-class
="org.jboss.seam.example.seamspace.MemberRole"/>

如上所述,有一套用来将实体Bean配置成能存储用户和角色的特别注释。下表将详细描述这些注释。

表 15.1. 用户实体注释

注释

状态

描述

@UserPrincipal

必选

标明字段或方法包含用户的用户名。

@UserPassword

必选

标明字段或方法包含用户的密码。它允许指定一个hash算法来对密码进行散列。可选的值包括md5, shanone。例如:

@UserPassword(hash = "md5")

public String getPasswordHash() { 

  return passwordHash; 

}

如果一个应用需要一个不被Seam原生支持的散列算法,可以通过扩展PasswordHash组件来实现其他的散列算法。

@UserFirstName

可选

标明字段或方法包含用户的姓。

@UserLastName

可选

标明字段或方法包含用户的名。

@UserEnabled

可选

标明字段或方法包含用户的可用状态。它是一个布尔属性,如果没有指定,那么所有用户账户都被假定为可用。

@UserRoles

必选

标明字段或方法包含用户的角色。这个属性将在下文进行更详细的描述。


表 15.2. 角色实体注释

注释

状态

描述

@RoleName

必选

标明字段或方法包含角色的名称。

@RoleGroups

可选

标明字段或方法包含角色的组关系。

@RoleConditional

可选

标明字段或方法说明角色是否有依赖性。依赖性角色将在后文中解释。


如前所述,JpaIdentityStore被设计成尽可能的灵活,以应对你的数据库模式中的用户和角色的表设计。在本节中,将看到一些可能被用到的存储用户和角色纪录的数据库模式。

在这个简化的示例中,一个简单的用户和角色表通过一个名为UserRoles的交叉引用表实现了多对多的关系。

@Entity
public class User {

  
private Integer userId;
  
private String username;
  
private String passwordHash;
  
private Set<Role> roles;

  @Id @GeneratedValue
  
public Integer getUserId() { return userId; }
  
public void setUserId(Integer userId) { this.userId = userId; }

  @UserPrincipal
  
public String getUsername() { return username; }
  
public void setUsername(String username) { this.username = username; }

  @UserPassword(hash 
= "md5")
  
public String getPasswordHash() { return passwordHash; }
  
public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
  
  @UserRoles
  @ManyToMany(targetEntity 
= Role.class)
  @JoinTable(name 
= "UserRoles"
    joinColumns 
= @JoinColumn(name = "UserId"),
    inverseJoinColumns 
= @JoinColumn(name = "RoleId"))
  
public Set<Role> getRoles() { return roles; }
  
public void setRoles(Set<Role> roles) { this.roles = roles; }

}

@Entity
public class Role {
  
private Integer roleId;
  
private String rolename;
  
  @Id @Generated
  
public Integer getRoleId() { return roleId; }
  
public void setRoleId(Integer roleId) { this.roleId = roleId; }
  
  @RoleName
  
public String getRolename() { return rolename; }
  
public void setRolename(String rolename) { this.rolename = rolename; }
}
本例建立在上面的简化示例基础上,包括了所有的可选字段以及允许角色拥有组关系。

@Entity
public class User {

  
private Integer userId;
  
private String username;
  
private String passwordHash;
  
private Set<Role> roles;
  
private String firstname;
  
private String lastname;
  
private boolean enabled;  

  @Id @GeneratedValue
  
public Integer getUserId() { return userId; }
  
public void setUserId(Integer userId) { this.userId = userId; }

  @UserPrincipal
  
public String getUsername() { return username; }
  
public void setUsername(String username) { this.username = username; }  

  @UserPassword(hash 
= "md5")
  
public String getPasswordHash() { return passwordHash; }
  
public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }  

  @UserFirstName
  
public String getFirstname() { return firstname; }
  
public void setFirstname(String firstname) { this.firstname = firstname; }  

  @UserLastName
  
public String getLastname() { return lastname; }
  
public void setLastname(String lastname) { this.lastname = lastname; }  

  @UserEnabled
  
public boolean isEnabled() { return enabled; }
  
public void setEnabled(boolean enabled) { this.enabled = enabled; }  

  @UserRoles
  @ManyToMany(targetEntity 
= Role.class)
  @JoinTable(name 
= "UserRoles"
    joinColumns 
= @JoinColumn(name = "UserId"),
    inverseJoinColumns 
= @JoinColumn(name = "RoleId"))
  
public Set<Role> getRoles() { return roles; }
  
public void setRoles(Set<Role> roles) { this.roles = roles; }

}

@Entity
public class Role {
  
private Integer roleId;
  
private String rolename;
  
private boolean conditional;
  
  @Id @Generated
  
public Integer getRoleId() { return roleId; }
  
public void setRoleId(Integer roleId) { this.roleId = roleId; }
  
  @RoleName
  
public String getRolename() { return rolename; }
  
public void setRolename(String rolename) { this.rolename = rolename; }
  
  @RoleConditional
  
public boolean isConditional() { return conditional; }
  
public void setConditional(boolean conditional) { this.conditional = conditional; }
  
  @RoleGroups
  @ManyToMany(targetEntity 
= Role.class)
  @JoinTable(name 
= "RoleGroups"
    joinColumns 
= @JoinColumn(name = "RoleId"),
    inverseJoinColumns 
= @JoinColumn(name = "GroupId"))
  
public Set<Role> getGroups() { return groups; }
  
public void setGroups(Set<Role> groups) { this.groups = groups; }  
  
}

IdentityManager中使用JpaIdentityStore作为身份管理的实现时,调用某些IdentityManager方法会触发一些事件。

15.4.2.4.1. JpaIdentityStore.EVENT_PRE_PERSIST_USER

调用IdentityManager.createUser()时会相应触发该事件。在用户实体被持久化到数据库之前,该事件被触发并传递实体实例作为事件参数。这个实体是配置文件中JpaIdentityStore指定的user-class的实例。

为这个事件编写一个观察者对于在这个实体上设置在标准createUser()方法中不能设置的额外的字段值是很有用的。

调用IdentityManager.authenticate()时会相应触发该事件。它传递用户实体实例作为事件参数,这样可以帮助你从被验证的用户实体中读取其他额外的属性。

这个身份存储是专为存储在一个LDAP目录中的用户纪录来设计的。它具有高配置化,以及对存储在LDAP目录中用户和角色的设计具有极大的灵活性。以下的章节将描述这个身份存储的配置选项以及提供了一些配置示例。

下表描述了在components.xml中配置LdapIdentityStore可能用到的属性。

Table 15.3. LdapIdentityStore配置属性

属性

缺省值

描述

server-address

localhost

LDAP服务器地址。

server-port

389

LDAP服务器监听端口号。

user-context-DN

ou=Person,dc=acme,dc=com

包含用户纪录的上下文的标识名(DN)。

user-DN-prefix

uid=

This value is prefixed to the front of the username to locate the user's record.

user-DN-suffix

,ou=Person,dc=acme,dc=com

This value is appended to the end of the username to locate the user's record.

role-context-DN

ou=Role,dc=acme,dc=com

The DN of the context containing role records.

role-DN-prefix

cn=

This value is prefixed to the front of the role name to form the DN for locating the role record.

role-DN-suffix

,ou=Roles,dc=acme,dc=com

This value is appended to the role name to form the DN for locating the role record.

bind-DN

cn=Manager,dc=acme,dc=com

This is the context used to bind to the LDAP server.

bind-credentials

secret

These are the credentials (the password) used to bind to the LDAP server.

user-role-attribute

roles

This is the name of the attribute of the user record that contains the list of roles that the user is a member of.

role-attribute-is-DN

true

This boolean property indicates whether the role attribute of the user record is itself a distinguished name.

user-name-attribute

uid

Indicates which attribute of the user record contains the username.

user-password-attribute

userPassword

Indicates which attribute of the user record contains the user's password.

first-name-attribute

null

Indicates which attribute of the user record contains the user's first name.

last-name-attribute

sn

Indicates which attribute of the user record contains the user's last name.

full-name-attribute

cn

Indicates which attribute of the user record contains the user's full (common) name.

enabled-attribute

null

Indicates which attribute of the user record determines whether the user is enabled.

role-name-attribute

cn

Indicates which attribute of the role record contains the name of the role.

object-class-attribute

objectClass

Indicates which attribute determines the class of an object in the directory.

role-object-classes

organizationalRole

An array of the object classes that new role records should be created as.

user-object-classes

person,uidObject

An array of the object classes that new user records should be created as.


The following configuration example shows how LdapIdentityStore may be configured for an LDAP directory running on fictional host directory.mycompany.com. The users are stored within this directory under the context ou=Person,dc=mycompany,dc=com, and are identified using the uid attribute (which corresponds to their username). Roles are stored in their own context, ou=Roles,dc=mycompany,dc=com and referenced from the user's entry via the roles attribute. Role entries are identified by their common name (the cn attribute) , which corresponds to the role name. In this example, users may be disabled by setting the value of their enabled attribute to false.
<security:ldap-identity-store
    
server-address="directory.mycompany.com"
    bind-DN
="cn=Manager,dc=mycompany,dc=com"
    bind-credentials
="secret"
    user-DN-prefix
="uid="
    user-DN-suffix
=",ou=Person,dc=mycompany,dc=com"
    role-DN-prefix
="cn="
    role-DN-suffix
=",ou=Roles,dc=mycompany,dc=com"
    user-context-DN
="ou=Person,dc=mycompany,dc=com"
    role-context-DN
="ou=Roles,dc=mycompany,dc=com"
    user-role-attribute
="roles"
    role-name-attribute
="cn"
    user-object-classes
="person,uidObject"
    enabled-attribute
="enabled"
    
/>

IdentityManger可以通过以下的注入Seam组件的方式获得:
@In IdentityManager identityManager;
或者通过它的静态方法instance()获得:
IdentityManager identityManager = IdentityManager.instance();

下表描述了IdentityManager的方法:

方法

返回值

描述

createUser(String name, String password)

boolean

用指定的用户名和密码创建一个用户账户。如果成功返回true,反之,返回false

deleteUser(String name)

boolean

删除一个指定用户名的用户账户。如果成功返回true,反之,返回false

createRole(String role)

boolean

使用指定的名称创建一个角色。如果成功返回true,反之,返回false

deleteRole(String name)

boolean

删除一个指定名称的角色。如果成功返回true,反之,返回false

enableUser(String name)

boolean

启用一个指定名称的用户账户。不能验证的账户不能启用。如果成功返回true,反之,返回false

disableUser(String name)

boolean

禁用一个指定名称的用户账户。如果成功返回true,反之,返回false

changePassword(String name, String password)

boolean

修改指定用户名的账户的密码。如果成功返回true,反之,返回false

isUserEnabled(String name)

boolean

如果账户是启用的返回true,反之,返回false

grantRole(String name, String role)

boolean

为指定的用户或角色赋予指定的角色。角色必须是已经存在的。如果成功授权返回true,如果已经给用户授权过了,返回false

revokeRole(String name, String role)

boolean

撤销从指定的用户或角色的特定角色。如果指定用户是角色的成员并且成功撤销,返回true;如果用户不是角色的成员,返回false

userExists(String name)

boolean

如果存在指定的用户返回true,反之,返回false

listUsers()

List

按照字母序返回用户用户名的列表。

listUsers(String filter)

List

按照字母序返回使用指定的过滤参数过滤的用户用户名的列表。

listRoles()

List

返回所有角色名称的列表。

getGrantedRoles(String name)

List

返回一个所有角色的名字明确授予指定的用户名的列表。

getImpliedRoles(String name)

List

返回一个所有角色的名字隐式授予指定的用户名列表。隐式授予角色包括那些不直接授予一个用户,而他们给予该用户的角色是其成员。例如,管理员角色是用户的角色的成员,而用户是管理员角色的成员,那么该用户的暗示作用都是管理员和用户角色。

authenticate(String name, String password)

boolean

验证指定的用户名和密码存储配置的身份。如果成功返回true,如果身份验证失败返回false。该方法在成功的认证后除了返回值不改变什么。它不改变Identity组件的状态-实际上也不是一定要执行一个适当的Seam登录的Identity.login()

addRoleToGroup(String role, String group)

boolean

添加作为指定组的成员中指定的角色。如果操作成功返回true。

removeRoleFromGroup(String role, String group)

boolean

从指定的角色组中删除指定的角色。如果操作成功返回true。

listRoles()

List

列出所有关角色的名字。

使用这些身份管理API需要调用用户有合适的授权才能调用这些方法。下表描述了IdentityManager中每一个方法需要的权限。表中列出的权限目标是字符串值。

表 15.5. 身份管理安全权限

方法

权限目标

权限动作

createUser()

seam.user

create

deleteUser()

seam.user

delete

createRole()

seam.role

create

deleteRole()

seam.role

delete

enableUser()

seam.user

update

disableUser()

seam.user

update

changePassword()

seam.user

update

isUserEnabled()

seam.user

read

grantRole()

seam.user

update

revokeRole()

seam.user

update

userExists()

seam.user

read

listUsers()

seam.user

read

listRoles()

seam.role

read

addRoleToGroup()

seam.role

update

removeRoleFromGroup()

seam.role

update

下列代码提供了一个例子,演示了一套给admin觉得的成员授权访问所有身份管理相关的方法的安全规则:
rule ManageUsers
  no
-loop
  activation
-group "permissions"
when
  check: PermissionCheck(name 
== "seam.user", granted == false)
  Role(name 
== "admin")
then
  check.grant();
end

rule ManageRoles
  no
-loop
  activation
-group "permissions"
when
  check: PermissionCheck(name 
== "seam.role", granted == false)
  Role(name 
== "admin")
then
  check.grant();
end



原文地址:https://www.cnblogs.com/rgbw/p/1564675.html