SpringSecurity权限管理

登录拦截

流程:
用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。

Created with Raphaël 2.1.0 login login AuthenticationProcessingFilter AuthenticationProcessingFilter AuthenticationManager AuthenticationManager ProviderManager ProviderManager User(包含权限) User(包含权限) 登陆过滤 被实现 调用 返回用户验证信息 验证通过

ProvideManager 根据用户名返回对应的角色权限信息
配置方式:

<authentication-manager alias="authenticationManager">  
  <authentication-provider user-service-ref="myUserDetailService">  
      <!--如果用户的密码采用加密的话 <password-encoder hash="md5" /> -->  
  </authentication-provider>  
</authentication-manager>  

代码:

/**
 * @author PL
 * 验证配置,认证管理器,实现用户认证的入口
 */
public class MyUserDetailService implements UserDetailsService {
    /**
     *  登陆验证时,通过username获取用户的所有权限信息,  
     *  并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用  
     */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>();   
        //GrantedAuthorityImpl auth2=new GrantedAuthorityImpl("ROLE_ADMIN");   
        GrantedAuthorityImpl auth1=new GrantedAuthorityImpl("ROLE_USER");   
        if(username.equals("admin")){   
            auths=new ArrayList<GrantedAuthority>();   
            auths.add(auth1);  
          //  auths.add(auth2);        
        }       
        User user = new User(username, "admin", true, true, true, true, auths);  
        System.out.println("--------UserDetailService:"+JSONObject.toJSONString(user));
        return user;    
    }

登录资源访问拦截

流程:
访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。

加载所有URL对应的角色:

Created with Raphaël 2.1.0 AbstractSecurityInterceptor AbstractSecurityInterceptor securityMetadataSource securityMetadataSource AccessDecisionManager授权管理器 AccessDecisionManager授权管理器 beforeInvocation()调用 加载所有URL和权限,通过getAttibbte()获取 afterInvocation(token, null)调用;

授权管理:

Created with Raphaël 2.1.0 AbstractSecurityInterceptor AbstractSecurityInterceptor AccessDecisionManager AccessDecisionManager SecurityContextHolder(spring全局缓存) SecurityContextHolder(spring全局缓存) afterInvocation(token, null)调用; 用户信息 decide()表决,没通过抛出异常

完整代码:

SpringSecurity.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security.xsd">

    <!-- 配置不过滤的资源(静态资源及登录相关) -->
    <http pattern="/**/*.css" security="none"></http>
    <http pattern="/**/*.jpg" security="none"></http>
    <http pattern="/**/*.jpeg" security="none"></http>
    <http pattern="/**/*.gif" security="none"></http>
    <http pattern="/**/*.png" security="none"></http>
    <http pattern="/js/*.js" security="none"></http>
    <http pattern="/login.jsp" security="none"></http>
    <http pattern="/getCode" security="none" /><!-- 不过滤验证码 -->
    <http pattern="/test/**" security="none"></http><!-- 不过滤测试内容 -->

    <http auto-config='true'  >
        <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <intercept-url pattern="/main.jsp" access="ROLE_ADMIN" />
        <intercept-url pattern="/**" access="ROLE_USER" />
        <form-login login-page="/login.jsp" />
        <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR" />
    </http>

    <!--一个自定义的filter,必须包含 authenticationManager,accessDecisionManager,securityMetadataSource三个属性, 
        我们的所有控制将在这三个类中实现,解释详见具体配置 -->
    <beans:bean id="myFilter"
        class="cn.panlei.springsecurity.service.MyFilterSecurityInterceptor">
        <beans:property name="authenticationManager" ref="authenticationManager" />
        <beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
        <beans:property name="securityMetadataSource" ref="securityMetadataSource" />
    </beans:bean>

    <!--验证配置,认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->  
    <authentication-manager alias="authenticationManager">  
        <authentication-provider user-service-ref="myUserDetailService">  
            <!--如果用户的密码采用加密的话 <password-encoder hash="md5" /> -->  
        </authentication-provider>  
    </authentication-manager>  
    <!--在这个类中,你就可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等 -->  
    <beans:bean id="myUserDetailService" class="cn.panlei.springsecurity.service.MyUserDetailService" />  
    <!--访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->  
    <beans:bean id="myAccessDecisionManagerBean" 
        class="cn.panlei.springsecurity.service.MyAccessDecisionManager">  
    </beans:bean>  
    <!--资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->  
    <beans:bean id="securityMetadataSource" 
        class="cn.panlei.springsecurity.service.MyInvocationSecurityMetadataSource" />   
</beans:beans>

MyUserDetailService.java

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.alibaba.fastjson.JSONObject;
/**
 * @author PL
 * 验证配置,认证管理器,实现用户认证的入口
 */
public class MyUserDetailService implements UserDetailsService {
    /**
     *  登陆验证时,通过username获取用户的所有权限信息,  
     *  并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用  
     */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>();   
        //GrantedAuthorityImpl auth2=new GrantedAuthorityImpl("ROLE_ADMIN");   
        GrantedAuthorityImpl auth1=new GrantedAuthorityImpl("ROLE_USER");   
        if(username.equals("admin")){   
            auths=new ArrayList<GrantedAuthority>();   
            auths.add(auth1);  
          //  auths.add(auth2);        
        }       
        User user = new User(username, "admin", true, true, true, true, auths);  
        System.out.println("--------UserDetailService:"+JSONObject.toJSONString(user));
        return user;    
    }
}

MyFilterSecurityInterceptor.java

/**
 * 
 * @author PL
 *
 *  资源访问是进行拦截
 */
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

    // 配置文件注入
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    // 登陆后,每次访问资源都通过这个拦截器拦截
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public Class<? extends Object> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        // fi里面有一个被拦截的url
        // 里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
        // object)这个方法获取fi对应的所有权限
        // 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            // 执行下一个拦截器
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }

    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
        this.securityMetadataSource = newSource;
    }

    public void destroy() {
    }

    public void init(FilterConfig arg0) throws ServletException {
    }
}

MyAccessDecisionManager .java 表决器

public class MyAccessDecisionManager implements AccessDecisionManager {

    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        System.out.println("进入AccessDecisionManager:"+authentication);
        if (configAttributes == null) {
            return;
        }

        Iterator<ConfigAttribute> ite = configAttributes.iterator();
        while (ite.hasNext()) {
            ConfigAttribute ca = ite.next();
            String needRole = ((SecurityConfig) ca).getAttribute();
            for (GrantedAuthority ga : authentication.getAuthorities()) {
                if (needRole.equals(ga.getAuthority())) {

                    return;
                }
            }
        }
        // 注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面
        throw new AccessDeniedException("no right");
    }

    public boolean supports(ConfigAttribute attribute) {
        // TODO Auto-generated method stub
        return true;
    }

    public boolean supports(Class<?> clazz) {
        // TODO Auto-generated method stub
        return true;
    }

}

MyInvocationSecurityMetadataSource.java

/**
 * 
 * @author PL
 * 
 *  资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问
 *
 */
public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    protected final Log logger = LogFactory.getLog(getClass());
    //将所有的角色和url的对应关系缓存起来  
    //private static List<RoleUrlResource> rus = null;  
    private UrlMatcher urlMatcher = new AntUrlPathMatcher();
    private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

    // tomcat启动时实例化一次
    public MyInvocationSecurityMetadataSource() {
        loadResourceDefine();
    }

    // tomcat开启时加载一次,加载所有url和权限(或角色)的对应关系
    private void loadResourceDefine() {
        resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
        Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
        ConfigAttribute ca = new SecurityConfig("ROLE_ADMIN");
        atts.add(ca);
        resourceMap.put("/index.jsp", atts);
        Collection<ConfigAttribute> attsno = new ArrayList<ConfigAttribute>();
        ConfigAttribute cano = new SecurityConfig("ROLE_NO");
        attsno.add(cano);
        resourceMap.put("/other.jsp", attsno);
        System.out.println("---------FilterInvocationSecurityMetadataSource"+JSONObject.toJSONString(resourceMap));
    }

    // 参数是要访问的url,返回这个url对于的所有权限(或角色)
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

        /*String url = ((FilterInvocation) object).getRequestUrl();

        // 查询所有的url和角色的对应关系
        if (rus == null) {
            rus = roleUrlDao.findAll();
        }

        // 匹配所有的url,并对角色去重
        Set<String> roles = new HashSet<String>();
        for (RoleUrlResource ru : rus) {
            if (urlMatcher.pathMatchesUrl(ru.getUrlResource().getUrl(), url)) {
                roles.add(ru.getRole().getRoleName());
            }
        }
        Collection<ConfigAttribute> cas = new ArrayList<ConfigAttribute>();
        for (String role : roles) {
            ConfigAttribute ca = new SecurityConfig(role);
            cas.add(ca);
        }
        return cas;*/

        // 将参数转为url
        //logger.info("getAttributes"+JSONObject.toJSONString(object));
        // 将参数转为url      
        String url = ((FilterInvocation)object).getRequestUrl();     
        Iterator<String>ite = resourceMap.keySet().iterator();   
        while (ite.hasNext()) {           
            String resURL = ite.next();    
            if (urlMatcher.pathMatchesUrl(resURL, url)) {   
                return resourceMap.get(resURL);           
                }         
            }   
        return null;
    }

    public boolean supports(Class<?> clazz) {
        return true;
    }

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

}

UrlMatcher.java

public interface UrlMatcher {
    Object compile(String paramString);  
    boolean pathMatchesUrl(Object paramObject, String paramString);  
    String getUniversalMatchPattern();   
    boolean requiresLowerCaseUrl();  
}

获取用户输入的密码:UsernamePasswordAuthenticationFilter

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值