SpringSecurity个性化配置

应用场景

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

要求在此基础上集成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,有不理解的可与我联系,需要源码的网友可留邮箱

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
配置Spring Security可以通过以下步骤完成: 1. 添加Spring Security依赖:在项目的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 创建Security配置类:创建一个类,并使用`@Configuration`注解标记,同时继承`WebSecurityConfigurerAdapter`类。这个配置类将用于定义安全规则和自定义认证配置。 ```java @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { // 配置用户认证的方式,如基于数据库的认证 auth.inMemoryAuthentication() .withUser("admin").password("{noop}password").roles("ADMIN") .and() .withUser("user").password("{noop}password").roles("USER"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") // 配置特定URL需要特定角色访问 .antMatchers("/user/**").hasAnyRole("ADMIN", "USER") .anyRequest().authenticated() // 其他请求需要认证 .and() .formLogin() // 启用表单登录 .and() .httpBasic(); // 启用基本认证 } } ``` 在`configureGlobal`方法中,我们可以配置用户的认证方式,上述示例使用了内存中的用户认证方式。可以将其替换为其他方式,如基于数据库的认证。 在`configure`方法中,我们可以定义URL级别的安全规则。上述示例中,配置了`/admin/**`需要角色为`ADMIN`的用户访问,`/user/**`需要角色为`ADMIN`或者`USER`的用户访问。 3. 配置登录页面:可以在`application.properties`文件中配置登录页面的路径和相关属性,例如: ```properties spring.security.loginPage=/login spring.security.loginProcessingUrl=/perform_login spring.security.usernameParameter=username spring.security.passwordParameter=password ``` 以上配置示例中,登录页面的路径为`/login`,登录表单提交到的URL为`/perform_login`,用户名和密码对应的参数名分别是`username`和`password`。 这只是一个简单的Spring Security配置示例,你可以根据具体需求进行更多的配置个性化定制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值