1,建一个web project,并导入所有需要的lib,这步就不多讲了。
2,配置web.xml,使用Spring的机制装载:
<?
xml version="1.0" encoding="UTF-8"
?>
<
web-app
version
="2.4"
xmlns
="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
>
<
context-param
>
<
param-name
>
contextConfigLocation
</
param-name
>
<
param-value
>
classpath:applicationContext*.xml
</
param-value
>
</
context-param
>
<
listener
>
<
listener-class
>
org.springframework.web.context.ContextLoaderListener
</
listener-class
>
</
listener
>
<
filter
>
<
filter-name
>
springSecurityFilterChain
</
filter-name
>
<
filter-class
>
org.springframework.web.filter.DelegatingFilterProxy
</
filter-class
>
</
filter
>
<
filter-mapping
>
<
filter-name
>
springSecurityFilterChain
</
filter-name
>
<
url-pattern
>
/*
</
url-pattern
>
</
filter-mapping
>
<
welcome-file-list
>
<
welcome-file
>
login.jsp
</
welcome-file
>
</
welcome-file-list
>
</
web-app
>
这个文件中的内容我相信大家都很熟悉了,不再多说了。
2,来看看applicationContext-security.xml这个配置文件,关于Spring Security的配置均在其中:
3,来看看自定义filter的实现:
最核心的代码就是invoke方法中的InterceptorStatusToken token =
super.beforeInvocation(fi);这一句,即在执行doFilter之前,进行权限的检查,而具体的实现已经交给
accessDecisionManager了,下文中会讲述。
4,来看看authentication-provider的实现:
package
com.
user
.erp.fwk.security;
import
java.util.ArrayList;
import
java.util.Collection;
import
org.springframework.dao.DataAccessException;
import
org.springframework.security.core.GrantedAuthority;
import
org.springframework.security.core.authority.GrantedAuthorityImpl;
import
org.springframework.security.core.userdetails.User;
import
org.springframework.security.core.userdetails.UserDetails;
import
org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.core.userdetails.UsernameNotFoundException;
public
class
MyUserDetailService
implements
UserDetailsService
{
@Override
public
UserDetails loadUserByUsername(String username)
throws
UsernameNotFoundException, DataAccessException
{
Collection
<
GrantedAuthority
>
auths
=
new
ArrayList
<
GrantedAuthority
>
();
GrantedAuthorityImpl auth2
=
new
GrantedAuthorityImpl(
"
ROLE_ADMIN
"
);
auths.add(auth2);
if
(username.equals(
"
user
1
"
))
{
auths
=
new
ArrayList
<
GrantedAuthority
>
();
GrantedAuthorityImpl auth1
=
new
GrantedAuthorityImpl(
"
ROLE_
user
"
);
auths.add(auth1);
}
//
User(String username, String password, boolean enabled, boolean accountNonExpired,
//
boolean credentialsNonExpired, boolean accountNonLocked, Collection<GrantedAuthority> authorities) {
User user
=
new
User(username,
"
user
"
,
true
,
true
,
true
,
true
, auths);
return
user;
}
}
在这个类中,你就可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等,我想这么简单的代码就不再多解释了。
5,对于资源的访问权限的定义,我们通过实现FilterInvocationSecurityMetadataSource这个接口来初始化数据。
看看loadResourceDefine方法,我在这里,假定index.jsp和i.jsp这两个资源,需要ROLE_ADMIN角色的用户才能访问。
这个类中,还有一个最核心的地方,就是提供某个资源对应的权限定义,即getAttributes方法返回的结果。注意,我例子中使用的是
AntUrlPathMatcher这个path
matcher来检查URL是否与资源定义匹配,事实上你还要用正则的方式来匹配,或者自己实现一个matcher。
6,剩下的就是最终的决策了,make a decision,其实也很容易,呵呵。
package
com.
user
.erp.fwk.security;
import
java.util.Collection;
import
java.util.Iterator;
import
org.springframework.security.access.AccessDecisionManager;
import
org.springframework.security.access.AccessDeniedException;
import
org.springframework.security.access.ConfigAttribute;
import
org.springframework.security.access.SecurityConfig;
import
org.springframework.security.authentication.InsufficientAuthenticationException;
import
org.springframework.security.core.Authentication;
import
org.springframework.security.core.GrantedAuthority;
public
class
MyAccessDecisionManager
implements
AccessDecisionManager
{
//
In this method, need to compare authentication with configAttributes.
//
1, A object is a URL, a filter was find permission configuration by this URL, and pass to here.
//
2, Check authentication has attribute in permission configuration (configAttributes)
//
3, If not match corresponding authentication, throw a AccessDeniedException.
public
void
decide(Authentication authentication, Object object,
Collection
<
ConfigAttribute
>
configAttributes)
throws
AccessDeniedException, InsufficientAuthenticationException
{
if
(configAttributes
==
null
)
{
return
;
}
System.out.println(object.toString());
//
object is a URL.
Iterator
<
ConfigAttribute
>
ite
=
configAttributes.iterator();
while
(ite.hasNext())
{
ConfigAttribute ca
=
ite.next();
String needRole
=
((SecurityConfig)ca).getAttribute();
for
(GrantedAuthority ga:authentication.getAuthorities())
{
if
(needRole.equals(ga.getAuthority()))
{
//
ga is user's role.
return
;
}
}
}
throw
new
AccessDeniedException(
"
no right
"
);
}
@Override
public
boolean
supports(ConfigAttribute attribute)
{
//
TODO Auto-generated method stub
return
true
;
}
@Override
public
boolean
supports(Class
<?>
clazz)
{
return
true
;
}
}
在这个类中,最重要的是decide方法,如果不存在对该资源的定义,直接放行;否则,如果找到正确的角色,即认为拥有权限,并放行,否则throw
new AccessDeniedException("no right");这样,就会进入上面提到的403.jsp页面。