SpringSecurity2配置精讲下

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 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 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 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 loadUrlAuthorities() {        Map urlAuthorities = new HashMap();        List 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 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 loadUrlAuthorities() {        Map urlAuthorities = new HashMap();        List 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 urlAuthorities = this.getUrlAuthorities(filterInvocation);        String grantedAuthorities = null;        for(IteraTor<Map.Entry> iter = urlAuthorities.entrySet().iteraTor(); iter.hasNext();) {            Map.Entry 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 getUrlAuthorities(FilterInvocation filterInvocation) {        ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();        return (Map)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 urlAuthorities = this.getUrlAuthorities(filterInvocation);        String grantedAuthorities = null;        for(IteraTor<Map.Entry> iter = urlAuthorities.entrySet().iteraTor(); iter.hasNext();) {            Map.Entry 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 getUrlAuthorities(FilterInvocation filterInvocation) {        ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();        return (Map)servletContext.getAttribute("urlAuthorities");    }}

4. 配置文件修改

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

Xml代码

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

请注意,由于我们所实现的,是FilterSecurityIntercepTor中的一个开放接口,所以我们实际上定义了一个新的bean,并通过插入到过滤器链中去。

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

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

不知道来年,会不会开出一地的记忆和忧愁。

SpringSecurity2配置精讲下

相关文章:

你感兴趣的文章:

标签云: