zoukankan      html  css  js  c++  java
  • SpringSecurity 3.2入门(9)自定义权限控制代码实现

    1、 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,我们的所有控制将在这三个类中实现 。

    package cn.jxufe.core.security;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.security.access.SecurityMetadataSource;
    import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
    import org.springframework.security.access.intercept.InterceptorStatusToken;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    
    public class MySecurityFilter extends AbstractSecurityInterceptor implements
            Filter {
        
        protected Logger logger = LoggerFactory.getLogger(getClass());
        
        // 与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应,
        // 其他的两个组件,已经在AbstractSecurityInterceptor定义
        private FilterInvocationSecurityMetadataSource securityMetadataSource;
    
        @Override
        public SecurityMetadataSource obtainSecurityMetadataSource() {
            return this.securityMetadataSource;
        }
    
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            logger.debug("------------MyFilterSecurityInterceptor.doFilter()-----------开始拦截了....");  
            FilterInvocation fi = new FilterInvocation(request, response, chain);
            invoke(fi);
        }
    
        private void invoke(FilterInvocation fi) throws IOException,
                ServletException {
            // object为FilterInvocation对象
            // 1.获取请求资源的权限 
            // 执行Collection<ConfigAttribute> attributes =
            // SecurityMetadataSource.getAttributes(object);
    
            logger.debug("--------------用户发送请求--------------");  
            InterceptorStatusToken token = null;
            token = super.beforeInvocation(fi);
            try {
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            }catch (Exception e) {
            }finally {
                super.afterInvocation(token, null);
            }
            
            logger.debug("------------MyFilterSecurityInterceptor.doFilter()-----------拦截结束了....");  
        }
    
        public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
            return securityMetadataSource;
        }
    
        public void setSecurityMetadataSource(
                FilterInvocationSecurityMetadataSource securityMetadataSource) {
            this.securityMetadataSource = securityMetadataSource;
        }
    
        public void init(FilterConfig arg0) throws ServletException {
            // TODO Auto-generated method stub
        }
    
        public void destroy() {
            // TODO Auto-generated method stub
        }
    
        public Class<? extends Object> getSecureObjectClass() {
            // 下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误
            return FilterInvocation.class;
        }
    }
    MySecurityFilter.java

    2、用于启动时加载资源列表,还拥有判断是否拥有请求访问资源权限的方法。

    package cn.jxufe.core.security;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.Map.Entry;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.access.SecurityConfig;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    import org.springframework.util.AntPathMatcher;
    
    import cn.jxufe.core.dao.BaseDao;
    import cn.jxufe.core.entiry.Resource;
    import cn.jxufe.core.entiry.Role;
    
    public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
        
        protected Logger logger = LoggerFactory.getLogger(getClass());
        
        private AntPathMatcher urlMatcher = new AntPathMatcher();  
        
        private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
          
        private BaseDao baseDao;
        
        public BaseDao getBaseDao() {
            return baseDao;
        }
    
        public void setBaseDao(BaseDao baseDao) {
            this.baseDao = baseDao;
        }
        
        //由spring调用
        public MyFilterInvocationSecurityMetadataSource(BaseDao baseDao) {
            this.baseDao=baseDao;
            loadResourceDefine();
        }
    
        //加载所有资源
        private void loadResourceDefine() {
            logger.debug("容器启动(MySecurityMetadataSource:loadResourceDefine)");
            logger.debug("--------------开始加载系统资源与权限列表数据--------------");  
               resourceMap = new HashMap<String,Collection<ConfigAttribute>>(); 
               String sql="select * from t_system_resource_info";
                    List<Resource> resources = this.baseDao.findListBeanByArray(sql, Resource.class);
                    for(Resource resource : resources){
                        Collection<ConfigAttribute> configAttributes = null;  
                        ConfigAttribute configAttribute = new SecurityConfig(resource.getName());  
                        if(resourceMap.containsKey(resource.getPath())){  
                            configAttributes = resourceMap.get(resource.getPath());  
                            configAttributes.add(configAttribute);  
                        }else{  
                            configAttributes = new ArrayList<ConfigAttribute>() ;  
                            configAttributes.add(configAttribute);  
                            resourceMap.put(resource.getPath(), configAttributes);  
                        }  
                    }  
                    logger.debug("--------------系统资源与权限列表数据加载结束--------------");  
        }
        
        /**
         *  根据请求的资源地址,获取它所拥有的权限 
         */
        public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
             //获取请求的url地址  
            String requestUrl = ((FilterInvocation) object).getRequestUrl();
            logger.debug("--------------取得请求资源所需权限(MySecurityMetadataSource:getAttributes)--------------");
            logger.debug("--------------请求地址为:"+requestUrl+"--------------");  
            Iterator<String> it = resourceMap.keySet().iterator();  
                while(it.hasNext()){  
                    String _url = it.next();  
                    if(_url.indexOf("?")!=-1){  
                        _url = _url.substring(0, _url.indexOf("?"));  
                    }  
                    if(urlMatcher.match(_url,requestUrl))  
                        return resourceMap.get(_url);  
                }
                return null; //如果是想做到没配的资源默认可以访问的话,那么就返回空或者NULL
                
                /**
                 * 使用下面的写法代替return null;没配的资源则不可以访问,建议开发的时候还是用上面的为好。
                 */
    //            Collection<ConfigAttribute> returnCollection = new ArrayList<ConfigAttribute>();
    //            returnCollection.add(new SecurityConfig("ROLE_NO_USER")); 
    //            return returnCollection;
        }
        
        public Collection<ConfigAttribute> getAllConfigAttributes() {
            Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
            for (Entry<String, Collection<ConfigAttribute>> entry : resourceMap.entrySet()) {
                allAttributes.addAll(entry.getValue());
            }
            logger.debug(allAttributes.toString());
            return allAttributes;
        }
        
        public Collection<ConfigAttribute> getConfigAttributes(String...value) {
            return SecurityConfig.createList(value);
        }
        
        public boolean supports(Class<?> clazz) {
            return true;
        }
    
    }
    MyFilterInvocationSecurityMetadataSource.java

    3、访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 ;做最终的访问控制决定。

    package cn.jxufe.core.security;
    
    import java.util.Collection;
    import java.util.Iterator;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.security.access.AccessDecisionManager;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.authentication.InsufficientAuthenticationException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    
    public class MyAccessDecisionManager implements AccessDecisionManager {
        
        protected Logger logger = LoggerFactory.getLogger(getClass());
        
        /** 
         *  认证用户是否具有权限访问该url地址 
         *  
         */  
        public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
            logger.debug("--------------匹配用户拥有权限和请求权限(MyAccessDecisionManager:decide)--------------");  
            logger.debug("--------------验证用户是否具有一定的权限--------------");  
             if(configAttributes == null) {
                return;
            }
            //所请求的资源拥有的权限(一个资源对多个权限)
            Iterator<ConfigAttribute> iterator = configAttributes.iterator();
            while(iterator.hasNext()) {
                ConfigAttribute configAttribute = iterator.next();
                //访问所请求资源所需要的权限
                String needPermission = configAttribute.getAttribute();
                logger.debug("--------------访问所请求资源所需要的权限为 " + needPermission+"--------------");  
                //用户所拥有的权限authentication
                for(GrantedAuthority ga : authentication.getAuthorities()) {
                    if(needPermission.equals(ga.getAuthority())) {
                        logger.debug("--------------权限验证通过--------------"); 
                        return;
                    }
                }
            }
            //没有权限让我们去捕捉
            logger.debug("--------------权限验证未通过--------------"); 
            throw new AccessDeniedException(" 没有权限访问!");
        }
    
        /** 
         * 启动时候被AbstractSecurityInterceptor调用,决定AccessDecisionManager是否可以执行传递ConfigAttribute。 
         */  
        public boolean supports(ConfigAttribute attribute) {
    //         System.out.println("MyAccessDescisionManager.supports()------------"+attribute.getAttribute());  
            return true;
        }
    
          /** 
         * 被安全拦截器实现调用,包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型 
         */  
        public boolean supports(Class<?> clazz) {
            logger.debug("MyAccessDescisionManager.supports()--------------------------------");  
            return true;
        }
        
    }
    MyAccessDecisionManager.java

    4、验证登录信息。

    package cn.jxufe.core.security;
    
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.security.authentication.AuthenticationServiceException;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    import cn.jxufe.core.dao.BaseDao;
    import cn.jxufe.core.entiry.User;
    
    public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
        private static final String USERNAME = "username";
        private static final String PASSWORD = "password";
        private static final String KAPTCHA = "kaptcha";
    
        private BaseDao baseDao;
    
        public BaseDao getBaseDao() {
            return baseDao;
        }
    
        public void setBaseDao(BaseDao baseDao) {
            this.baseDao = baseDao;
        }
    
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            if (!request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            }
    
            String username = obtainUsername(request);
            String password = obtainPassword(request);
            String kaptcha = obtainKaptcha(request);
            logger.debug("--------------用户:" + username +"正在登录---"+kaptcha+"-----------");  
            // 验证码校验
            String kaptchaExpected = (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
            if(!kaptcha.equals(kaptchaExpected)){
                BadCredentialsException exception = new BadCredentialsException("验证码不匹配!");
                throw exception;
            }
            
            // 账号与密码校验
            username = username.trim();
            String sql="select * from t_system_user_info where username=? and password=?";
            Object[] args=new Object[]{ username, password };
    
            User users = (User) this.baseDao.findUniqueBeanByArray(sql, User.class, args);
            if (users == null || !users.getPassword().equals(password)) {
                BadCredentialsException exception = new BadCredentialsException("用户名或密码不匹配!");
                throw exception;
            }
            // 实现 Authentication
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            // 允许子类设置详细属性
            setDetails(request, authRequest);
            // 运行UserDetailsService的loadUserByUsername 再次封装Authentication
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    
        @Override
        protected String obtainUsername(HttpServletRequest request) {
            Object obj = request.getParameter(USERNAME);
            return null == obj ? "" : obj.toString();
        }
    
        @Override
        protected String obtainPassword(HttpServletRequest request) {
            Object obj = request.getParameter(PASSWORD);
            return null == obj ? "" : obj.toString();
        }
        
        protected String obtainKaptcha(HttpServletRequest request) {
            Object obj = request.getParameter(KAPTCHA);
            return null == obj ? "" : obj.toString();
        }
        
        @Override
        protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
            super.setDetails(request, authRequest);
        }
    }
    MyUsernamePasswordAuthenticationFilter.java

    5、登录成功后,根据用户名,返回一个Userdetail。

    package cn.jxufe.core.security;
    
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    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 org.springframework.stereotype.Component;
    
    
    import cn.jxufe.core.dao.BaseDao;
    import cn.jxufe.core.entiry.Resource;
    
    @Component("userDetailsService")
    public class MyUserDetailsService implements UserDetailsService {
    
        protected Logger logger = LoggerFactory.getLogger(getClass());
        
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Autowired
        private BaseDao baseDao;
        
        //登录验证
        @Override
        public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException, DataAccessException {
            logger.debug("------------验证并授权(MyUserDetailsServiceImpl:loadUserByUsername)------------");
            
            // 根据用户名获取帐户和权限信息
            String sql = "SELECT username,password,enabled,name rname " +
                    "FROM t_system_user_info u,t_system_role_info r,t_system_user_role ur " +
                    "WHERE u.id=ur.user_id AND r.id=ur.role_id AND username= ?";
            // 如果一个用户具有多个权限,连接查询会返回一个List
            List list =this.jdbcTemplate.queryForList(sql, new Object[] { username });
    
            // 取出帐户和权限信息填充到User中返回
            if (list == null || list.size() <= 0)
                // spring-security定义的异常
                throw new UsernameNotFoundException("用户不存在!");
            // 如果用户存在
            Map<String, Object> map = (Map<String, Object>) list.get(0);
            // 密码
            String password = (String) map.get("password");
            // 帐户是否可用
            boolean enabled = ((Integer) map.get("enabled") == 1);
            boolean accountNonExpired = true;
            boolean credentialsNonExpired = true;
            boolean accountNonLocked = true;
            // 帐户所具有的权限
            
            Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
            List<Resource> resources=this.getResourceByUsername(username);
            logger.debug("------------用户所拥有权限------------");
            for (int i=0;i<resources.size();i++) {
                GrantedAuthority authority = new SimpleGrantedAuthority(resources.get(i).getName()); 
                authSet.add(authority);
            }
            logger.debug("------------"+authSet.toString()+"------------");
            // spring-security提供的类
            User userdetail = new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authSet);
            return userdetail;
    
        }
        
        private List<Resource> getResourceByUsername(String username){  
            String sql ="SELECT * FROM t_system_resource_info WHERE id IN(" +
                    "SELECT DISTINCT resource_id FROM t_system_authority_resource WHERE authority_id IN(" +
                    "SELECT authority_id FROM t_system_role_authority WHERE role_id IN(" +
                    "SELECT role_id FROM t_system_user_role WHERE user_id  =( " +
                    "SELECT id FROM t_system_user_info WHERE username= ? ))))";
            List<Resource> list =this.baseDao.findListBeanByArray(sql, Resource.class, new Object[] { username });
            return list;  
        }
        
    
    }
    MyUserDetailsService.java

    6、配置信息

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:security="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.2.xsd">
    
        <!-- 配置不过滤的资源(静态资源及登录相关) -->
        <security:http pattern="/**/*.css" security="none" />
        <security:http pattern="/**/*.js" security="none" />
        <security:http pattern="/**/*.jpg" security="none" />
        <security:http pattern="/**/*.jpeg" security="none" />
        <security:http pattern="/**/*.gif" security="none" />
        <security:http pattern="/**/*.png" security="none" />
        <security:http pattern="/favicon.ico" security="none" />
        <!-- 不过滤验证码 -->
        <security:http pattern="/captcha-image.htm" security="none" />
        <!-- 不过滤登录页面 -->
        <security:http pattern="/login.htm" security="none" />
        <security:http pattern="/login.jsp" security="none" />
        <!-- 不过滤首页 -->
        <security:http pattern="/index.htm" security="none" />
        <security:http pattern="/index.jsp" security="none" />
    
        <!-- 配置SpringSecurity的http安全服务 -->
        <!-- 使用了 use-expressions="true 则 需使用hasRole('ROLE_USER')-->
        <!-- 配置了auto-config="true"loginFilter报错,如果你没有自定义的登录页面,它就会跳转到security默认的登录页面中。 -->
        <security:http access-denied-page="/accessDenied.jsp" entry-point-ref="authenticationProcessingFilterEntryPoint">
            <security:session-management>
                <security:concurrency-control
                    max-sessions="1" />
            </security:session-management>
    
            <!-- 检测失效的sessionId,session超时时,定位到另外一个URL -->
            <security:session-management
                invalid-session-url="/sessionTimeOut.jsp" />
    
            <!-- 配置登出信息,指定退出系统后,跳转页面 -->
            <security:logout logout-url="/logout"
                logout-success-url="/login.htm" invalidate-session="true" />
            
            <!-- 认证和授权 -->
            <security:custom-filter ref="myLoginFilter" position="FORM_LOGIN_FILTER"  />
            <security:custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
            
        </security:http>
    
        <!-- 认证管理器,配置SpringSecutiry的权限信息 -->
        <security:authentication-manager>
            <security:authentication-provider>
                <!-- 使用数据库中的用户名和密码 -->
                <security:jdbc-user-service
                    data-source-ref="dataSource" />
            </security:authentication-provider>
        </security:authentication-manager>
    
        <!-- 验证配置 , 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
        <security:authentication-manager alias="myAuthenticationManager">
            <!-- 使用自己数据库中的用户和角色表,获取用户拥有的权限 -->
            <security:authentication-provider
                user-service-ref="myUserDetailsServiceImpl" />
        </security:authentication-manager>
        
        <!-- 登录验证器 -->
        <bean id="myLoginFilter"
            class="cn.jxufe.core.security.MyUsernamePasswordAuthenticationFilter">
            <!-- 处理登录 -->
            <property name="filterProcessesUrl" value="/j_spring_security_check"></property>
            <property name="usernameParameter" value="username"></property>
            <property name="passwordParameter" value="password"></property>
            <property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></property>
            <property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></property>
            <property name="authenticationManager" ref="myAuthenticationManager"></property>
            <property name="baseDao" ref="baseDao"></property>
        </bean>
        
        <bean id="loginLogAuthenticationSuccessHandler"
            class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
            <property name="defaultTargetUrl" value="/index.jsp"></property>
        </bean>
        <bean id="simpleUrlAuthenticationFailureHandler"
            class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
            <property name="defaultFailureUrl" value="/login.jsp"></property>
        </bean>
        
        
         <!-- 认证过滤器 -->
        <bean id="securityFilter" class="cn.jxufe.core.security.MySecurityFilter">
            <!-- 用户拥有的权限 -->
            <property name="authenticationManager" ref="myAuthenticationManager" />
            <!-- 用户是否拥有所请求资源的权限 -->
            <property name="accessDecisionManager" ref="myAccessDecisionManager" />
            <!-- 资源与权限对应关系 -->
            <property name="securityMetadataSource" ref="myFilterInvocationSecurityMetadataSource" />
        </bean>
        
        <bean id="myUserDetailsServiceImpl" class="cn.jxufe.core.security.MyUserDetailsService" />
        <bean id="myAccessDecisionManager" class="cn.jxufe.core.security.MyAccessDecisionManager"/>
        <bean id="myFilterInvocationSecurityMetadataSource" class="cn.jxufe.core.security.MyFilterInvocationSecurityMetadataSource">
            <constructor-arg name="baseDao" ref="baseDao" />    
        </bean>
    
        <bean id="baseDao" class="cn.jxufe.core.dao.BaseDaoImpl" />
        
        
        <!-- 定义上下文返回的消息的国际化 -->
        <bean id="messageSource"
            class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
            <property name="basename"
                value="classpath:org/springframework/seurity/messages_zh_CN" />
        </bean>
    
        <!-- 未登录的切入点 -->  
        <bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
            <property name="loginFormUrl" value="/login.jsp"/>  
        </bean>
        
    </beans>  
    View Code

    (详细的拦截过程将会在下一章中学习)。

  • 相关阅读:
    velocity语法
    使用VS2003创建WEB程序的时候出现"AutoMation服务器不能创建对象"错误
    ASP.NET 2.0 Tips:跨页提交
    Tip #1 – 创建、管理、应用样式表的强大工具(Visual Studio 2008)
    解决ASP.NET2.0和1.1在同一台电脑上不能并行的问题
    Tip #2 - 样式应用工具(style application toolbar)
    利用HttpModuler实现WEB程序同一时间只让一个用户实例登陆
    ASP.NET Tips: 获取插入记录的ID
    通过rsync远程增量备份数据
    array_merge() [function.arraymerge]: Argument #1 is not an array in ……错误的解决办法
  • 原文地址:https://www.cnblogs.com/hehaiyang/p/4286988.html
Copyright © 2011-2022 走看看