首先acegi框架是什么,它是一个权限框架.很多人不知道,我一说spring-security很多人就知道。
acegi框架是spring-security的前身,后面随着版本演化更名为spring-security的。
为什么不直接读spring-security源码读acegi源码,因为框架随着时间推移,抽象程度更高,不容易体会思想,
一旦领会了思想,那么框架后面的更新只是更加抽象而已,本质上换汤不换药。我选取的是acegi-security-1.0.4版本进行研究的,
这个版本网上很难找到,CSDN可以找到,需要积分,如有需要,评论留下邮箱,我会发过去。
我们首先谈一个问题,不用框架怎么写一个权限控制框架,很多人说这很简单.基于RBAC原则设计权限 角色 用户等表以及关联表即可实现,表查询即可。是的,是很简单,但是写出来的代码是否高度封装,高度抽象,就不简单。比如说用户对象怎么定义,很多人不会基于面向接口的思维去开发的,直接一上来就写了一个user对象 里面放入username password等字段,我们来看看acegi框架是怎么处理的
首先框架先定义个UserDetails接口
/** * Provides core user information. * * <p> * Implementations are not used directly by Acegi Security for security * purposes. They simply store user information which is later encapsulated * into {@link Authentication} objects. This allows non-security related user * information (such as email addresses, telephone numbers etc) to be stored * in a convenient location. * </p> * * <p> * Concrete implementations must take particular care to ensure the non-null * contract detailed for each method is enforced. See * {@link org.acegisecurity.userdetails.User} for a * reference implementation (which you might like to extend). * </p> * * <p> * Concrete implementations should be immutable (value object semantics, * like a String). This is because the <code>UserDetails</code> will be * stored in caches and as such multiple threads may use the same instance. * </p> * * @author Ben Alex * @version $Id: UserDetails.java 1784 2007-02-24 21:00:24 +0000 (Sat, 24 Feb 2007) luke_t $ */ public interface UserDetails extends Serializable { //~ Methods ======================================================================================================== /** * Returns the authorities granted to the user. Cannot return <code>null</code>. * * @return the authorities (never <code>null</code>) */ GrantedAuthority[] getAuthorities(); /** * Returns the password used to authenticate the user. Cannot return <code>null</code>. * * @return the password (never <code>null</code>) */ String getPassword(); /** * Returns the username used to authenticate the user. Cannot return <code>null</code>. * * @return the username (never <code>null</code>) */ String getUsername(); /** * Indicates whether the user's account has expired. An expired account cannot be authenticated. * * @return <code>true</code> if the user's account is valid (ie non-expired), <code>false</code> if no longer valid * (ie expired) */ boolean isAccountNonExpired(); /** * Indicates whether the user is locked or unlocked. A locked user cannot be authenticated. * * @return <code>true</code> if the user is not locked, <code>false</code> otherwise */ boolean isAccountNonLocked(); /** * Indicates whether the user's credentials (password) has expired. Expired credentials prevent * authentication. * * @return <code>true</code> if the user's credentials are valid (ie non-expired), <code>false</code> if no longer * valid (ie expired) */ boolean isCredentialsNonExpired(); /** * Indicates whether the user is enabled or disabled. A disabled user cannot be authenticated. * * @return <code>true</code> if the user is enabled, <code>false</code> otherwise */ boolean isEnabled(); }
看着很长很累是吧 但是一个好的接口的重要性不言而喻,接口更像是一个规范,比如说你实现了其中的isAccountNonLocked接口
那么返回true就表示账号没有被锁定 否则false就代表可以锁定。我在接口上已经明确了定义,那么你的实现就要乖乖遵守。不能不同实现不同语义。而且这个接口很多情况都考虑到了,什么账号过期 凭证失效。因此好的框架肯定适应于大部分的业务不会只应付简单情况,即使出现了特殊情况 比如你会说 这里没有我想要的cid字段(备注 cid做推送的时候用于标注每个手机的设备号),很简单,实现这个接口再加入你的特殊字段即可。说白了就是要有一种面向接口开发的思想,不要一上来就定义一个实体类。
仔细看user类的实现 很有意思 因此面向接口思维开发后 需要写很多代码 但是这些代码具有很强的生命力了 需要好好体会
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.acegisecurity.userdetails; import org.acegisecurity.GrantedAuthority; import org.springframework.util.Assert; /** * Models core user information retieved by an {@link UserDetailsService}.<p>Implemented with value object * semantics (immutable after construction, like a <code>String</code>). Developers may use this class directly, * subclass it, or write their own {@link UserDetails} implementation from scratch.</p> * * @author Ben Alex * @version $Id: User.java 1784 2007-02-24 21:00:24 +0000 (Sat, 24 Feb 2007) luke_t $ */ public class User implements UserDetails { //~ Instance fields ================================================================================================ private static final long serialVersionUID = 1L; private String password; private String username; private GrantedAuthority[] authorities; private boolean accountNonExpired; private boolean accountNonLocked; private boolean credentialsNonExpired; private boolean enabled; //~ Constructors =================================================================================================== /** * Construct the <code>User</code> with the details required by * {@link org.acegisecurity.providers.dao.DaoAuthenticationProvider}. * * @param username the username presented to the * <code>DaoAuthenticationProvider</code> * @param password the password that should be presented to the * <code>DaoAuthenticationProvider</code> * @param enabled set to <code>true</code> if the user is enabled * @param authorities the authorities that should be granted to the caller * if they presented the correct username and password and the user * is enabled * * @throws IllegalArgumentException if a <code>null</code> value was passed * either as a parameter or as an element in the * <code>GrantedAuthority[]</code> array * * @deprecated use new constructor with extended properties (this * constructor will be removed from release 1.0.0) */ public User(String username, String password, boolean enabled, GrantedAuthority[] authorities) throws IllegalArgumentException { this(username, password, enabled, true, true, authorities); } /** * Construct the <code>User</code> with the details required by * {@link org.acegisecurity.providers.dao.DaoAuthenticationProvider}. * * @param username the username presented to the * <code>DaoAuthenticationProvider</code> * @param password the password that should be presented to the * <code>DaoAuthenticationProvider</code> * @param enabled set to <code>true</code> if the user is enabled * @param accountNonExpired set to <code>true</code> if the account has not * expired * @param credentialsNonExpired set to <code>true</code> if the credentials * have not expired * @param authorities the authorities that should be granted to the caller * if they presented the correct username and password and the user * is enabled * * @throws IllegalArgumentException if a <code>null</code> value was passed * either as a parameter or as an element in the * <code>GrantedAuthority[]</code> array * * @deprecated use new constructor with extended properties (this * constructor will be removed from release 1.0.0) */ public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, GrantedAuthority[] authorities) throws IllegalArgumentException { this(username, password, enabled, accountNonExpired, credentialsNonExpired, true, authorities); } /** * Construct the <code>User</code> with the details required by * {@link org.acegisecurity.providers.dao.DaoAuthenticationProvider}. * * @param username the username presented to the * <code>DaoAuthenticationProvider</code> * @param password the password that should be presented to the * <code>DaoAuthenticationProvider</code> * @param enabled set to <code>true</code> if the user is enabled * @param accountNonExpired set to <code>true</code> if the account has not * expired * @param credentialsNonExpired set to <code>true</code> if the credentials * have not expired * @param accountNonLocked set to <code>true</code> if the account is not * locked * @param authorities the authorities that should be granted to the caller * if they presented the correct username and password and the user * is enabled * * @throws IllegalArgumentException if a <code>null</code> value was passed * either as a parameter or as an element in the * <code>GrantedAuthority[]</code> array */ public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, GrantedAuthority[] authorities) throws IllegalArgumentException { if (((username == null) || "".equals(username)) || (password == null)) { throw new IllegalArgumentException("Cannot pass null or empty values to constructor"); } this.username = username; this.password = password; this.enabled = enabled; this.accountNonExpired = accountNonExpired; this.credentialsNonExpired = credentialsNonExpired; this.accountNonLocked = accountNonLocked; setAuthorities(authorities); } //~ Methods ======================================================================================================== public boolean equals(Object rhs) { if (!(rhs instanceof User) || (rhs == null)) { return false; } User user = (User) rhs; // We rely on constructor to guarantee any User has non-null and >0 // authorities if (user.getAuthorities().length != this.getAuthorities().length) { return false; } for (int i = 0; i < this.getAuthorities().length; i++) { if (!this.getAuthorities()[i].equals(user.getAuthorities()[i])) { return false; } } // We rely on constructor to guarantee non-null username and password return (this.getPassword().equals(user.getPassword()) && this.getUsername().equals(user.getUsername()) && (this.isAccountNonExpired() == user.isAccountNonExpired()) && (this.isAccountNonLocked() == user.isAccountNonLocked()) && (this.isCredentialsNonExpired() == user.isCredentialsNonExpired()) && (this.isEnabled() == user.isEnabled())); } public GrantedAuthority[] getAuthorities() { return authorities; } public String getPassword() { return password; } public String getUsername() { return username; } public int hashCode() { int code = 9792; if (this.getAuthorities() != null) { for (int i = 0; i < this.getAuthorities().length; i++) { code = code * (this.getAuthorities()[i].hashCode() % 7); } } if (this.getPassword() != null) { code = code * (this.getPassword().hashCode() % 7); } if (this.getUsername() != null) { code = code * (this.getUsername().hashCode() % 7); } if (this.isAccountNonExpired()) { code = code * -2; } if (this.isAccountNonLocked()) { code = code * -3; } if (this.isCredentialsNonExpired()) { code = code * -5; } if (this.isEnabled()) { code = code * -7; } return code; } public boolean isAccountNonExpired() { return accountNonExpired; } public boolean isAccountNonLocked() { return this.accountNonLocked; } public boolean isCredentialsNonExpired() { return credentialsNonExpired; } public boolean isEnabled() { return enabled; } protected void setAuthorities(GrantedAuthority[] authorities) { Assert.notNull(authorities, "Cannot pass a null GrantedAuthority array"); for (int i = 0; i < authorities.length; i++) { Assert.notNull(authorities[i], "Granted authority element " + i + " is null - GrantedAuthority[] cannot contain any null elements"); } this.authorities = authorities; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append(super.toString()).append(": "); sb.append("Username: ").append(this.username).append("; "); sb.append("Password: [PROTECTED]; "); sb.append("Enabled: ").append(this.enabled).append("; "); sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; "); sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired).append("; "); sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; "); if (this.getAuthorities() != null) { sb.append("Granted Authorities: "); for (int i = 0; i < this.getAuthorities().length; i++) { if (i > 0) { sb.append(", "); } sb.append(this.getAuthorities()[i].toString()); } } else { sb.append("Not granted any authorities"); } return sb.toString(); } }