package com.shenzhen.management.util.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;
/**
*AccessdecisionManager在Spring security中是很重要的。
*
*在验证部分简略提过了,所有的Authentication实现需要保存在一个GrantedAuthority对象数组中。
*这就是赋予给主体的权限。 GrantedAuthority对象通过AuthenticationManager
*保存到 Authentication对象里,然后从AccessDecisionManager读出来,进行授权判断。
*
*Spring Security提供了一些拦截器,来控制对安全对象的访问权限,例如方法调用或web请求。
*一个是否允许执行调用的预调用决定,是由AccessDecisionManager实现的。
*这个 AccessDecisionManager 被AbstractSecurityInterceptor调用,
*它用来作最终访问控制的决定。 这个AccessDecisionManager接口包含三个方法:
*
void decide(Authentication authentication, Object secureObject,
List
boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);
从第一个方法可以看出来,AccessDecisionManager使用方法参数传递所有信息,这好像在认证评估时进行决定。
特别是,在真实的安全方法期望调用的时候,传递安全Object启用那些参数。
比如,让我们假设安全对象是一个MethodInvocation。
很容易为任何Customer参数查询MethodInvocation,
然后在AccessDecisionManager里实现一些有序的安全逻辑,来确认主体是否允许在那个客户上操作。
如果访问被拒绝,实现将抛出一个AccessDeniedException异常。
这个 supports(ConfigAttribute) 方法在启动的时候被
AbstractSecurityInterceptor调用,来决定AccessDecisionManager
是否可以执行传递ConfigAttribute。
supports(Class)方法被安全拦截器实现调用,
包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型。
*/
public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide( Authentication authentication, Object object, Collection
throws AccessDeniedException, InsufficientAuthenticationException{
if( configAttributes == null ) {
return ;
}
Iterator
while(iterator.hasNext()){
ConfigAttribute configAttribute = iterator.next();
String needRole = ((SecurityConfig)configAttribute).getAttribute();
//grantedAuthority 为用户所被赋予的权限。 needRole为访问相应的资源应该具有的权限。
for( GrantedAuthority grantedAuthority: authentication.getAuthorities()){
if(needRole.trim().equals(grantedAuthority.getAuthority().trim())){
return;
}
}
}
throw new AccessDeniedException("");
}
public boolean supports( ConfigAttribute attribute ){
return true;
}
public boolean supports(Class> clazz){
return true;
}
}
-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------
package com.shenzhen.management.util.security;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
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;
/**
*
* 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。
* securityMetadataSource相当于本包中自定义的MyInvocationSecurityMetadataSourceService。
* 该MyInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源,装配到HashMap中,
* 供Spring Security使用,用于权限校验。
*
*/
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation filterInvocation = new FilterInvocation(request, response, chain);
invoke(filterInvocation);
}
public Class extends Object> getSecureObjectClass() {
return FilterInvocation.class;
}
public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
try {
filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void destroy() {
}
public void init(FilterConfig filterconfig) throws ServletException {
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
}
-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------
package com.shenzhen.management.util.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import com.shenzhen.management.dao.UserDao;
/**
* 最核心的地方,就是提供某个资源对应的权限定义,即getAttributes方法返回的结果。 此类在初始化时,应该取到所有资源及其对应角色的定义。
*
*/
public class MyInvocationSecurityMetadataSourceService implements
FilterInvocationSecurityMetadataSource {
private UrlMatcher urlMatcher = new AntUrlPathMatcher();
private static Map
public MyInvocationSecurityMetadataSourceService() {
loadResourceDefine();
}
private void loadResourceDefine() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
List
// 应当是资源为key, 权限为value。 资源通常为url, 权限就是那些以ROLE_为前缀的角色。 一个资源可以由多个权限来访问。
resourceMap = new HashMap
for (String securitie : securities) {
ConfigAttribute configAttribute = new SecurityConfig(securitie);
List
for (String resource : resources) {
String url = resource;
// 判断资源文件和权限的对应关系,如果已经存在相关的资源url,则要通过该url为key提取出权限集合,将权限增加到权限集合中。
if (resourceMap.containsKey(url)) {
Collection
value.add(configAttribute);
resourceMap.put(url, value);
} else {
Collection
configAttributes.add(configAttribute);
resourceMap.put(url, configAttributes);
}
}
}
}
@Override
public Collection
return null;
}
// 根据URL,找到相关的权限配置。
@Override
public Collection
throws IllegalArgumentException {
// object 是一个URL,被用户请求的url。
String url = ((FilterInvocation) object).getRequestUrl();
int firstQuestionMarkIndex = url.indexOf("?");
if (firstQuestionMarkIndex != -1) {
url = url.substring(0, firstQuestionMarkIndex);
}
Iterator
while (ite.hasNext()) {
String resURL = ite.next();
if (urlMatcher.pathMatchesUrl(url, resURL)) {
return resourceMap.get(resURL);
}
}
return null;
}
@Override
public boolean supports(Class> arg0) {
return true;
}
}
-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------
package com.shenzhen.management.util.security;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.shenzhen.management.dao.UserDao;
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Autowired
private UserCache userCache;
/**
*该类的主要作用是为Spring Security提供一个经过用户认证后的UserDetails。
*该UserDetails包括用户名、密码、是否可用、是否过期等信息。
*/
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
Collection
// 得到用户的权限
auths = userDao.loadUserAuthoritiesByName(username);
String password = null;
// 取得用户的密码
password = userDao.getPasswordByUsername(username);
return new User(username, password, true, true, true, true, auths);
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 设置用户缓存功能。
public void setUserCache(UserCache userCache) {
this.userCache = userCache;
}
public UserCache getUserCache() {
return this.userCache;
}
}
-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------
xmlns:b="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.0.xsd"> filters="none" /> authentication-failure-url="/index.jsp?error=true" default-target-url="/index.jsp" /> class="com.shenzhen.management.util.security.MyFilterSecurityInterceptor"> ref="authenticationManager"/> ref="myAccessDecisionManager"/> ref="mySecurityMetadataSource"/> class="com.shenzhen.management.util.security.MyAccessDecisionManager"> class="com.shenzhen.management.util.security.MyInvocationSecurityMetadataSourceService">
-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> value="classpath:org/springframework/security/messages_zh_CN"/> class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" /> class="org.springframework.security.provisioning.JdbcUserDetailsManager"> class="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache"> class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------
-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------