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<ConfigAttributeDefinition> config) throws AccessDeniedException; 

 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<ConfigAttribute> configAttributes)   

 throws AccessDeniedException, InsufficientAuthenticationException{  

   

 if( configAttributes == null ) {  

 

 return ;  

 }  

   

 Iterator<ConfigAttribute> iterator = configAttributes.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<String, Collection<ConfigAttribute>> resourceMap = null;


public MyInvocationSecurityMetadataSourceService() {

loadResourceDefine();

}


private void loadResourceDefine() {


ApplicationContext context = new ClassPathXmlApplicationContext(

"classpath:applicationContext.xml");

UserDao userDao = (UserDao) context.getBean("userDao");


List<String> securities = userDao.getSecurities();

// 应当是资源为key, 权限为value。 资源通常为url, 权限就是那些以ROLE_为前缀的角色。 一个资源可以由多个权限来访问。

resourceMap = new HashMap<String, Collection<ConfigAttribute>>();


for (String securitie : securities) {

ConfigAttribute configAttribute = new SecurityConfig(securitie);

List<String> resources = userDao.getResources(securitie);

for (String resource : resources) {

String url = resource;

// 判断资源文件和权限的对应关系,如果已经存在相关的资源url,则要通过该url为key提取出权限集合,将权限增加到权限集合中。

if (resourceMap.containsKey(url)) {

Collection<ConfigAttribute> value = resourceMap.get(url);

value.add(configAttribute);

resourceMap.put(url, value);

} else {

Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();

configAttributes.add(configAttribute);

resourceMap.put(url, configAttributes);

}


}


}


}


@Override

public Collection<ConfigAttribute> getAllConfigAttributes() {


return null;


}


// 根据URL,找到相关的权限配置。

@Override

public Collection<ConfigAttribute> getAttributes(Object object)

throws IllegalArgumentException {


// object 是一个URL,被用户请求的url。

String url = ((FilterInvocation) object).getRequestUrl();

int firstQuestionMarkIndex = url.indexOf("?");

if (firstQuestionMarkIndex != -1) {

url = url.substring(0, firstQuestionMarkIndex);

}


Iterator<String> ite = resourceMap.keySet().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<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();


// 得到用户的权限

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;

}


}



-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/security"  

 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">  

  

  

 <http auto-config="true" access-denied-page="/WEB-INF/page/errorMsg.jsp">  

  <!-- 不要过滤图片等静态资源 -->  

  <intercept-url pattern="/**/*.jpg" filters="none" />  

  <intercept-url pattern="/**/*.png" filters="none" />  

  <intercept-url pattern="/**/*.gif" filters="none" />  

  <intercept-url pattern="/**/*.css" filters="none" />  

  <intercept-url pattern="/**/*.js" filters="none" />  

  <!-- 登录页面和忘记密码页面不过滤  -->  

  <intercept-url pattern="/index.jsp" filters="none" />  

  <intercept-url pattern="/WEB-INF/page/forgotpassword.jsp"  

   filters="none" />    

    

  <form-login login-page="/index.jsp"  

   authentication-failure-url="/index.jsp?error=true"  

   default-target-url="/index.jsp" />  

  

  <!-- "记住我"功能,采用持久化策略(将用户的登录信息存放在数据库表中) -->  

  <remember-me data-source-ref="dataSource" />  

    

  <!-- 检测失效的sessionId,超时时定位到另外一个URL -->  

  <session-management invalid-session-url="/WEB-INF/page/sessionTimeout.jsp" />  

    

    

  <!-- 增加一个自定义的filter,放在FILTER_SECURITY_INTERCEPTOR之前,  

  实现用户、角色、权限、资源的数据库管理。  -->  

  <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>   

    

    

 </http>  

   

   

 <!-- 一个自定义的filter,必须包含authenticationManager,  

  accessDecisionManager,securityMetadataSource三个属性。  -->  

 <bean id="myFilter"   

  class="com.shenzhen.management.util.security.MyFilterSecurityInterceptor">  

  <property name="authenticationManager"   

   ref="authenticationManager"/>  

  <property name="accessDecisionManager"   

   ref="myAccessDecisionManager"/>  

  <property name="securityMetadataSource"   

   ref="mySecurityMetadataSource"/>  

 </bean>  

   

   

  

 <!-- 注意能够为authentication-manager 设置alias别名  -->  

 <authentication-manager alias="authenticationManager">  

  <authentication-provider user-service-ref="userDetailsManager">  

   <password-encoder ref="passwordEncoder">  

    <salt-source user-property="username" />  

   </password-encoder>  

  </authentication-provider>  

 </authentication-manager>  

  

  

 <!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源。 -->  

 <bean id="myAccessDecisionManager"  

  class="com.shenzhen.management.util.security.MyAccessDecisionManager">  

 </bean>    

  

  

 <!-- 资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色去访问。 -->  

 <bean id="mySecurityMetadataSource"  

  class="com.shenzhen.management.util.security.MyInvocationSecurityMetadataSourceService">  

 </bean>   

  

</beans>  



-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"  

 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">  

   

 <!-- 定义上下文返回的消息的国际化。 -->  

 <bean id="messageSource"  

  class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  

  <property name="basename"  

   value="classpath:org/springframework/security/messages_zh_CN"/>  

 </bean>  

  

 <!--   事件监听:实现了 ApplicationListener监听接口,包括AuthenticationCredentialsNotFoundEvent 事件,  

  AuthorizationFailureEvent事件,AuthorizedEvent事件, PublicInvocationEvent事件 -->  

 <bean  class="org.springframework.security.authentication.event.LoggerListener" />  

  

 <!-- 用户的密码加密或解密 -->  

 <bean id="passwordEncoder"  

  class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />  

  

  

 <!-- 用户详细信息管理 : 数据源、用户缓存、启用用户组功能。  -->  

 <bean id="userDetailsManager"  

  class="org.springframework.security.provisioning.JdbcUserDetailsManager">  

  <property name="dataSource" ref="dataSource" />  

  <property name="userCache" ref="userCache" />  

 </bean>   

   

 <bean id="userCache"  

  class="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache">  

  <property name="cache" ref="userEhCache" />  

 </bean>   

   

   

 <bean id="userEhCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">  

  <property name="cacheName" value="userCache" />  

  <property name="cacheManager" ref="cacheManager" />  

 </bean>  

   

 <!-- 缓存用户管理 -->  

 <bean id="cacheManager"  

  class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />  

    

  

 <!-- spring security自带的与权限有关的数据读写Jdbc模板 -->  

 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  

  <property name="dataSource" ref="dataSource" />  

 </bean>  

  

</beans> 



-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------

  <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>



-----------------------------------------------------------默默无闻的分割线-----------------------------------------------------------