`
karlhell
  • 浏览: 105605 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Spring Security 2 配置精讲 下

    博客分类:
  • SSH
阅读更多
  2. 在系统启动的时候,把所有的资源load到内存作为缓存

  由于资源信息对于每个项目来说,相对固定,所以我们可以将他们在系统启动的时候就load到内存作为缓存。这里做法很多,我给出的示例是将资源的存放在servletContext中。

  Java代码

public class ServletContextLoaderListener implements ServletContextListener {   
  
    /* (non-Javadoc)  
     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)  
     */  
    public void contextInitialized(ServletContextEvent servletContextEvent) {   
        ServletContext servletContext = servletContextEvent.getServletContext();   
        SecurityManager securityManager = this.getSecurityManager(servletContext);   
           
        Map<String, String> urlAuthorities = securityManager.loadUrlAuthorities();   
        servletContext.setAttribute("urlAuthorities", urlAuthorities);   
    }   
  
       
    /* (non-Javadoc)  
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)  
     */  
    public void contextDestroyed(ServletContextEvent servletContextEvent) {   
        servletContextEvent.getServletContext().removeAttribute("urlAuthorities");   
    }   
  
    /**  
     * Get SecurityManager from ApplicationContext  
     *   
     * @param servletContext  
     * @return  
     */  
    protected SecurityManager getSecurityManager(ServletContext servletContext) {   
       return (SecurityManager) WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean("securityManager");    
    }   
  
}  

public class ServletContextLoaderListener implements ServletContextListener {

    /* (non-Javadoc)
     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     */
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ServletContext servletContext = servletContextEvent.getServletContext();
        SecurityManager securityManager = this.getSecurityManager(servletContext);
        
        Map<String, String> urlAuthorities = securityManager.loadUrlAuthorities();
        servletContext.setAttribute("urlAuthorities", urlAuthorities);
    }

    
    /* (non-Javadoc)
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
     */
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        servletContextEvent.getServletContext().removeAttribute("urlAuthorities");
    }

    /**
     * Get SecurityManager from ApplicationContext
     * 
     * @param servletContext
     * @return
     */
    protected SecurityManager getSecurityManager(ServletContext servletContext) {
       return (SecurityManager) WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean("securityManager"); 
    }

}


  这里,我们看到了SecurityManager,这是一个接口,用于权限相关的逻辑处理。还记得之前我们使用数据库管理User的时候所使用的一个实现类SecurityManagerSupport嘛?我们不妨依然借用这个类,让它实现SecurityManager接口,来同时完成url的读取工作。

  Java代码
@Service("securityManager")   
public class SecurityManagerSupport extends HibernateDaoSupport implements UserDetailsService, SecurityManager {   
       
    /**  
     * Init sessionFactory here because the annotation of Spring 2.5 can not support override inject  
     *    
     * @param sessionFactory  
     */  
    @Autowired  
    public void init(SessionFactory sessionFactory) {   
        super.setSessionFactory(sessionFactory);   
    }   
       
    /* (non-Javadoc)  
     * @see org.springframework.security.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)  
     */  
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {   
        List<User> users = getHibernateTemplate().find("FROM User user WHERE user.name = ? AND user.disabled = false", userName);   
        if(users.isEmpty()) {   
            throw new UsernameNotFoundException("User " + userName + " has no GrantedAuthority");   
        }   
        return users.get(0);   
    }   
       
    /* (non-Javadoc)  
     * @see com.javaeye.sample.security.SecurityManager#loadUrlAuthorities()  
     */  
    public Map<String, String> loadUrlAuthorities() {   
        Map<String, String> urlAuthorities = new HashMap<String, String>();   
        List<Resource> urlResources = getHibernateTemplate().find("FROM Resource resource WHERE resource.type = ?", "URL");   
        for(Resource resource : urlResources) {   
            urlAuthorities.put(resource.getValue(), resource.getRoleAuthorities());   
        }   
        return urlAuthorities;   
    }      
}  

@Service("securityManager")
public class SecurityManagerSupport extends HibernateDaoSupport implements UserDetailsService, SecurityManager {
    
    /**
     * Init sessionFactory here because the annotation of Spring 2.5 can not support override inject
     *  
     * @param sessionFactory
     */
    @Autowired
    public void init(SessionFactory sessionFactory) {
        super.setSessionFactory(sessionFactory);
    }
    
    /* (non-Javadoc)
     * @see org.springframework.security.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
     */
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
        List<User> users = getHibernateTemplate().find("FROM User user WHERE user.name = ? AND user.disabled = false", userName);
        if(users.isEmpty()) {
            throw new UsernameNotFoundException("User " + userName + " has no GrantedAuthority");
        }
        return users.get(0);
    }
    
    /* (non-Javadoc)
     * @see com.javaeye.sample.security.SecurityManager#loadUrlAuthorities()
     */
    public Map<String, String> loadUrlAuthorities() {
        Map<String, String> urlAuthorities = new HashMap<String, String>();
        List<Resource> urlResources = getHibernateTemplate().find("FROM Resource resource WHERE resource.type = ?", "URL");
        for(Resource resource : urlResources) {
            urlAuthorities.put(resource.getValue(), resource.getRoleAuthorities());
        }
        return urlAuthorities;
    }   
}


  3. 编写自己的FilterInvocationDefinitionSource实现类,对资源进行认证

  Java代码

public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationDefinitionSource, InitializingBean {   
       
    private UrlMatcher urlMatcher;   
  
    private boolean useAntPath = true;   
       
    private boolean lowercaseComparisons = true;   
       
    /**  
     * @param useAntPath the useAntPath to set  
     */  
    public void setUseAntPath(boolean useAntPath) {   
        this.useAntPath = useAntPath;   
    }   
       
    /**  
     * @param lowercaseComparisons  
     */  
    public void setLowercaseComparisons(boolean lowercaseComparisons) {   
        this.lowercaseComparisons = lowercaseComparisons;   
    }   
       
    /* (non-Javadoc)  
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()  
     */  
    public void afterPropertiesSet() throws Exception {   
           
        // default url matcher will be RegexUrlPathMatcher   
        this.urlMatcher = new RegexUrlPathMatcher();   
           
        if (useAntPath) {  // change the implementation if required   
            this.urlMatcher = new AntUrlPathMatcher();   
        }   
           
        // Only change from the defaults if the attribute has been set   
        if ("true".equals(lowercaseComparisons)) {   
            if (!this.useAntPath) {   
                ((RegexUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(true);   
            }   
        } else if ("false".equals(lowercaseComparisons)) {   
            if (this.useAntPath) {   
                ((AntUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(false);   
            }   
        }   
           
    }   
       
    /* (non-Javadoc)  
     * @see org.springframework.security.intercept.ObjectDefinitionSource#getAttributes(java.lang.Object)  
     */  
    public ConfigAttributeDefinition getAttributes(Object filter) throws IllegalArgumentException {   
           
        FilterInvocation filterInvocation = (FilterInvocation) filter;   
        String requestURI = filterInvocation.getRequestUrl();   
        Map<String, String> urlAuthorities = this.getUrlAuthorities(filterInvocation);   
           
        String grantedAuthorities = null;   
        for(Iterator<Map.Entry<String, String>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {   
            Map.Entry<String, String> entry = iter.next();   
            String url = entry.getKey();   
               
            if(urlMatcher.pathMatchesUrl(url, requestURI)) {   
                grantedAuthorities = entry.getValue();   
                break;   
            }   
               
        }   
           
        if(grantedAuthorities != null) {   
            ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();   
            configAttrEditor.setAsText(grantedAuthorities);   
            return (ConfigAttributeDefinition) configAttrEditor.getValue();   
        }   
           
        return null;   
    }   
  
    /* (non-Javadoc)  
     * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()  
     */  
    @SuppressWarnings("unchecked")   
    public Collection getConfigAttributeDefinitions() {   
        return null;   
    }   
  
    /* (non-Javadoc)  
     * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class)  
     */  
    @SuppressWarnings("unchecked")   
    public boolean supports(Class clazz) {   
        return true;   
    }   
       
    /**  
     *   
     * @param filterInvocation  
     * @return  
     */  
    @SuppressWarnings("unchecked")   
    private Map<String, String> getUrlAuthorities(FilterInvocation filterInvocation) {   
        ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();   
        return (Map<String, String>)servletContext.getAttribute("urlAuthorities");   
    }   
  
}  

public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationDefinitionSource, InitializingBean {
    
    private UrlMatcher urlMatcher;

    private boolean useAntPath = true;
    
    private boolean lowercaseComparisons = true;
    
    /**
     * @param useAntPath the useAntPath to set
     */
    public void setUseAntPath(boolean useAntPath) {
        this.useAntPath = useAntPath;
    }
    
    /**
     * @param lowercaseComparisons
     */
    public void setLowercaseComparisons(boolean lowercaseComparisons) {
        this.lowercaseComparisons = lowercaseComparisons;
    }
    
    /* (non-Javadoc)
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        
        // default url matcher will be RegexUrlPathMatcher
        this.urlMatcher = new RegexUrlPathMatcher();
        
        if (useAntPath) {  // change the implementation if required
            this.urlMatcher = new AntUrlPathMatcher();
        }
        
        // Only change from the defaults if the attribute has been set
        if ("true".equals(lowercaseComparisons)) {
            if (!this.useAntPath) {
                ((RegexUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(true);
            }
        } else if ("false".equals(lowercaseComparisons)) {
            if (this.useAntPath) {
                ((AntUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(false);
            }
        }
        
    }
    
    /* (non-Javadoc)
     * @see org.springframework.security.intercept.ObjectDefinitionSource#getAttributes(java.lang.Object)
     */
    public ConfigAttributeDefinition getAttributes(Object filter) throws IllegalArgumentException {
        
        FilterInvocation filterInvocation = (FilterInvocation) filter;
        String requestURI = filterInvocation.getRequestUrl();
        Map<String, String> urlAuthorities = this.getUrlAuthorities(filterInvocation);
        
        String grantedAuthorities = null;
        for(Iterator<Map.Entry<String, String>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {
            Map.Entry<String, String> entry = iter.next();
            String url = entry.getKey();
            
            if(urlMatcher.pathMatchesUrl(url, requestURI)) {
                grantedAuthorities = entry.getValue();
                break;
            }
            
        }
        
        if(grantedAuthorities != null) {
            ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();
            configAttrEditor.setAsText(grantedAuthorities);
            return (ConfigAttributeDefinition) configAttrEditor.getValue();
        }
        
        return null;
    }

    /* (non-Javadoc)
     * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()
     */
    @SuppressWarnings("unchecked")
 public Collection getConfigAttributeDefinitions() {
        return null;
    }

    /* (non-Javadoc)
     * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class)
     */
    @SuppressWarnings("unchecked")
 public boolean supports(Class clazz) {
        return true;
    }
    
    /**
     * 
     * @param filterInvocation
     * @return
     */
    @SuppressWarnings("unchecked")
 private Map<String, String> getUrlAuthorities(FilterInvocation filterInvocation) {
        ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
        return (Map<String, String>)servletContext.getAttribute("urlAuthorities");
    }

}


  4. 配置文件修改

  接下来,我们来修改一下Spring Security的配置文件,把我们自定义的这个过滤器插入到过滤器链中去。

  Xml代码

<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-2.5.xsd   
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">  
       
    <beans:bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener" />  
       
    <http access-denied-page="/403.jsp" >  
        <intercept-url pattern="/static/**" filters="none" />  
        <intercept-url pattern="/template/**" filters="none" />  
        <intercept-url pattern="/" filters="none" />  
        <intercept-url pattern="/login.jsp" filters="none" />  
        <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" default-target-url="/index" />  
        <logout logout-success-url="/login.jsp"/>  
        <http-basic />  
    </http>  
  
    <authentication-manager alias="authenticationManager"/>  
       
    <authentication-provider user-service-ref="securityManager">  
        <password-encoder hash="md5"/>  
    </authentication-provider>  
       
    <beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">  
        <beans:property name="allowIfAllAbstainDecisions" value="false"/>  
        <beans:property name="decisionVoters">  
            <beans:list>  
                <beans:bean class="org.springframework.security.vote.RoleVoter"/>  
                <beans:bean class="org.springframework.security.vote.AuthenticatedVoter"/>  
            </beans:list>  
        </beans:property>  
    </beans:bean>  
       
    <beans:bean id="resourceSecurityInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">  
        <beans:property name="authenticationManager" ref="authenticationManager"/>  
        <beans:property name="accessDecisionManager" ref="accessDecisionManager"/>  
        <beans:property name="objectDefinitionSource" ref="secureResourceFilterInvocationDefinitionSource" />  
        <beans:property name="observeOncePerRequest" value="false" />  
        <custom-filter after="LAST" />  
    </beans:bean>  
       
    <beans:bean id="secureResourceFilterInvocationDefinitionSource" class="com.javaeye.sample.security.interceptor.SecureResourceFilterInvocationDefinitionSource" />  
       
</beans:beans>  

<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-2.5.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
 
 <beans:bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener" />
 
 <http access-denied-page="/403.jsp" >
  <intercept-url pattern="/static/**" filters="none" />
  <intercept-url pattern="/template/**" filters="none" />
  <intercept-url pattern="/" filters="none" />
  <intercept-url pattern="/login.jsp" filters="none" />
     <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" default-target-url="/index" />
     <logout logout-success-url="/login.jsp"/>
     <http-basic />
 </http>

 <authentication-manager alias="authenticationManager"/>
 
 <authentication-provider user-service-ref="securityManager">
  <password-encoder hash="md5"/>
 </authentication-provider>
 
 <beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
     <beans:property name="allowIfAllAbstainDecisions" value="false"/>
     <beans:property name="decisionVoters">
         <beans:list>
             <beans:bean class="org.springframework.security.vote.RoleVoter"/>
             <beans:bean class="org.springframework.security.vote.AuthenticatedVoter"/>
         </beans:list>
     </beans:property>
 </beans:bean>
 
 <beans:bean id="resourceSecurityInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
  <beans:property name="authenticationManager" ref="authenticationManager"/>
     <beans:property name="accessDecisionManager" ref="accessDecisionManager"/>
     <beans:property name="objectDefinitionSource" ref="secureResourceFilterInvocationDefinitionSource" />
     <beans:property name="observeOncePerRequest" value="false" />
     <custom-filter after="LAST" />
 </beans:bean>
 
 <beans:bean id="secureResourceFilterInvocationDefinitionSource" class="com.javaeye.sample.security.interceptor.SecureResourceFilterInvocationDefinitionSource" />
 
</beans:beans>


  请注意,由于我们所实现的,是FilterSecurityInterceptor中的一个开放接口,所以我们实际上定义了一个新的bean,并通过<custom-filter after="LAST" />插入到过滤器链中去。

  Spring Security对象的访问

  1. 访问当前登录用户

  Spring Security提供了一个线程安全的对象:SecurityContextHolder,通过这个对象,我们可以访问当前的登录用户。我写了一个类,可以通过静态方法去读取:

  Java代码

public class SecurityUserHolder {   
  
    /**  
     * Returns the current user  
     *   
     * @return  
     */  
    public static User getCurrentUser() {   
        return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();   
    }   
  
}  

public class SecurityUserHolder {

 /**
  * Returns the current user
  * 
  * @return
  */
 public static User getCurrentUser() {
  return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
 }

}


  2. 访问当前登录用户所拥有的权限

  通过上面的分析,我们知道,用户所拥有的所有权限,其实是通过UserDetails接口中的getAuthorities()方法获得的。只要实现这个接口,就能实现需求。在我的代码中,不仅实现了这个接口,还在上面做了点小文章,这样我们可以获得一个用户所拥有权限的字符串表示:

  Java代码

/* (non-Javadoc)  
 * @see org.springframework.security.userdetails.UserDetails#getAuthorities()  
 */  
public GrantedAuthority[] getAuthorities() {   
    List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>(roles.size());   
    for(Role role : roles) {   
        grantedAuthorities.add(new GrantedAuthorityImpl(role.getName()));   
    }   
       return grantedAuthorities.toArray(new GrantedAuthority[roles.size()]);   
}   
  
/**  
 * Returns the authorites string  
 *   
 * eg.   
 *    downpour --- ROLE_ADMIN,ROLE_USER  
 *    robbin --- ROLE_ADMIN  
 *   
 * @return  
 */  
public String getAuthoritiesString() {   
    List<String> authorities = new ArrayList<String>();   
    for(GrantedAuthority authority : this.getAuthorities()) {   
        authorities.add(authority.getAuthority());   
    }   
    return StringUtils.join(authorities, ",");   
}  

 /* (non-Javadoc)
  * @see org.springframework.security.userdetails.UserDetails#getAuthorities()
  */
 public GrantedAuthority[] getAuthorities() {
  List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>(roles.size());
     for(Role role : roles) {
      grantedAuthorities.add(new GrantedAuthorityImpl(role.getName()));
     }
        return grantedAuthorities.toArray(new GrantedAuthority[roles.size()]);
 }
 
 /**
  * Returns the authorites string
  * 
  * eg. 
  *    downpour --- ROLE_ADMIN,ROLE_USER
  *    robbin --- ROLE_ADMIN
  * 
  * @return
  */
 public String getAuthoritiesString() {
     List<String> authorities = new ArrayList<String>();
     for(GrantedAuthority authority : this.getAuthorities()) {
         authorities.add(authority.getAuthority());
     }
     return StringUtils.join(authorities, ",");
 }


  3. 访问当前登录用户能够访问的资源

  这就涉及到用户(User),权限(Role)和资源(Resource)三者之间的对应关系。我同样在User对象中实现了一个方法:

  Java代码

/**  
 * @return the roleResources  
 */  
public Map<String, List<Resource>> getRoleResources() {   
    // init roleResources for the first time   
    if(this.roleResources == null) {               
        this.roleResources = new HashMap<String, List<Resource>>();   
               
        for(Role role : this.roles) {   
            String roleName = role.getName();   
            Set<Resource> resources = role.getResources();   
            for(Resource resource : resources) {   
                String key = roleName + "_" + resource.getType();   
                    if(!this.roleResources.containsKey(key)) {   
                        this.roleResources.put(key, new ArrayList<Resource>());   
                }   
                    this.roleResources.get(key).add(resource);                     
            }   
        }   
               
    }   
    return this.roleResources;   
}  

/**
 * @return the roleResources
 */
public Map<String, List<Resource>> getRoleResources() {
 // init roleResources for the first time
 if(this.roleResources == null) {   
  this.roleResources = new HashMap<String, List<Resource>>();
   
  for(Role role : this.roles) {
   String roleName = role.getName();
   Set<Resource> resources = role.getResources();
   for(Resource resource : resources) {
    String key = roleName + "_" + resource.getType();
     if(!this.roleResources.containsKey(key)) {
      this.roleResources.put(key, new ArrayList<Resource>());
    }
     this.roleResources.get(key).add(resource);     
   }
  }
   
 }
 return this.roleResources;
}



  这里,会在User对象中设置一个缓存机制,在第一次取的时候,通过遍历User所有的Role,获取相应的Resource信息。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics