1:认证
认证的认证方法定义在一个接口中:AuthenticationProvider 这个接口定义了认证方法:Authentication authenticate(Authentication authentication),要求认证传入参数类型是:Authentication类型,
Authentication也是一个接口,该接口定义了认证相关的信息,如下:
GrantedAuthority[] getAuthorities();
权限主体拥有的权限,用户认证后权限实体将拥有一个保存了一系列GrantedAuthority对象的数组,之后可以用于进行验证用户是否可以访问系统中被保护资源。GrantedAuthority接口内只有一个方法getAuthority(),它的返回值是一个字符串,这个字符串用来标识系统中的某一权限。(这个字符串通常是ROLE_ADMIN,或者ROLE_USER),因为:类GrantedAuthorityImpl实现接口GrantedAuthority,并实现方法String getAuthority(),
该实现类里面返回的权限是字符串Role_ADMIN等其他
Object getCredentials();
获得权限主体的凭证,此凭证应该可以唯一标示权限主体。默认情况下,凭证是用户的登录密码
Object getDetails();
获得验证请求有关的附加信息。默认情况下,附加信息是WebAuthenticationDetails的一个实例,其中保存了客户端ip和sessionid
Object getPrincipal();
获得权限主体。默认情况下,权限主体是一个实现了UserDetails接口的对象。
其中UserDetails接口定义如下:
GrantedAuthority[] getAuthorities();
返回授予该user的权限
String getPassword();
返回被用来认证该用户的密码
String getUsername();
返回被用来认证该用户的用户名
boolean isAccountNonExpired();
指出用户的账号是否过期,过期的账号不能被认证,识别
boolean isAccountNonLocked();
指出用户是否被锁定,被锁定的用户不能通过认证。
boolean isCredentialsNonExpired();
指出用户的密码是否过期,也就是说判断该用户的密码是否正确。无效的密码将被组织通过认证。
boolean isEnabled();
指出用户是否被禁用,禁用的用户不能通过认证。
上面谈到的是认证的接口类AuthenticationProvider,而这个接口类又有不同的实现,所以在spring security中很多中认证机制:
一:匿名用户认证:
class AnonymousAuthenticationProvider,该类提供了匿名用户登陆的认证方式,
class RememberMeAuthenticationProvider,该类提供了自动登陆认证方式,
class RemoteAuthenticationProvider,该类提供了远程认证方式
class DaoAuthenticationProvider,该类实现form表单认证方式,
class LdapAuthenticationProvider ,该类实现了Ldap认证方式,
等等,还有其他认证不是很常用,我们这里主要的认证方式就后面这俩个认证方式。
我们这里主要谈谈Ldap认证即LdapAuthenticationProvider:
LdapAuthenticationProvider是AuthenticationProvider的ldap认证方式的实现。
而在LdapAuthenticationProvider内部有是使用了不同的认证方式的,它有两个实现,分别对应于LDAP的两种认证方式:bind(BindAuthenticator)和password compare(PasswordComparisonAuthenticator)。Spring Security缺省的schema配置使用的是bind认证。如果使用password compare方式认证,需要指定如何获取口令。
LDAP的俩种认证方式定义在一个接口中,这个接口是:
interface LdapAuthenticator,它只提供了一个认证方法:
DirContextOperations authenticate(Authentication authentication);
不用多说,一看就明白。
Spring Security在认证成功后,将票据存放在SecurityContext中。SecurityContext是一个接口,
这里又出现了新的SecurityContext安全上下文:
为使所有的组件都可以通过同一方式访问当前的权限实体,Spring Security特别提供了SecurityContext作为安全上下文,
可以直接通过SecurityContextHolder获得当前线程中的SecurityContext。
SecurityContext securityContext = SecurityContextHolder.getContext();
默认情况下,SecurityContext的实现基于ThreadLocal,系统会在每次用户请求时将SecurityContext与当前Thread进行绑定,也可以调用SecurityContextHolder的setStrategyName()方法来修改系统使用的策略。
SecurityContextHolder.setStrategyName("MODE_GLOBAL");
如果用户尚未通过认证,那么SecurityContext.getAuthenticaiton()方法就会返回null。可以使用Authentication接口中定义的几个方法,获得当前权限实体的信息
这个前面已经说过,不在重复。
从接口的方法可以看出,用户可以通过SecurityContext存放和读取票据信息(Authentication)。
SecurityContext又存放在SecurityContextHolder。SecurityContextHolder定义了SecurityContext
的相关操作,如初始化,清空,读取等。SecurityContextHolder并没有直接实现这些操作,而是使用了策略模式,
由一个SecurityContextHolderStrategy接口,来完成真正的逻辑。
Spring Security提供了3个实现:
GlobalSecurityContextHolderStrategy,在这种策略下,JVM所有的对象共享一个SecurityContext对象。
一般在胖客户端使用,一个客户端就是一个用户代理。
ThreadLocalSecurityContextHolderStrategy,这种策略下,每个线程关联一个SecurityContext。每次都从当前线程读取或存放SecurityContext。一般用于为多个用户服务的服务器,每个用户一个线程。是最常用的模式,也是Spring Security缺省的策略。用户注销时,应该把该用户所在线程的SecurityContext清除掉。
InheritableThreadLocalSecurityContextHolderStrategy,这种策略是InheritableThreadLocal版本的实现。
有两种方法可以设置策略:1)用要使用策略的类名作为系统属性spring.security.strategy的值。2)直接
调用SecurityContextHolder的静态方法setStrategyName。
认证完成通过LdapAuthoritiesPopulator来获取用户权限,其中LdapAuthoritiesPopulator也是一个接口,定义如下:
interface LdapAuthoritiesPopulator,该接口定义了唯一的方法
GrantedAuthority[] getGrantedAuthorities(DirContextOperations userData, String username);
它有三个实现。
其中一个是一个空实现,不返回任何权限信息。另外两个实现对应的授权信息的两个来源。如果授权信息从LDAP中获取,则使用DefaultLdapAuthoritiesPopulator,如果用户权限从外部的UserDetailsService中获取,则使用UserDetailsServiceLdapAuthoritiesPopulator。
其中DefaultLdapAuthoritiesPopulator从LDAP中获取的权限前面会添加上前缀,缺省值为"ROLE_"。
谈到这里不得不提一下,为什么权限前面会添加前缀,而其缺省值为:ROLE_,实际上这与Spring Security中的Voter机制有着千丝缕的联系
Voter表决者(投票器的概念)
AccessDecisionManager维护着一个AccessDecisionVoter列表参与授权的投票。根据处理投票的策略不同
interface AccessDecisionVoter定义了方法:
int vote(Authentication authentication, Object object, ConfigAttributeDefinition config);
Spring Security中AccessDecisionManager有3个不同的实现。如下:
UnanimousBased.java 只要有一个Voter不能完全通过权限要求,就禁止访问。
AffirmativeBased.java只要有一个Voter可以通过权限要求,就可以访问。
ConsensusBased.java只要通过的Voter比禁止的Voter数目多就可以访问了。
AccessDecisionVoter的vote方法,实现了投票的逻辑。vote方法需要的参数AccessDecisionManager的decide方法
完全相同。实际上AccessDecisionManager的功能就是委托给AccessDecisionVoter实现的。Spring Security中有很多AccessDecisionVoter的实现,最简单的有RoleVoter。RoleVoter通过将Authentication里面获取的权限信息,与从
ConfigAttributeDefinition配置的访问资源需要的权限对比,来投票通过,或拒绝,或弃权。
RoleVoter默认角色名称都是以ROLE_开头
稍微注意一下,默认角色名称都要以ROLE_开头,否则不会被计入权限控制,如果需要修改,就在xml里配个什么前缀的。可以用过配置roleVoter的rolePrefix来改变这个前缀。
如果读源代码的化,会发现:这三个类都实现了同一个接口:
interface AccessDecisionManager,这个接口又是干什么的呢?
AccessDecisionManager完成授权的功能。观察AccessDecisionManager接口的授权方法:
void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
对一次访问授权,需要传入三个信息。
(1)认证过的票据Authentication,确定了谁正在访问资源。
(2)被访问的资源object。
(3)访问资源要求的权限配置ConfigAttributeDefinition。
从票据中可以获取认证用户所拥有的权限,再对比访问资源要求的权限,即可断定当前认证用户是否能够访问该资源。
返回认证:
认证的过程涉及到认证方法,认证需要提供的凭证信息,和认证的后的票据
AuthenticationManager是认证的入口,这个接口只有一个方法
Authentication authenticate(Authentication authentication)。
凭证信息:
凭证信息包括用户名,密码,证书或其他信息。观察AuthenticationManager接口,authenticate方法接收一个Authentication类型的参数,
Authentication有一个方法getCredentials(),返回Object,这就是凭证信息。Authentication对象是存放凭证信息的地方。
Spring Security没有规定认证方法,认证启动后会调用AuthenticationManager.authenticate方法,实现认证。Spring Security提供了一个缺省的实现ProviderManager,管理一个AuthenticationProvider的列表。真正认证的功能代理给AuthenticationProvider完成(文章头部),每个AuthenticationProvider提供了真正的认证逻辑。缺省的实现中只要有一个AuthenticationProvider证成功,ProviderManager就认为认证成功,并返回票据。Spring Security提供了一些默认的AuthenticationProvider的实现,如DaoAuthenticationProvider,LdapAuthenticationProvider等,分别对应不同的认证方法。
票据是一个用户身份标识,系统根据用户的票据为用户授权。AuthenticationManager的authenticate返回一个Authentication作为票据,包含了认证过的Principal和权限(GrantedAuthority)信息。实际上Authentication自身就是一个Principal(实现了java.security.Principal接口)。
在认证的时候,spring security提供了认证加密策略
如果使用认证加密,需要在配置文件里做如下配置:
加密工具类interface PasswordEncoder,该接口定义了方法加密和解密
String encodePassword(String rawPass, Object salt)
boolean isPasswordValid(String encPass, String rawPass, Object salt)
该接口的实现类很多,不同的实现类实现不同的加密手段:我们常用的是MD5加密
PasswordEncoder和SaltValue
默认提供的PasswordEncoder包含plaintext, sha, sha-256, md5, md4, {sha}, {ssha}。
其中{sha}和{ssha}是专门为ldap准备的,plaintext意味着不对密码进行加密,
如果我们不设置PasswordEncoder,默认就会使用它。
SaltValue是为了让密码加密更加安全,它有两种策略可以选择。
user-property, system-wide分别对应着ReflectionSaltSource和SystemWideSaltSource,
它们的区别是ReflectionSaltSource会使用反射,从用户的Principal对象汇总取出一个对应的属性来作为盐值,
而SystemWideSaltSource会为所有用户都设置相同的盐值。
使用了PasswordEncoder和SaltValue的结果就是数据库中的密码变得难以辨认了,
这就要注意在添加用户时要使用相同的策略对密码进行加密,这才能保证新用户可以正常登陆。
Spring security还提供了缓冲机制,用于用户信息缓存,有关缓冲的定义在接口
UserCache中,类EhCacheBasedUserCache实现了缓冲接口中的方法,具体实现是依赖与spring的缓冲jar包
用户信息缓存
Spring security在获取用户权限的时候,可以通过不同的方式,获取,我们上面介绍的是通过Ldap服务器验证,还有一种是通过内存验证,只需在配置文件加入如下代码即可:
患有很多中认证方式,通常普遍的是JDBC数据库验证,
到这里该提下这个接口UserDetailsService,这个接口用与获取用户的权限信息或者说主体信息,该借口定义了唯一的方法:
UserDetails loadUserByUsername(String username)
接口UserDetailsManager继承了接口UserDetailsService,并新增添了一些方法:
void createUser(UserDetails user);
void updateUser(UserDetails user);
void deleteUser(String username);
boolean userExists(String username);
等等,于此相对应的也有一个接口GroupManager,用与对组的CRUD操作,
String[] findAllGroups();
String[] findUsersInGroup(String groupName);
void createGroup(String groupName, GrantedAuthority[] authorities);
void deleteGroup(String groupName);
等等,spring security把用户和组的概念结合起来了,有组的概念,权限组?
JdbcUserDetailsManager 实现接口UserDetailsManager,GroupManager,继承类JdbcDaoImpl
JdbcDaoImpl类继承类JdbcDaoSupport实现接口UserDetailsSdervice
抽象类JdbcDaoSupport继承DaoSupport
JdbcDaoSupplor 中成员变量JdbcTemplate实现连接数据库
所以如果我们自己程序里用spring security,
可以使用JdbcTemplate,UserDetailsManager,但需要实现他的一个接口
interface UserDetailsService
说的在详细点:
UserDetailsManager接口定义了User的增删改查方法,并继承了接口UserDetailsService接口,
JdbcUserDetailsManager类继承了JdbcDaoImpl,实现了接口UserDetailsManager,GroupManager
GroupManager接口定义了组的增删改查和用户与组之间的关系
JdbcUserDetailsManager实现了User的增删改查方法和组的增删改查,通过调用getJdbcTemplate完成的,而getJdbcTemplate是JdbcTemplate类,该类实现了数据库的底层,删除用户的时候会清理缓冲,缓冲类的接口是UserCache,该接口定义了从缓冲中获取用户,把用户存入缓冲,及从缓冲中删除用户等方法
EhCacheBasedUserCache类实现了UserCache接口,并实现了UserCache接口定义的方法
总结一下:
pring Security中的UserDetails被作为一个通用的权限主体,凡是涉及到username和password的情况,
都会使用到UserDetails和它对应的服务。
常用的服务有从内存中读取用户信息的InMemoryDaoImpl和用数据库中读取用户信息的JdbcDaoImp。
它们都实现了UserDetailsService,因此都可以使用loadUserByUsername()方法获得对应用户的信息。
InMemoryDaoImpl中读取权限主体是UserMap,而UserMap是一个Map类型,key放的是username的大写,value放的UserDetail
到底是为什么,?到底是怎么工作的?不得不提到spring security的核心
HttpSessionContextIntegrationFilter
位于过滤器顶端,第一个起作用的过滤器。
用途一,在执行其他过滤器之前,率先判断用户的session中是否已经存在一个SecurityContext了。如果存在,就把SecurityContext拿出来,放到SecurityContextHolder中,供Spring Security的其他部分使用。如果不存在,就创建一个SecurityContext出来,还是放到SecurityContextHolder中,供Spring Security的其他部分使用。
用途二,在所有过滤器执行完毕后,清空SecurityContextHolder,因为SecurityContextHolder是基于ThreadLocal的,如果在操作完成后清空ThreadLocal,会受到服务器的线程池机制的影响。
LogoutFilter
只处理注销请求,默认为/j_spring_security_logout。
用途是在用户发送注销请求时,销毁用户session,清空SecurityContextHolder,
然后重定向到注销成功页面。可以与rememberMe之类的机制结合,在注销的同时清空用户cookie。
AuthenticationProcessingFilter
处理form登陆的过滤器,与form登陆有关的所有操作都是在此进行的。
默认情况下只处理/j_spring_security_check请求,这个请求应该是用户使用form登陆后的提交地址
此过滤器执行的基本操作时,通过用户名和密码判断用户是否有效,如果登录成功就跳转到成功页面
(可能是登陆之前访问的受保护页面,也可能是默认的成功页面),如果登录失败,就跳转到失败页面。
DefaultLoginPageGeneratingFilter
此过滤器用来生成一个默认的登录页面,默认的访问地址为/spring_security_login,
这个默认的登录页面虽然支持用户输入用户名,密码,也支持rememberMe功能,但是因为太难看了,
只能是在演示时做个样子,不可能直接用在实际项目中。
BasicProcessingFilter
此过滤器用于进行basic验证,功能与AuthenticationProcessingFilter类似,只是验证的方式不同。
SecurityContextHolderAwareRequestFilter
此过滤器用来包装客户的请求。目的是在原始请求的基础上,为后续程序提供一些额外的数据。比如getRemoteUser()时直接返回当前登陆的用户名之类的
RememberMeProcessingFilter
此过滤器实现RememberMe功能,当用户cookie中存在rememberMe的标记,此过滤器会根据标记自动实现用户登陆,并创建SecurityContext,授予对应的权限。
AnonymousProcessingFilter
为了保证操作统一性,当用户没有登陆时,默认为用户分配匿名用户的权限。
ExceptionTranslationFilter
此过滤器的作用是处理中FilterSecurityInterceptor抛出的异常,然后将请求重定向到对应页面,或返回对应的响应错误代码
SessionFixationProtectionFilter
防御会话伪造攻击。有关防御会话伪造的详细信息
FilterSecurityInterceptor
用户的权限控制都包含在这个过滤器中。
功能一:如果用户尚未登陆,则抛出AuthenticationCredentialsNotFoundException“尚未认证异常”。
功能二:如果用户已登录,但是没有访问当前资源的权限,则抛出AccessDeniedException“拒绝访问异常”。
功能三:如果用户已登录,也具有访问当前资源的权限,则放行。
所有的过滤器都会实现SpringSecurityFilter,安全过滤器
在谈谈spring security的标签库
为了方便使用者对权限的访问和控制,非常简单,只有几个而已
所以就直接列举出来了,,
authentication的功能是从SecurityContext中获得一些权限相关的信息。
获得当前用户所有的权限,把权限列表放到authorities变量里,然后循环输出权限信息:
${authority.authority}
authorize用来判断当前用户的权限,然后根据指定的条件判断是否显示内部的内容。
admin and user
admin or user
not admin
acl/accesscontrollist
用于判断当前用户是否拥有指定的acl权限。
|
记录操作日志
官方提供了LoggerListener用来向控制台输出日志。如果我们希望在数据库中保存用户访问日志,
可以实现自己的AuditListener,实现ApplicationListener接口,
然后Spring Security就会自动以事件的形式发送审计日志了。
Spring Security使用AOP对方法调用进行权限控制,这部分内容基本都是来自于Spring提供的AOP功能,Spring Security进行了自己的封装,
我们可以使用声明和编程两种方式进行权限管理。
因为Spring Security对方法调用和ACL权限控制的实现都是基于Spring的AOP,
所以只能保护定义在applicationContext.xml中的Java类,对直接new出来的对象是无法保护的。
我们自己要保护的java类必须实现MessageService接口
public class MessageServiceImpl implements MessageService
xml配置文件
access="ROLE_ADMIN"/>
权限管理的基本概念
Spring Security作为权限管理框架,其内部机制可分为两大部分,其一是认证授权auhorization,
其二是权限校验authentication。
认证授权authorization是指,根据用户提供的身份凭证,生成权限实体,并为之授予相应的权限。
权限校验authentication是指,用户请求访问被保护资源时,将被保护资源所需的权限和用户权限实体所拥护的权限二者进行比对,
如果校验通过则用户可以访问被保护资源,否则拒绝访问。
我们之前讲过的form-login,http-basic, digest都属于认证授权authorization部分的概念,用户可以通过这些机制登录到系统中,
系统会为用户生成权限主体,并授予相应的权限。
与之相对的,FilterSecurityInterceptor,Method保护,taglib,@Secured都属于权限校验authentication,无论是对URL的请求,
对方法的调用,页面信息的显示,都要求用户拥有相应的权限才能访问,否则请求即被拒绝。
保存登录之前的请求
Spring Security提供了一种称作SavedRequest功能,可以在未登录用户访问资源时,将用户请求保存起来,
当用户登录成功之后SecurityContextHolderAwareRequestFilter会使用之前保存的请求,
结合当前用户的请求生成一个新的请求对象,而这个请求对象中就保存了用户登录之前提交的信息。
SavedRequest功能默认就会被Spring Security启用,不需任何配置就可以重用登陆之前请求提交的数据。
如果希望禁用SavedRequet这一功能,只需要在http标签中设置servlet-api-provision参数。
详细:
public class SecurityContextHolderAwareRequestFilter extends SpringSecurityFilter
匹配URL地址
Spring security中定义了一个接口用户URL的匹配:
interface UrlMatcher
我们默认使用的URL匹配器就是这个AntUrlPathMatcher
class AntUrlPathMatcher implements UrlMatcher
它为我们提供了三种通配符。
通配符:?
示例:/admin/g?t.jsp
匹配任意一个字符,/admin/g?t.jsp可以匹配/admin/get.jsp和/admin/got.jsp或是/admin/gxt.do。不能匹配/admin/xxx.jsp。
通配符:*
示例:/admin/*.jsp
匹配任意多个字符,但不能跨越目录。/*/index.jsp可以匹配/admin/index.jsp和/user/index.jsp,但是不能匹配/index.jsp和/user/test/index.jsp。
通配符:**
示例:/**/index.jsp
可以匹配任意多个字符,可以跨越目录,可以匹配/index.jsp,/admin/index.jsp,/user/admin/index.jsp和/a/b/c/d/index.jsp
另外还有一个实现类,也实现了UrlMatcher,功能更强大
class RegexUrlPathMatcher implements UrlMatcher
它支持使用正则表达式对URL地址进行匹配。
如果希望使用RegexUrlPathMatcher,就需要在配置文件中添加如下配置:
lowercase-comparisons
与URL匹配相关的配置还有一个lowercase-comparisons,
它的功能是在进行URL匹配前是否需要自动将URL中的字符都转换成小写。
如果我们没有设置lowercase-comparisons的值
,那么在默认情况下AntUrlPathMatcher会在每次匹配URL之前都将URL中的字符转换为小写,
而RegexUrlPathMatcher默认不会将URL中的字符转换为小写。
也就是说默认情况下AntUrlPathMatcher的默认值是lowercase-comparisons="true",
而RegexUrlPathMatcher的默认值是lowercase-comparisons="false"。
如果需要修改lowercase-comparisons参数的值,可以像下面这样修改配置。
各个类详细:
Form验证登陆详细:
AuthenticationProcessingFilter类中调用了attemptAuthentication方法,
1,获取用户名
2,获取密码,判断用户名和密码是否为空,如果为空,则都为"",
3,用户名去空格username.trim();
4,执行UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,password);
将创建好的authRequest放入SecurityContext安全上下文中
进入类UsernamePasswordAuthenticationToken,该类继承抽象类AbstractAuthenticationToken
进入抽象类AbstractAuthenticationToken,该类实现接口Authentication
执行AbstractAuthenticationToken类的构造函数,传入的对象是(GrantedAuthority[] ,,)
将authorities赋与AbstractAuthenticationToken的成员变量GrantedAuthority[]数组,此时赋予的authorities值为null,然后返回,继续执行
类,
将用户名和密码分别赋予UsernamePasswordAuthenticationToken的成员变量credentials和principal,然后执行setAuthenticated(boolean )方法,传入的值是true
然后执行父类AbstractAuthenticationToken的setAuthenticated方法,
将传入的boolean值赋到成员变量authenticated中,此时的值为false,执行完成,
返回到UsernamePasswordAuthenticationToken类中,执行完成,返回到
AuthenticationProcessingFilter类中,
5,执行 request.getSession(false)如果当前不存在session,就返回null,
6,判断当前session是否为null,此时不为null,
7,执行session.setAttribute(),key值为常量SPRING_SECURITY_LAST_USERNAME,
value值为,,,,
8,执行UsernamePasswordAuthenticationToken对象的setDetails()方法,
传入的值为authenticationDetailsSource.buildDetails(request)
其中authenticationDetailsSource = new WebAuthenticationDetail
进入WebAuthenticationDetailsSource,因为WebAuthenticationDetailsSource
实现了接口AuthenticationDetailsSource
执行WebAuthenticationDetailsSource方法buildDetails,
在这个方法里创建了constructor对象
9,AuthenticationProcessingFilter类的方法执行完成,
返回Authentication对象,
进入类AbstractProcessingFilter
AbstractProcessingFilter的方法doFilterHttp()中,执行
Authentication authResult = attemptAuthentication(request);
进入类ProviderManager,执行doAuthentication方法,
执行过成为AbstractUserDetailsAuthenticationProvider中的authenticate方法中会调用retrieveUser方法,
然后进入DaoAuthenticationProvider执行retrieveUser方法
在这个方法执行权限认证
ACL认证
我们做ACL认证接触的接口:
interface ObjectIdentity
interface MutableAclService
MutableAclService 接口定义了有关ACL的创建,删除,和更新方法
MutableAclService 本身又继承了AclService 接口,该接口定义了如下:
ObjectIdentity[] findChildren(ObjectIdentity parentIdentity);
Acl readAclById(ObjectIdentity object) throws NotFoundException;
Acl readAclById(ObjectIdentity object, Sid[] sids)
Map readAclsById(ObjectIdentity[] objects)
Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
类class JdbcAclService implements AclService,实现了方法
接口LookupStrategy (查找策略)定义唯一方法
Map readAclsById(ObjectIdentity[] objects, Sid[] sids);
final类BasicLookupStrategy实现了LookupStrategy接口
LookupStrategy的作用就是从数据库中读取信息,把这些信息提供给aclService使用,它在加载数据时候还使用了缓冲,即aclCalhe,和前面的缓冲实现是一样的,
最终我们通过aclService管理ACL
类JdbcMutableAclService继承类JdbcAclService,实现接口MutableAclService
类JdbcAclService实现接口AclService
接口MutableAclService继承接口AclService
Acl是一个接口
定义的方法如下:
AccessControlEntry[] getEntries();
ObjectIdentity getObjectIdentity();
Sid getOwner();
Acl getParentAcl();
boolean isEntriesInheriting();
boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
boolean isSidLoaded(Sid[] sids);
其中AuditableAcl,MutableAcl,OwnershipAcl三个接口分别都继承了接口Acl
而这些接口都由类AclImpl唯一实现
接口ObjectIdentity方法定义如下:
boolean equals(Object obj);
Serializable getIdentifier();
Class getJavaType();
int hashCode();
接口ObjectIdentity别类ObjectIdentityImpl唯一实现
ObjectIdentity的作用是通过类名和ID生成唯一标识。
最终我们通过这个唯一标识创建一个ACL,如下:
ObjectIdentity oid = new ObjectIdentityImpl(Account.class,account.getId());
MutableAcl acl = mutableAclService.createAcl(oid);
使用class和id可以唯一标识一个对象。
接口Permission定义的方法如下:
int getMask();
String getPattern();
Permission接口被AbstractPermission抽象类,BasePermission类,CumulativePermission类实现
其中类BasePermission继承了类抽象类AbstractPermission
抽象类AbstractPermission实现了接口Permission
类DefaultPermissionFactory实现了接口PermissionFactory
接口PermissionFactory定义了唯一的方法:
public abstract Permission buildFromMask(int mask);
接口AclAuthorizationStrategy(ACL授权策略)定义了唯一的方法
void securityCheck(Acl acl, int changeType);
int CHANGE_OWNERSHIP = 0;
int CHANGE_AUDITING = 1;
int CHANGE_GENERAL = 2;
类AclAuthorizationStrategyImpl实现接口AclAuthorizationStrategy,
在这个类中实现了Acl的授权,核心类
接口GrantedAuthority定义了唯一的方法
String getAuthority();
该方法返回当前用户拥有的权限
类GrantedAuthorityImpl实现接口GrantedAuthority,并实现方法String getAuthority(),
该实现类里面返回的权限是字符串Role_ADMIN等其他
类AclEntryAfterInvocationCollectionFilteringProvider继承抽象类AbstractAclProvider,
抽象类AbstractAclProvider实现了接口AfterInvocationProvider
类PrincipalSid实现了接口Sid,接口Sid定义的方法:
boolean equals(Object obj);
int hashCode();
ACL数据库的四张表
ACL_SID:让我们定义系统中唯一主体或授权(“SID”意思是“安全标识”)。它包含的列有ID,一个文本类型的SID,一个标识,用来表示是否使用文本显示引用的主体名或一个GrantedAuthority,因此,对每个唯一的主体或GrantedAuthority都有单独一行,在使用获得授权的环境下,一个SID通常叫做“recipient”授予者。
ACL_CLASS:让我们在系统中确定唯一的领域对象类,包含的列有ID和java类名,因此,对每个我们希望保存ACL权限的类都有单独一行。
ACL_OBJECT_IDENTITY:为系统中每个唯一的领域对象实例保存信息。列包括ID,指向ACL_CLASS的外键,唯一标识,所以我们知道为哪一个ACL_CLASS实例提供信息,parent,一个外键指向ACL_SID表,展示领域对象实例的拥有者,我们是否允许ACL条目从任何父亲ACL继承,我们对每个领域对象实例有一个单独的行,来保存ACL权限。
ACL_ENTRY:保存分配给每个授予者单独的权限,列包括一个ACL_OBJECT_IDENTITY的外键,recipient(比如一个ACL_SID外键),我们是否通过审核,和一个证书位掩码,表示真实的权限被授权或被拒绝,我们对于每个授权者都有单独一行,与领域对象工作获得一个权限。
ACL系统整数位掩码,每个位表示一个权限。默认授权是可读(位0),写(位1),创建(位2),删除(位3)和管理(位4),如果使用其他权限,需要实现自己的Permission实例。
ACL接口:
ACL:每个领域对象有一个,并只有一个ACL对象,它的内部保存着AccessContrilEntry,记住这是Acl的所有者,一个acl不直接引用领域对象,但是作为替代的是使用一个ObjectIdentity。这个acl保存在ACL_OBJECT_IDENTITY表里。
AccessContrilEntry:一个Acl里有多个AccessContrilEntry,在框架里常常略写成ACE,每个ACE引用特别的Permission,Sid和Acl,一个ACE可以授权或不授权,包含审核设置。ACE保存在ACL_ENTRY表里。
Permission:一个persmiion表示特殊不变的位掩码,为位掩码和输出信息提供方便的功能,上面的基本权限(位0到4)保存在BasePermssion类里。
Sid:这个ACL模板需要引用主体和GrantedAuthrity[],间接的等级由Sid接口提供,简写成“安全标识”,通常类包含PrincipalSid(表示主体在Authentication里)和GrantedAuthoritySid,。安全标识信息保存在ACL_SID表里。
OjbectIdentity:每个领域对象放在ACL模型的内部,使用OjbectIdentity。默认实现叫做OjbectIdentityImpl.
AclService:重审Acl对应的ObjectIdentity.包含的实现(JdbcAclService),重审操作代理LookupStrategy,这个LookupStrategy为检索ACL信息提供高优化策略。使用批量检索(BasicLookupStrategy)然后支持自定义实现,
MutableAclService:允许修改了的Acl放到持久化中,
//用户注销
LogoutFilter过滤器实现注销,继承至SpringSecurityFilter
private String filterProcessesUrl = "/j_spring_security_logout";
SpringSecurityFilter抽象类实现连接口Filter,并从写了doFilter,向外界提供了
抽象方法doFilterHttp,
LogoutFilter实现了doFilterHttp实现用户注销功能,在doFilterHttp中调用了LogoutHandler的logout方法注销,其中LogoutHandler是接口
interface LogoutHandler,定义的方法唯一:
void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication);
实现LogoutHandler接口的类有抽象类AbstractRememberMeServices,
其中AbstractRememberMeServices实现了接口LogoutHandler和RememberMeServices
interface RememberMeServices接口定义方法:
Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
void loginFail(HttpServletRequest request, HttpServletResponse response);
void loginSuccess(HttpServletRequest request, HttpServletResponse response,Authentication successfulAuthentication);
spring security异常机制:
abstract class SpringSecurityException extends NestedRuntimeException
这个异常是security异常的基类。
class AccessDeniedException extends SpringSecurityException
AccessDeniedException表示当前用户无权访问被保护资源时抛出的异常
class AuthorizationServiceException extends AccessDeniedException
AuthorizationServiceException表示认证服务异常
class AlreadyExistsException extends SpringSecurityException
AlreadyExistsException表示已经推出异常ACL相关
abstract class AuthenticationException extends SpringSecurityException
AuthenticationException表示验证服务异常
AuthenticationException包括以下异常:
abstract class AccountStatusException extends AuthenticationException
AccountStatusException表示当前用户不正常异常
AuthenticationCancelledException,AuthenticationCredentialsNotFoundException,AuthenticationServiceException,BadCredentialsException,ConcurrentLoginException,
InsufficientAuthenticationException(不满足验证异常),NonceExpiredException,NtlmBaseException,OpenIDAuthenticationRequiredException,PreAuthenticatedCredentialsNotFoundException,ProviderNotFoundException,RememberMeAuthenticationException,SessionAlreadyUsedException
一下是和ACL相关异常
class ChildrenExistException extends SpringSecurityException
class UnloadedSidException extends SpringSecurityException
class NotFoundException extends SpringSecurityException
class IdentityUnavailableException extends SpringSecurityException下载本文