程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Spring Security 2配置精講 下

Spring Security 2配置精講 下

編輯:關於JAVA

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信息。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved