SpringSecurity个性化配置

标签: springsecurity 个性 | 发表时间:2013-04-02 15:55 | 作者:JavaMan_chen
出处:http://blog.csdn.net

应用场景

现有的数据库中包含以下几张表格用于权限管理

要求在此基础上集成SpringSecurity,将表格的数据作为数据源来完成登录和权限校验逻辑

SpringSecurity的配置可通过两种方式呈现,基于自身的namespace配置和传统的基于Bean的配置。通过namespace来配置Security非常简洁,隐藏了很多繁琐的实现细节,但也不便于初学者进行理解,而如果要想对Security进行个性化定制(替换现有功能实现),最好还是采用传统的基于Bean的方式进行配置,虽然结构复杂,但是细节清晰明了
以下是两种方式的配置比较:
1.基于namespace来配置
<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-3.0.xsd
                    http://www.springframework.org/schema/security
                    http://www.springframework.org/schema/security/spring-security-3.1.xsd">
    <http pattern="/js/**" security="none" />
    <http use-expressions="true" access-denied-page="/error.html">
        <intercept-url pattern="/peoplemanage/**" access="hasRole('admin')" />
        <form-login login-page='/login.jsp'/>
        <logout />
    </http>
    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="zhangsan" password="zhangsan" authorities="admin,user"/>
                <user name="wangwu" password="wangwu" authorities="user" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
</beans:beans>
2.同样的配置还原成Bean的方式
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:sec="http://www.springframework.org/schema/security"
    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.1.xsd">

    <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <constructor-arg>
            <list>
                <sec:filter-chain pattern="/js/**" filters="none"/>
                <sec:filter-chain pattern="/**"
                    filters="securityContextPersistenceFilter,authenticationFilter,exceptionTranslationFilter,filterSecurityInterceptor"/>
            </list>
        </constructor-arg>
    </bean>
    <bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
          <property name="authenticationManager" ref="authenticationManager"/>
          <property name="accessDecisionManager" ref="accessDecisionManager"/>
          <property name="securityMetadataSource">
            <sec:filter-security-metadata-source use-expressions="true">
                  <sec:intercept-url pattern="/peoplemanage/**" access="hasRole('admin')"/>
            </sec:filter-security-metadata-source>
          </property>
    </bean>
    <!-- exceptionTranslationFilter -->
    <bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
         <property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
         <property name="accessDeniedHandler" ref="accessDeniedHandler"/>
    </bean>
    <bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
         <property name="loginFormUrl" value="/login.jsp"/>
    </bean>
    <bean id="accessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
          <property name="errorPage" value="/error.html"/>
    </bean>
    <!-- securityContextPersistenceFilter -->
    <bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
    <!-- authenticationFilter -->
    <bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
          <property name="authenticationManager" ref="authenticationManager"/>
          <property name="filterProcessesUrl" value="/j_spring_security_check"/>
    </bean>
    <!-- Core Service -->
    <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
          <property name="providers">
            <list>
                <ref local="daoAuthenticationProvider"/>
            </list>
          </property>
    </bean>
    <bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <property name="userDetailsService" ref="inMemoryDaoImpl"/>
    </bean>
    <bean id="inMemoryDaoImpl" class="org.springframework.security.provisioning.InMemoryUserDetailsManager">
        <constructor-arg name="users">
            <props>
                <prop key="zhangsan">zhangsan,enabled</prop>
                <prop key="wangwu">wangwu,enabled</prop>
            </props>
        </constructor-arg>
    </bean>
    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
        <property name="decisionVoters">
            <list>
                <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"></bean>
            </list>
        </property>
    </bean>
</beans>
还原成Bean的配置方式之后,在实现个性化的定制就变得清晰明了了。
一、首先需要修改userDetailsService的实现
在上述Demo配置中使用的是Spring内置的InMemoryUserDetailsManager,该类的主要作用是从配置文件加载zhangsan、wangwu等信息来构建用户数据源,而我们的用户数据是存储在数据库里的,因此需要修改实现,实现方式如下:
1.自定义一个Service,实现org.springframework.security.core.userdetails.UserDetailsService接口
public class MyUserDetailsService implements UserDetailsService {
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
    /**
     * TODO 从数据库中加载用户信息,并封装成UserDetails对象
     */
    }
}
2.替换Demo中的对应的配置
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
      <property name="userDetailsService" ref="myUserDetailsService"/>
</bean>
<bean id="myUserDetailsService" class="com.youcompany.MyUserDetailsService"/>
二、修改filterSecurityInterceptor中securityMetadataSource属性的注入方式
在Demo配置中securityMetadataSource属性的配置是静态的,将每一个资源和资源对应的角色封装到<sec:intercept-url>标签里
而我们的需求场景是资源信息存储在数据库里,因此不能通过这种静态的方式去描述,修改方式如下:
1.声明一个Service实现org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource接口
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
    public MyFilterInvocationSecurityMetadataSource(){
        requestMap=new HashMap<RequestMatcher, Collection<ConfigAttribute>>();
        loadMetadataInfo();//将数据库中的资源和角色实体封装到requestMap里
    }
    private void loadMetadataInfo() {
        List<Resource> resources=...//TODO 获取数据库中所有的资源实体
        for(Resource res:resources){
            Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
            List<Role> roles=...//TODO 获取该资源对应的访问角色
            for(Role role:roles){
                allAttributes.add(new SecurityConfig(role.getRoleName()));
            }
            RequestMatcher key=new AntPathRequestMatcher(res.getUrl()+"/**");
            requestMap.put(key, allAttributes);
        }
    }
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
        List<Role> roles...//TODO 获取库中所有的角色实体
        for(Role role:roles){
            allAttributes.add(new SecurityConfig(role.getRoleName()));
        }
        return allAttributes;
    }
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
            if (entry.getKey().matches(request)) {
                return entry.getValue();
            }
        }
        return null;
    }
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}
2.修改Demo中相应的配置
<bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
      <property name="authenticationManager" ref="authenticationManager"/>
      <property name="accessDecisionManager" ref="accessDecisionManager"/>
      <property name="securityMetadataSource" ref="myFilterInvocationSecurityMetadataSource"/>
</bean>
<bean id="myFilterInvocationSecurityMetadataSource" class="com.youcompany.MyFilterInvocationSecurityMetadataSource"/>
三、修改accessDecisionManager中decisionVoters的实现逻辑
SpringSecurity默认使用AffirmativeBased来进行访问权限控制,该类封装了很多AccessDecisionVoter对象,基于投票的机制来决定访问是否通过
AccessDecisionVoter之间是OR的逻辑(只要有一个AccessDecisionVoter判断权限通过,用户便可访问界面)。
在Demo配置里,使用的是WebExpressionVoter基于表达式的权限认证逻辑(hasRole('admin')),而我们的需求是将用户的角色和访问资源需要的角色进行对比,来判断该用户是否具有访问界面的权限,因此需要进行以下修改:
1.声明一个Service实现org.springframework.security.access.AccessDecisionVoter接口
public class MyAccessDecisionVoter implements AccessDecisionVoter<Object> {
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }
    public boolean supports(Class<?> clazz) {
        return true;
    }
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        int result = ACCESS_DENIED;
        for (ConfigAttribute attribute : attributes) {//可访问该页面的角色
            for (GrantedAuthority authority : authentication.getAuthorities()) {//登录用户具备的角色
                 if (attribute.getAttribute().equals(authority.getAuthority())) {//判断用户是否具有相应角色
                     return ACCESS_GRANTED;
                 }
            }
        }
        return result;
    }
}
2.修改Demo中对应的配置
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <property name="decisionVoters">
        <list>
            <bean class="com.youcompany.MyAccessDecisionVoter"></bean>
        </list>
    </property>
</bean>
至此,SpringSecurity个性化定制修改完成。有点长,部分代码加了TODO,有不理解的可与我联系,需要源码的网友可留邮箱

作者:JavaMan_chen 发表于2013-4-2 15:55:07 原文链接
阅读:77 评论:0 查看评论

相关 [springsecurity 个性] 推荐:

SpringSecurity个性化配置

- - CSDN博客架构设计推荐文章
现有的数据库中包含以下几张表格用于权限管理. 要求在此基础上集成SpringSecurity,将表格的数据作为数据源来完成登录和权限校验逻辑. SpringSecurity的配置可通过两种方式呈现,基于自身的namespace配置和传统的基于Bean的配置. 通过namespace来配置Security非常简洁,隐藏了很多繁琐的实现细节,但也不便于初学者进行理解,而如果要想对Security进行个性化定制(替换现有功能实现),最好还是采用传统的基于Bean的方式进行配置,虽然结构复杂,但是细节清晰明了.

利用springsecurity做用户权限限制

- - CSDN博客推荐文章
不同的user(用户)需要不同的role(角色),不同的role(角色)又会需要不同的resource(资源权限),比如说我王二,是个管理员(admin),我的权限大到什么都能操作,包括新建一个代理(proxy),再比如说我弟弟王三,是个代理(proxy),他却不能新建代理,他权限不够大. 利用springsecurity来实现的话,就非常简便(当然了,必须先做点准备工作).

个性化搜索

- - CSDN博客云计算推荐文章
         随着大数据日益成为IT领域的主流,如何利用大数据为业务提供支持以及来扩展市场成为当今众多公司追逐的目标. 目前,比较热门的领域有两块:recommendation(推荐系统)和personalization search(个性化搜索).        这两者有着很大的关联性和相似性,都是在大数据的环境得到了充分的发展,特别是recommendation,在Netflix公司举办的一个比赛---奖金一百万美元.

风趣个性俏皮话

- 东东小屋 - 乐淘吧
1、钱不是没有,只不过你有的是正数,我有的是负数. 2、我戒酒了,咱们今天少喝点吧. 3、又在承诺,咋那么多谎言呀. 4、小弟:大哥,你的人缘真好,周围那么多人经常找你. 5、上联:为需求而生,为用户而死,为浏览器奋斗一辈子. 下联:吃CSS亏,上JS的当,最后死在兼容上. 6、北京一高校贴出告示,劝戒学生先不要谈恋爱,等你拿了诺贝尔奖以后,你现在交的女友很可能是你的丈母娘.

个性签名单车

- Hua - 设计|生活|发现新鲜
每个人都想拥有一件属于自己独一无二的东东. 譬如说,名字,当然不能重名(重名的孩子不要觉得悲催,说明你的名字比较fashion). 设计师Juri Zaech巧妙的用名字打造这一辆辆个性单车. 不过估计这创意只能用在英文名上,中文名你说说是肿么个做法. 「设计,生活,发现新鲜」在新浪微博,更即时地获读更新,更直接地交流沟通.

超经典的2011年个性签名

- gloomy - 乐淘吧
自己一定要在乎自尊,你的自尊在别人眼里一钱不值. 空欢喜就是早上醒来,以为自己长高了,仔细一看,原来是被子盖横了……. 愿你成为这样的女子:不炫耀,不争吵,做一个博学的女子;不空洞,不浮躁,做一个丰盈的女子;即便生命枯竭,亦在优雅中变老. 所谓“特困户”,其实共有两种——因缺钱而特困、因缺觉而特困.

个性化推荐产品研究

- rosa - 36氪
编者按:本文来自曾庆锐的投稿,点这里关注他的新浪微博,点这里查看@曾庆锐之前在36氪上的文章:微博应用的7种商机. 最近在微博上看到好几款做微博过滤的产品,跟两个月前自己的思路比较接近. 但通过最近一段时间的思考,有了一些新的发现和总结. 个性化推荐本来是一个技术性很强的话题,但本文重点仅仅讨论推荐过程中的“道理”,不涉及任何深邃难懂的技术.

关于个性化的产品

- 007 - 不周山
随着近年来互联网公司对个性化服务的关注程度在提高,开始有很多人从学术、从产品、从实现上研究推荐系统这个存在已久却颇为潮流的事物. 一个产品的成功,需要建立在管理人员、产品经理及技术人员对相关知识体系里某些基本概念的共识,及由此产生的默契,避开无谓的争执与误解. 这里,我要根据自己的理解,斗胆给一些模糊的概念下一个定义.

个性化推荐产品研究

- Holy Breeze - www.36kr.com
最近在微博上看到好几款做微博过滤的产品,跟两个月前自己的思路比较接近. 但通过最近一段时间的思考,有了一些新的发现和总结. 个性化推荐本来是一个技术性很强的话题,但本文重点仅仅讨论推荐过程中的“道理”,不涉及任何深邃难懂的技术. 本文的讨论主要以Zite和当前几款微博过滤产品基本特征作为参照. 为了避免不必要的争议,除新浪微博官方功能外,不指明其他具体产品名称.

个性化推荐系统综述

- Tony - 所有文章 - UCD大社区
上个月写过一篇产品推荐的文章,详情请见《我所了解的产品推荐》,内容很泛,多为工作心得. 本周读了几篇相关的论文,收获颇多,分享点干货. 以下内容摘自《个性化推荐系统的研究进展》,该文发表于2009年1月的《自然科学进展》专题评述,作者是刘建国、周涛、汪秉宏. 我略去了具体的算法和许多公式,重点看原理、思路和比较.