zoukankan      html  css  js  c++  java
  • Spring-Security (学习记录四)--配置权限过滤器,采用数据库方式获取权限

    完整代码见附件

    1. 需要在spring-security.xml中配置验证过滤器,来取代spring-security.xml的默认过滤器

    spring-security中需要配置验证过滤器来实现整个拦截的过程,其中需要配置一下三个来实现。

    authenticationManager:在配置用户名和角色的时候,已经配置过了。 security:authentication-manager
    accessDecisionManager:用来判断url是否有权限
    securityMetadataSource:可以通过url得到角色名称

    <!-- 配置 验证过滤器, 此过滤器取代系统的XML权限过滤 , 此过滤器配置完毕之后存放到 系统缺省的过滤链中-->
    <bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
        <!-- 需要认证管理器, 通过它可以获取 管理员已经拥有的角色信息  ,
            由于id已经被org.springframework.security.authenticationManager默认了。不能更改,所以用别名-->
        <property name="authenticationManager" ref="authenticationManager"></property>
        <!-- 决策器 -->
        <property name="accessDecisionManager" ref="roleAccessDecisionManager" />         
        <!-- 配置urlService ,security可以通过url得到角色名称 -->
         <property name="securityMetadataSource" ref="urlService" />
    </bean>  
    

    2. 配置securityMetadataSource,可以通过url来获取角色名称

    package com.hp.service.impl;
    
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.annotation.Resource;
    
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    import org.springframework.stereotype.Service;
    
    import com.hp.dao.UrlDao;
    import com.hp.model.Role;
    import com.hp.model.Url;
    import com.hp.service.UrlService;
    
    /**
     * 通过URL地址获取相应权限然后在获取相应的角色集合
     * 
     * 需要实现FilterInvocationSecurityMetadataSource接口
     * 
     * @author baojulin
     *
     */
    @Service("urlService")
    public class UrlServiceImpl implements UrlService, FilterInvocationSecurityMetadataSource {
    
    	@Resource
    	private UrlDao urlDao;
    
    	@Override
    	public Url getRoleByUrl(String url) {
    		return urlDao.getRoleByUrl(url);
    	}
    
    	/**
    	 * 此方法就是通过url地址获取 角色信息的方法
    	 */
    	@Override
    	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    		// 获取当前的URL地址
    		System.out.println("object的类型为:" + object.getClass());
    		FilterInvocation filterInvocation = (FilterInvocation) object;
    		String url = filterInvocation.getRequestUrl();
    		System.out.println("访问的URL地址为(包括参数):" + url);
    		url = filterInvocation.getRequest().getServletPath();
    		System.out.println("访问的URL地址为:" + url);
    		Url urlObject = getRoleByUrl(url); //调用自己实现的方法来url
    		System.out.println("urlObject:" + urlObject);
    		if (urlObject != null && urlObject.getPrivilege() != null) {
    			Set<Role> roles = urlObject.getPrivilege().getRoles();
    			Collection<ConfigAttribute> c = new HashSet<ConfigAttribute>();
    			c.addAll(roles);
    			return c; // 将privilege中的roles改为Collection<ConfigAttribute>,role需要实现ConfigAttribute接口
    		} else {
    			// 如果返回为null则说明此url地址不需要相应的角色就可以访问, 这样Security会放行
    			return null;
    		}
    	}
    
    	@Override
    	public Collection<ConfigAttribute> getAllConfigAttributes() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	/**
    	 * 如果为真则说明支持当前格式类型,才会到上面的 getAttributes 方法中
    	 */
    	@Override
    	public boolean supports(Class<?> clazz) {
    		// TODO Auto-generated method stub
    		// 需要返回true表示支持
    		return true;
    	}
    
    }
    
    

    urlDao中的实现

        /**
         * 通过URL地址获取相应权限然后在获取相应的角色集合
         */
        @Override
        public Url getRoleByUrl(String url) {
            String hql = "FROM Url u JOIN FETCH u.privilege up JOIN FETCH up.roles WHERE u.url=:url";
            Session session = sessionFactory.openSession();
            return (Url) session.createQuery(hql).setString("url", url).uniqueResult();
        }  
    

    3. 配置决策器:roleAccessDecisionManager

    package com.hp.service.impl;
    
    import java.util.Collection;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    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;
    import org.springframework.stereotype.Service;
    
    /**
     * 此类是决策器: 用来对 用户应有的角色,与URL地址可以访问的角色进行对比,如果不匹配则抛出异常
     * 
     * @author baojulin
     *
     */
    @Service("roleAccessDecisionManager")
    public class RoleAccessDecisionManager implements AccessDecisionManager {
    
    	protected final Log logger = LogFactory.getLog(RoleAccessDecisionManager.class);
    
    	/**
    	 * 决策方法: 如果方法执行完毕没有抛出异常,则说明可以放行, 否则抛出异常 AccessDeniedException
    	 */
    	@Override
    	public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
    		// System.out.println("---------------decide------------------");
    		// 如果登陆成功,则信息会存储在Authorities中
    		Collection<? extends GrantedAuthority> myRoles = authentication.getAuthorities();
    		// 如果前面的 getAttributes() 返回非空,则返回的数据做为形参传入, 如果返回为null 则不会进入decide() 直接放行
    
    		// System.out.println("myRole:" + myRoles);
    		// System.out.println("sysRole:" + configAttributes);
    
    		for (GrantedAuthority myRole : myRoles) {// 当前登录的角色
    			for (ConfigAttribute urlRoles : configAttributes) {// 前台传入的url的角色,UrlDaoImpl.getAttributes获得的
    				if (myRole.getAuthority().equals(urlRoles.getAttribute())) {
    					// 说明此URL地址符合权限,可以放行
    					return;
    				}
    			}
    		}
    		// System.out.println("-----权限验证失败------");
    		logger.error("-----权限验证失败------");
    		throw new AccessDeniedException("权限越界!");
    	}
    
    	/**
    	 * 返回true表示支持
    	 */
    	@Override
    	public boolean supports(ConfigAttribute attribute) {
    		// System.out.println("public boolean supports(ConfigAttribute attribute)");
    		return true;
    	}
    
    	/**
    	 * 返回true表示支持
    	 */
    	@Override
    	public boolean supports(Class<?> clazz) {
    		// System.out.println("public boolean supports(Class<?> clazz)");
    		return true;
    	}
    
    }
    
    

    4. 在配置文件中,修改默认过滤器,将xml方式配置的权限去掉,改用数据库

        <security:http auto-config="false" use-expressions="true" access-denied-page="/login.jsp?error=access-denied-page">
            <!-- xml配置,配置的 pattern="/admin/**" 表示需要登录才能访问,而登录的角色为ROLE_ADMIN
            <security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
            <security:intercept-url pattern="/user/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />    
             -->
        
            <!-- 增加权限过滤器,采用数据库方式获取权限 -->
            <security:custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>
            
            <!-- 默认登录地址:j_spring_security_check -->
            <security:form-login default-target-url="/index.jsp" 
                username-parameter="username"
                password-parameter="password"
                authentication-failure-url="/login.jsp?error=authentication-failure-url"
                login-page="/login.jsp"/>
            <!-- 注销也是由,Security框架来实现,LogoutFilter ,默认地址j_spring_security_logout   -->
             <security:logout logout-success-url="/login.jsp"/>
        </security:http>  
    

    5. 图解spring-security整个流程

    百度云链接:http://pan.baidu.com/s/1cBTdb4 密码:pjzk

  • 相关阅读:
    Java注解入门
    两种求素数
    几个经典的递归小程序
    Java8新特性——接口的默认方法和类方法
    SSH框架总结
    初识SSH框架
    Mybatis中DAO层接口没有写实现类,Mapper中的方法和DAO接口方法是怎么绑定到一起的
    使用SQL查询所有数据库名和表名
    mybatis中#{}和${}的区别
    SOCKET, TCP/UDP, HTTP, FTP 浅析
  • 原文地址:https://www.cnblogs.com/linhp/p/5817054.html
Copyright © 2011-2022 走看看