1.web.xml中的配置
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<span style="font-family:Microsoft YaHei;"> </span><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><span style="color:#000000;">
</span>
在web.xml中需要配置ContextLoaderListener,ContextLoaderListener的作用是启动web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。
springSecurityFilterChain过滤器的作用在上一篇博客中已经叙述,这里不再阐述。
2.security-applicationContext.xml中的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<!-- 以下文件不检查权限 -->
<http auto-config="true"><!-- 访问权限 -->
<!--<custom-filter position="CAS_FILTER" ref="casAuthenticationFilter"></custom-filter>-->
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myFilter" />
<form-login login-page="/login.html"
authentication-failure-url="/login.html?error=true"
login-processing-url="/j_spring_security_check"/>
<logout logout-url="/logout"/>
</http>
<!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
我们的所有控制将在这三个类中实现,解释详见具体配置 -->
<!-- 配置过滤器 -->
<beans:bean id="myFilter"
class="org.whut.platform.business.user.security.MySecurityInterceptorFilter">
<!-- 用户拥有的权限 -->
<beans:property name="authenticationManager" ref="authenticationManager" />
<!-- 用户是否拥有所请求资源的权限 -->
<beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />
<!-- 资源与权限对应关系 -->
<beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />
</beans:bean>
<!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
<beans:bean id="myAccessDecisionManager"
class="org.whut.platform.business.user.security.MyAccessDecisionManager">
</beans:bean>
<!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->
<beans:bean id="mySecurityMetadataSource"
class="org.whut.platform.business.user.security.MySecurityMetadataSource"
>
</beans:bean>
<!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-manager alias="authenticationManager">
<authentication-provider
user-service-ref="myUserDeatailsService">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
<beans:bean id="myUserDeatailsService" class="org.whut.platform.business.user.security.MyUserDetailsService"/>
</beans:beans>
这是在
security-applicationContext.xml中的配置,下面按照spring security的作用顺序(认证,访问权限决策)来详细分析
3.MySecurityInterceptorFilter
自定义了一个MySecurityInterceptorFilter,它继承了AbstractSecurityInterceptor,如下面的代码所示:
/**
AbstractSecurityInterceptor确保security interceptor得到正确的启动配置,它将同样实现的对安全对象的操作有:
1.从SecurityContextHolder对象中获得Authentication对象
2.依靠对ObjectDefinitionSource查询获得的安全对象访问来判断请求涉及的是一个受保护的对象或是一个公用的对象 .
如果对象是受保护的,即对于安全对象有一个ConfigAttributeDefinition ,则进行如下流程:
1.如果Authentication.isAuthenticated()方法返回false,或者alwaysReauthenticate为true,则通过配置的
AuthenticationManager对
请求进行认证。如果认证成功,将返回的Authentication对象放回SecurityContextHolder中。
2.通过配置的AccessDecisionManager对请求授权
3.通过RanAsManager处理所有的run-as替换
4.将控制传递给实际的子类继续执行。为了确保AbstractSecurityInterceptor被重新调用,当子类完成处理后一个
InterceptorStatusToken将被返回。具体的子类将通过afterInvocation(InterceptorStatusToken, Object) 方法
<span style="font-family:Microsoft YaHei;"> </span>重新调用AbstractSecurityInterceptor。
5.如果RunAsManager替换了Authentication对象,则为该对象返回SecurityContextHolder。
6.如果一个AfterInvocationManager被定义,那么执行它,并允许其替换应返回给调用者的对象。
对于公共的对象,即对此安全对象没有对应的ConfigAttributeDefinition:
具体的子类在安全对象被执行后返回的InterceptorStatusToken随后将传回AbstractSecurityInterceptor,Abstract
SecurityInterceptor在afterInvocation(InterceptorStatusToken,Object)方法被调用后将不继续进行其它动作。
之后控制重新返回给实际子类,子类将返回结果或异常给原始的调用者
*/
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import javax.servlet.*;
import java.io.IOException;
public class MySecurityInterceptorFilter extends AbstractSecurityInterceptor implements
Filter {
// 与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应,
// 其他的两个组件,已经在AbstractSecurityInterceptor定义
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
private void invoke(FilterInvocation fi) throws IOException,
ServletException {
// object为FilterInvocation对象
// super.beforeInvocation(fi);//源码
// 1.获取请求资源的权限
//执行 Collection<ConfigAttribute> attributes = securityMetadataSource.getAttributes(fi);
<span style="font-family:Microsoft YaHei;"> //</span>在 AbstractSecurityInterceptor中调用 其实现在MySecurityMetadataSourc<span style="font-family:Microsoft YaHei;">e</span>
// 2.是否拥有权限
// this.accessDecisionManager.decide(authenticated, fi, attributes);
// this.accessDecisionManager.decide(authenticated, fi, attributes);
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public Class<? extends Object> getSecureObjectClass() {
//下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误
return FilterInvocation.class;
}
}
这里就不贴出
AbstractSecurityInterceptor的源码了 有兴趣的自己可以去看。
4.MyUserDetailsService类
MyUserDetailsService类实现了UserDetailService接口
public class MyUserDetailsService implements UserDetailsService {
private static PlatformLogger logger = PlatformLogger.getLogger(MyUserDetailsService.class);
@Autowired
private UserService userService;
@Autowired
private UserAuthorityService userAuthorityService;
/*
在这个类中实现方法UserDetails loadUserByUsername(String username),从数据库获取用户信息,以及其拥有的角色
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByName(username);
if(user!=null){
MyUserDetail userDetails = new MyUserDetail();
userDetails.setUserName(username);
userDetails.setPassword(user.getPassword());//这里可以从数据库取
userDetails.setId(user.getId());
userDetails.setAppId(user.getAppId());
userDetails.setAuthorities(getUserAuthority(username));
return userDetails;
}
return null;
}
//获取用户的授权角色列表
private List<GrantedAuthority> getUserAuthority(String userName){
List<UserAuthority> authorityList = userAuthorityService.findByUserName(userName);
if(authorityList==null) return null;
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for(UserAuthority userAuthority:authorityList){
authorities.add(new GrantedAuthorityImpl(userAuthority.getAuthorityName()));
}
return authorities;
}
}
5.MySecurityMetadataSource
MySecurityMetadataSource的作用是加载资源与权限的对应关系
/**
* 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。
* securityMetadataSource相当于本包中自定义的MySecurityMetadataSource。
* 该MySecurityMetadataSource的作用提从数据库提取权限和资源,装配到HashMap中,
* 供Spring Security使用,用于权限校验。
*
*/
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource,InitializingBean {
@Autowired
private AuthorityPowerService authorityPowerServices;
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
private RequestMatcher pathMatcher;
//由spring调用
public MySecurityMetadataSource(){
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return new ArrayList<ConfigAttribute>();
}
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return true;
}
//加载所有资源与权限的关系
private void loadResourceDefine(){
if (resourceMap == null) {
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
List<AuthorityPower> resources = authorityPowerServices.getAuthorityPowerList();
for (AuthorityPower resource : resources) {
Collection<ConfigAttribute> configAttributes = null;
if(resourceMap.containsKey(resource.getPowerResource())){
ConfigAttribute configAttribute = new SecurityConfig(resource.getAuthorityName());
configAttributes = (Collection<ConfigAttribute>)resourceMap.get(resource.getPowerResource());
configAttributes.add(configAttribute);
}else{
configAttributes = new ArrayList<ConfigAttribute>();
/*以权限名封装为Spring的security Object
resource.getRoleName() 角色名称 可随意 role_admin 或者 admin
<span style="font-family:Microsoft YaHei;"> </span>*/
ConfigAttribute configAttribute =
new SecurityConfig(resource.getAuthorityName());
configAttributes.add(configAttribute);
/*resource.getInterceptUrl() 格式必须是 拦截的包路径
*/
//或者是 比如 /manager/**/*.jh 或者 /system/manager/**/*.jsp
}
resourceMap.put(resource.getPowerResource(), configAttributes);
}
}
}
//返回所请求资源所需要的权限
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
Iterator<String> it = resourceMap.keySet().iterator();
while (it.hasNext()) {
String resURL = it.next();
Iterator<String> ite = resourceMap.keySet().iterator();
pathMatcher = new AntPathRequestMatcher(resURL);
if (pathMatcher.matches(((FilterInvocation) object).getRequest())) {
Collection<ConfigAttribute> returnCollection =
resourceMap.get(resURL);
return returnCollection;
}
}
return null;
}
@Override
public void afterPropertiesSet() throws Exception {
loadResourceDefine();
}
}
以上就是
security-applicationContext.xml中的配置。
当然最重要的是需要在数据库中建立user(用户)、authority(角色)、power(资源)、user_authority、authority_power,五张表。