`

shiro拦截url动态配置在数据库

 
阅读更多

核心思想:

1:对 org.apache.shiro.web.filter.mgt.DefaultFilterChainManager.filterChains 执行清空,它维护的是个map.否则原先的没有被覆盖的filterChains还会存在!

2:对org.apache.shiro.web.filter.PathMatchingFilter.appliedPaths执行清空,它维护的是个map,否则原先的没有被覆盖的filter还会存在!

3:使用org.apache.shiro.web.filter.mgt.DefaultFilterChainManager.createChain(String, String)进行filterChains与appliedPaths的覆盖

 

package com.capitalbio.soft.service.account;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.Filter;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.capitalbio.soft.core.cache.SysCache;
import com.capitalbio.soft.core.shiro.FilterChainDefinitionsLoader;
import com.capitalbio.soft.core.utils.encoding.EncodeUtils;
import com.capitalbio.soft.core.utils.exception.ExceptionUtils;
import com.capitalbio.soft.core.utils.reflection.ReflectionUtils;
import com.capitalbio.soft.core.utils.security.Digests;
import com.capitalbio.soft.core.utils.spring.SpringContextHolder;
import com.capitalbio.soft.entity.account.Authority;
import com.capitalbio.soft.entity.account.Role;
import com.capitalbio.soft.entity.account.User;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

@Component("shiroDbRealm")
public class ShiroDbRealm extends AuthorizingRealm {
	
	private static Logger logger = LoggerFactory.getLogger(ShiroDbRealm.class);
	@Autowired private UserService userService;

	/**
	 * 认证回调函数,登录时调用.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		User user = userService.getByLoginName(token.getUsername(),false);
		if (user != null) {
			if(user.isUserDisabled()) { // DisabledAccountException || AuthenticationInfo org.apache.shiro.realm.SimpleAccountRealm.doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
				throw new DisabledAccountException(); // throw new LockedAccountException("Account [" + user.getLoginName() + "] is locked.");
			}
			byte[] salt = EncodeUtils.decodeHex(user.getSalt());
			return new SimpleAuthenticationInfo(new ShiroUser(user), user.getPassword(), ByteSource.Util.bytes(salt), getName());
		} else {
			return null;
		}
	}

	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.在配有缓存的情况下,只加载一次.
	 */
	@Override
	public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
		User user = userService.getByLoginName(shiroUser.getLoginName(),true);
		
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

		Set<String> roleSet = Sets.newLinkedHashSet(); // 角色集合
		Set<String> authoritySet = Sets.newLinkedHashSet(); // 权限集合
		Set<Authority> menuSet = Sets.newLinkedHashSet(); // 菜单集合
		Map<String, List<Authority>> toolbars = Maps.newLinkedHashMap(); // 按钮集合
		Set<Authority> authFilters = Sets.newLinkedHashSet();
		
		if(null!=user && !user.isUserDisabled()) { // 判断是否用户是否禁用
			for(Role r : user.getRoleList()) {
				if(!r.isRoleDisabled()) { // 判断角色是否禁用
					if(StringUtils.isNotBlank(r.getCode())) roleSet.add(r.getCode());
					for(Authority a : r.getAuthorityList()) { // 判断权限是否禁用
						if(authFilters.add(a)) { // 过滤已经处理过的权限
							if(!a.isAuthorityDisabled()) {
								if(StringUtils.isNotBlank(a.getCode())) authoritySet.add(a.getCode()); // 增加权限编码
								if(1==a.getAuthorityType()) {
									menuSet.add(a); // 增加菜单
								}else if(2==a.getAuthorityType() && null!=a.getParent()) { // 遍历增加按钮权限
									String purl = a.getParent().getUrl();
									if(StringUtils.isNotBlank(purl)) {
										if(!toolbars.containsKey(purl)) {
											toolbars.put(purl, new ArrayList<Authority>()); 
										}
										toolbars.get(purl).add(a); 
									}
								}
							}
						}
					}
				}
			}
		}
		
		info.setRoles(roleSet); // 设置角色
		info.setStringPermissions(authoritySet); // 设置权限
		shiroUser.setAuthorities(buildSubMenu(null,Lists.newArrayList(menuSet.toArray(new Authority[]{})))); // 设置菜单
		shiroUser.setToolbars(toolbars); // 设置工具栏
		
		logger.debug("{}当前用户权限:{}{}",SysCache.newline,SysCache.newline,authoritySet.toString().substring(1).replace(", ", SysCache.newline));
		
		return info;
	}
	
	
	/**
	 * 更新用户授权信息缓存.
	 */
	public void clearCachedAuthorizationInfo(Object principal) {
		SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
		clearCachedAuthorizationInfo(principals);
	}

	/**
	 * 清除所有用户授权信息缓存.
	 */
	public void clearAllCachedAuthorizationInfo() {
		Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
		if (cache != null) {
			for (Object key : cache.keys()) {
				cache.remove(key);
			}
		}
	}
	
	
	/**
	 * 设定Password校验的Hash算法与迭代次数.
	 */
	@PostConstruct
	public void initCredentialsMatcher() {
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(HASH_ALGORITHM);
		matcher.setHashIterations(HASH_INTERATIONS);

		setCredentialsMatcher(matcher);
	}
 
	/**
	 * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息.
	 */
	public static class ShiroUser extends User implements Serializable {
		private static final long serialVersionUID = 1778589964992915025L;

		public ShiroUser() { }
		
		public ShiroUser(String loginName) {
			super(loginName);
		}
		
		public ShiroUser(User user) {
			super(user.getId(),user.getLoginName(), user.getName(), user.getEmail(), user.getNickname(), user.getPhone(), user.getDisabled(), user.getSex(), user.getCreateDate());
		}
		

		/**
		 * 本函数输出将作为默认的<shiro:principal/>输出.
		 */
		@Override
		public String toString() {
			return loginName;
		}

		/**
		 * 重载hashCode,只计算loginName;
		 */
		@Override
		public int hashCode() {
			return Objects.hashCode(loginName);
		}

		/**
		 * 重载equals,只计算loginName;
		 */
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			ShiroUser other = (ShiroUser) obj;
			if (loginName == null) {
				if (other.loginName != null)
					return false;
			} else if (!loginName.equals(other.loginName))
				return false;
			return true;
		}
	}
	
	public static final String HASH_ALGORITHM = "SHA-1"; // 使用的加密算法
	public static final int HASH_INTERATIONS = 1024; // 加密迭代次数
	private static final int SALT_SIZE = 8; // 加密盐的大小
	
	/**
	 * 设定安全的密码,生成随机的salt并经过1024次 sha-1 hash
	 */
	public static User entryptUserPassword(User user) {
		byte[] salt = Digests.generateSalt(SALT_SIZE);
		user.setSalt(EncodeUtils.encodeHex(salt));

		byte[] hashPassword = Digests.sha1(user.getPassword().getBytes(), salt, HASH_INTERATIONS);
		user.setPassword(EncodeUtils.encodeHex(hashPassword));
		return user;
	}
	
	/**
	 * 得到当前登录用户
	 * @return
	 */
	public static ShiroUser getLoginUser() {
		return (ShiroUser)SecurityUtils.getSubject().getPrincipal();
	}
	
	/**  
	 * 通过登录名移除权限缓存
	 * @param loginId
	 * @return boolean 
	 */
	public static boolean removeAuthCacheByLoginName(String loginName) {
		try {
			SpringContextHolder.getBean(ShiroDbRealm.class).clearCachedAuthorizationInfo(new ShiroUser(loginName));
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(ExceptionUtils.getStackTraceAsString(e));
			return false;
		}
	}
	
	/**
	 * 重新载入过滤器url
	 * @param chains eg:keys:/static/**, value:authc,user,roles[admin]
	 */
	@SuppressWarnings("unchecked")
	public static void reloadShiroFilterChains(Map<String, String> chains) throws Exception  {
		DefaultFilterChainManager filterManager = getFilterChainManager();
		
//		logger.debug(getFiltersDescription(filterManager)); // 此句可注释掉...
		for(Entry<String, Filter> filterEntry : filterManager.getFilters().entrySet()) {
			if(PathMatchingFilter.class.isInstance(filterEntry.getValue())) {
				PathMatchingFilter filter = PathMatchingFilter.class.cast(filterEntry.getValue());
				Map<String, Object> appliedPaths = (Map<String, Object>)ReflectionUtils.getFieldValue(filter, "appliedPaths");
				synchronized (appliedPaths) { 
					appliedPaths.clear();
				}
			}
		}
		synchronized (filterManager.getFilterChains()) {
//				filterManager.getFilters().clear();
			logger.debug(""+filterManager.getFilterConfig());
			filterManager.getFilterChains().clear();
			for(Entry<String,String> chain : chains.entrySet()){
				filterManager.createChain(chain.getKey(), chain.getValue());
//				logger.debug("{} = {}",chain.getKey(),chain.getValue());
			}
		}
		logger.debug(getFiltersDescription(filterManager)); // 此句可注释掉...
	}

	/**
	 * 描述当前过滤器
	 */
	@SuppressWarnings("unchecked")
	public static String getFiltersDescription(DefaultFilterChainManager filterManager) {
		StringBuilder filtersDesc = new StringBuilder();
		for(Entry<String, Filter> filterEntryDesc : filterManager.getFilters().entrySet()) {
			filtersDesc.append(SysCache.newline).append(filterEntryDesc.getKey()).append(":").append(SysCache.newline);
			if(PathMatchingFilter.class.isInstance(filterEntryDesc.getValue())) {
				PathMatchingFilter filter = PathMatchingFilter.class.cast(filterEntryDesc.getValue());
				Map<String, Object> appliedPaths = (Map<String, Object>)ReflectionUtils.getFieldValue(filter, "appliedPaths");
				for(Entry<String, Object> paths : appliedPaths.entrySet()) {
					filtersDesc.append(paths.getKey()).append(" = ").append(Arrays.toString((String[])paths.getValue())).append(SysCache.newline);
				}
			}
		}
		return filtersDesc.toString();
	}
	
	/**
	 * 重新载入过滤器url
	 */
	public static void reloadShiroFilterChains() throws Exception {
		Map<String, String> allDefinitionMap = SpringContextHolder.getBean(FilterChainDefinitionsLoader.class).loadAllDefinitionMap();
		reloadShiroFilterChains(allDefinitionMap);
	}
	
	/**
	 * 将菜单组织为树形的结构
	 * 级联取出下级菜单(递归算法) 
	 */
	public static List<Authority> buildSubMenu(Authority parent,List<Authority> list) {
		List<Authority> subNodes = Lists.newLinkedList();
		for(Authority s : list) {
			if((null==parent && null==s.getParent()) || (null!=parent && null!=s.getParent() && parent.getId().equals(s.getParent().getId()))) {
				s.setSubnodes(buildSubMenu(s, list));
				subNodes.add(s); // logger.debug((null!=parent? parent.getName() : "") + " > " + s.getName());
			}
		}
		Authority.sort(subNodes,false);
		return subNodes;
	}
	
	/**
	 * @return 得到过滤器链
	 */
	public static DefaultFilterChainManager getFilterChainManager() {
		return ((DefaultFilterChainManager)((PathMatchingFilterChainResolver)SpringContextHolder.getBean(AbstractShiroFilter.class).getFilterChainResolver()).getFilterChainManager());
	}
	
	/**
	 *  解决在登录时无法加载菜单项的问题,即在每次登录时重新加载用户权限缓存
	 */
	public static void forceShiroToReloadUserAuthorityCache(String username) {
		// SpringContextHolder.getBean(ShiroDbRealm.class).doGetAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
		ShiroDbRealm shiroDbRealm = SpringContextHolder.getBean(ShiroDbRealm.class);
		
		shiroDbRealm.clearCachedAuthorizationInfo(new ShiroUser(username)); // 清除权限缓存
		shiroDbRealm.isPermitted(SecurityUtils.getSubject().getPrincipals(), "强制shiro检查加载用户权限缓存,避免懒加载!" + System.currentTimeMillis());
	}
	
}

 

 

 

想法来自于: http://www.infoq.com/cn/articles/apache-shiro

 

    1. [urls]
      /assets/** = anon
      /user/signup = anon
      /user/** = user
      /rpc/rest/** = perms[rpc:invoke], authc
      /** = authc

      上面权限配置,我想放到数据库里面?Shiro好像没有扩展?我以为用spring security都有。
      难道大家没有这个需求?以前好像听白衣说没有必要,各位老大,能描述一下为什么吗?

 

    1. 返回顶部

      Re: 权限配置想放到数据库

      26/09/2012 10:45 发表人 yang cai

      求高手回答一下吧

 

  1. 返回顶部

    Re: 权限配置想放到数据库

    04/12/2012 08:36 发表人 Lingwei Hu

    写一个读filterDefinitions的方法,然后在spring配置中:
    <property name="filterChainDefinitions" value="#{filterChainDefinitionsLoader.loadDefinitions()}"/>
    如果要在运行时重新加载filterDefinitions的话可以这样:
    从springContext中拿到shiroFilter.PathMatchingFilterChainResolver.DefaultFilterChainManager, 把filterChainManager中的filterChains清空,再调用filterChainManager.createChain来重新生成,注意对filterChains加同步。

分享到:
评论
1 楼 xiaoliu128 2014-01-26  
汗。写了一大堆,没看出来是怎么数据库配置权限

相关推荐

    spring boot整合shiro实现url请求过滤

    本demo为Spring boot整合shiro,以mybatis plus做dao层交互数据,实现了读取数据库用户数据实现用户登录,权限认证,读取数据库中用户对应的url请求,实现请求的过滤。自定义了relam和过滤器来实现这些功能

    SpringBoot 集成 Shiro 实现动态uri权限

    1. **配置Shiro**: 在SpringBoot项目中引入Shiro依赖,并配置Shiro的Realm,用于处理认证和授权。 Realm是Shiro与应用程序的特定安全存储(如数据库)的桥梁。 2. **定义权限模型**: 设计权限、角色和用户的模型,...

    apache_shiro_管理用户权限与数据库交互

    在 `web.xml` 中,我们需要配置 Shiro 的过滤器 `ShiroFilter`,它会拦截所有的请求并进行权限检查: ```xml &lt;filter-name&gt;ShiroFilter &lt;filter-class&gt;org.apache.shiro.web.servlet.IniShiroFilter ...

    jfinal整合shiro权限控制(从数据库读取配置信息)

    通过这个工厂,我们可以根据JFinal的路由或URL来动态配置Shiro的过滤器,实现对不同URL的权限控制。 2. **JfinalShiroFilter.java**:这是JFinal与Shiro整合的一个自定义过滤器。该过滤器可能负责拦截请求,执行...

    shiro登录拦截校验demo

    - **Web.xml配置**:设置Shiro Filter链,指定哪些URL需要拦截和如何处理。 4. **登录验证流程**: - 用户提交登录请求,携带用户名和密码。 - Shiro的Filter拦截请求,通过`Subject.login()`方法进行身份验证。...

    SpringBoot+Shiro+JWT+Jedis+MybatisPlus+前后端分离+基于url通用权限管理系统

    Shiro支持基于URL的权限拦截,这意味着可以为每个URL分配特定的访问权限,只有拥有相应权限的用户才能访问。这种方式提高了权限控制的粒度,使权限管理更为精细化。 JWT(JSON Web Token)是一种轻量级的身份验证...

    Apache shiro权限控制基础配置代码

    在本文中,我们将深入探讨Apache Shiro的基础配置和代码实现,以帮助你理解如何有效地使用Shiro进行权限控制。 **1. Shiro架构组件** Shiro的核心组件包括:Subject、Realms、Caches、Session Manager、...

    maven+springboot+jpa+shiro权限管理内有数据库sql

    在本项目中,Shiro用于实现用户登录验证、角色权限分配、URL拦截等功能,确保只有经过授权的用户才能访问特定的资源。开发者可以自定义安全策略,实现细粒度的权限控制。 数据库SQL脚本的提供,意味着项目已经包含...

    springmvc+shiro配置教程

    在Web.xml文件中,我们需要定义Shiro的过滤器,以便拦截所有的请求。以下是一个基本的Web.xml配置文件: ```xml &lt;filter-name&gt;shiroFilter &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy...

    管理系统系列--使用SpringBoot与shiro实现基于数据库的细粒度动态权限管理系统实例.zip

    在本教程中,我们将深入探讨如何构建一个基于SpringBoot和Shiro的细粒度动态权限管理系统。SpringBoot作为现代Java应用程序开发的首选框架,简化了配置和依赖管理,而Shiro则是一个强大的安全管理框架,专注于身份...

    struts2和shiro完美整合解决方案

    4. **集成拦截器**:使用Struts2的拦截器机制,添加Shiro的`authc`拦截器,该拦截器会在每个Action执行前检查用户是否已登录。 5. **权限控制**:在Action或Action方法上添加注解,指定需要的权限,Shiro会根据这些...

    ssm集成redis和shiro

    3. **SSM拦截器与Shiro结合**: 在SpringMVC中配置拦截器,通过Shiro的SecurityUtils.getSubject()进行认证和授权检查。 在实际项目中,可能还需要考虑安全性、性能优化、异常处理等问题。集成完成后,你可以享受到...

    shiro整合ssm的全部jar包

    4. **Web层配置**:在Spring MVC的配置中,添加Shiro的过滤器链,配置对应的URL拦截规则。 5. **启动Shiro**:在应用启动时,初始化Shiro的SecurityManager,确保其正常运行。 6. **业务代码整合**:在需要进行...

    SpringBoot集成Shiro、Jwt和Redis

    Shiro通过配置拦截器,可以对URL进行访问控制,实现权限的动态分配。 **Jwt** 是一种开放标准(RFC 7519),定义了一种紧凑的、自包含的方式来安全地在各方之间传输信息作为一个 JSON 对象。这个信息可以被验证和...

    shiro使用简单Demo

    在这个"shiro使用简单Demo"中,我们可以看到作者提供了一个基础的Shiro实现,特别针对URL和注解的权限管理进行演示,这对于初学者来说是一个很好的起点。 首先,我们来看`shiro2.xml`,这是Shiro的配置文件。在该...

    spring shiro项目

    项目中提到的"可以通过DB控制地址拦截"意味着我们可以动态地在数据库中维护权限规则,而不是硬编码在代码中。这样,管理员可以在不修改代码的情况下,通过后台系统修改权限策略,提高了系统的灵活性。 6. **Spring...

    shirodemo整合spring+springmvc+shiro

    3. **编写Shiro配置**:在Spring的配置文件(如`applicationContext.xml`或`applicationContext-shiro.xml`)中,定义Shiro的FilterChainDefinitionSource,配置Shiro过滤器链,指定哪些URL需要经过哪些过滤器处理。...

    jfinal+shiro实例

    4. **Shiro拦截器**:在JFinal中,我们需要创建一个Shiro拦截器,用于在每个请求之前进行权限检查。如果用户未登录或者无权访问某些资源,拦截器将阻止请求并返回相应的错误信息。 5. **会话管理**:Shiro提供了...

    shiro整合ssm框架

    注意,实际应用中可能还需要根据业务需求调整Shiro的配置和逻辑,例如自定义拦截器、实现动态权限分配等。 整合Shiro到SSM框架,不仅简化了安全管理的代码,也提高了代码的可维护性和可扩展性。通过Shiro的丰富功能...

    spring boot shiro demo项目

    Shiro Filter Chain 配置决定了哪些URL需要经过哪些Filter处理。 - 编写登录和登出接口,调用Shiro提供的API进行操作。 - 如果使用Web环境,可以配置Shiro的Web支持,包括`ShiroFilterFactoryBean`等。 3. **...

Global site tag (gtag.js) - Google Analytics