shiro安全框架扩展教程--如何动态控制页面节点元素的权限

简介:          上些章节我们都学习了shiro中的各种实际应用技巧,今天我想讲的是如何动态控制页面节点权限,相信这个控制对于很多玩权限的人来说都是一个比较头痛的, 因为这实在不怎么好统...

         上些章节我们都学习了shiro中的各种实际应用技巧,今天我想讲的是如何动态控制页面节点权限,相信这个控制对于很多玩权限的人来说都是一个比较头痛的,

因为这实在不怎么好统一控制的,现在我来展示下我通过shiro是如何实现的,算是抛砖引玉,希望大家有更好的解决方案,可以相互学习;下面就直接进入主题不啰嗦了


之前我们已经学习了如何在项目启动的时候加载所有的资源角色,包括第三方资源,下面我再帖上加长加粗版的代码方便说明


package com.silvery.security.shiro.service.impl;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.shiro.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;

import com.silvery.project.cms.model.Authority;
import com.silvery.project.cms.model.Permission;
import com.silvery.project.cms.service.PermissionService;
import com.silvery.project.cms.vo.PermissionVo;
import com.silvery.security.shiro.cache.SimpleMapCache;
import com.silvery.security.shiro.cache.extend.SimpleCacheManager;
import com.silvery.security.variable.Const;

/**
 * 
 * 加载第三方角色资源配置服务类
 * 
 * @author shadow
 * 
 */
public class SimpleFilterChainDefinitionsService extends AbstractFilterChainDefinitionsService {

	@Autowired
	private SimpleCacheManager simpleCacheManager;

	@Autowired
	private PermissionService permissionService;

	@Override
	public Map<String, String> initOtherPermission() {
		return converResultMap(initOperation());
	}

	@SuppressWarnings("unchecked")
	private Map<Object, Object> initOperation() {

		Map<Object, Object> resultMap = new HashMap<Object, Object>();

		// 加载数据库所有资源
		PermissionVo vo = new PermissionVo();
		List<Permission> permissions = (List<Permission>) permissionService.query(vo).getValue();

		List<Authority> authorities = null;

		for (Permission permission : permissions) {

			// 遍历查询当前资源的配置角色
			vo.setId(permission.getId());
			authorities = (List<Authority>) permissionService.query4authority(vo).getValue();

			// 组装角色集合
			Set<String> authoritySet = getPermissionSet(authorities);

			if (authoritySet.isEmpty()) {
				continue;
			}
			if (permission.getType() == 1) {
				// 请求路径资源处理
				resultMap.put(permission.getContent(), MessageFormat.format(SHIRO_AUTHORITY_FORMAT, authoritySet));
			} else {
				// 元素资源处理
				Map<Object, Object> map = new HashMap<Object, Object>(1);
				map.put(Const.OTHER_PERMISSSION_CACHE_NAME, authoritySet);
				Cache<Object, Object> cache = new SimpleMapCache(Const.OTHER_PERMISSSION_CACHE_NAME, map);
				simpleCacheManager.createCache(Const.OTHER_PERMISSSION_CACHE_NAME + "_" + permission.getId(), cache);
			}
		}

		return resultMap;
	}

	/** 获取角色名称集合 */
	private Set<String> getPermissionSet(List<Authority> authorities) {
		Set<String> authoritieSet = new HashSet<String>(authorities.size());
		for (Authority authority : authorities) {
			authoritieSet.add(authority.getContent());
		}
		return authoritieSet;
	}

	/** 泛型Object转换String */
	private Map<String, String> converResultMap(Map<Object, Object> map) {
		Map<String, String> resultMap = new HashMap<String, String>(map.size());
		for (Map.Entry<Object, Object> entry : map.entrySet()) {
			resultMap.put(entry.getKey().toString(), entry.getValue().toString());
		}
		return resultMap;
	}

}

1. 加载所有资源,包括请求URL,元素节点等数据,我这里为了演示,没有分那么细就只有两种

2. 请求URL的直接放到框架中,形式如/user/list.do*=role[root,user],跟我们shiro.xml配置的一样

3. 元素节点则相应放到以键值对的形式放到缓存,以节点的编号组合成key保证可以找到这个缓存对,值是相应的角色集合


我们的缓存就存在各个资源所需要的角色集合, 加载数据步骤已经完毕了下面看看我们是怎么应用的


首先我是使用springMVC+freemarker作为展现层,具体怎么配置整合我也不说,百度一堆,直接看我帖代码说明


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>欢迎主页</title>
</head>
<body>
	${username!"游客"}, 欢迎您的访问! <br />
	<hr />
	可选操作:
	<#if username??>
		<@sec id="2" body="<a href='/cms/user/page.do'>用户列表</a> " /><a href="/cms/authority/page.do">权限列表</a> <a href="/cms/permission/page.do">资源列表</a> <a href="/cms/logout.do">注销退出</a> 
	<#else>
		<a href="#" onclick="top.location.href='/cms/logout.do';">前往登录</a> 
	</#if>
	<hr />
</body>
</html>

很明显看到我的页面有一个@sec的标签,然后有两个参数,一个id,一个是body,至于id则是你资源的编号,body是需要元素节点内容


然后我们怎么通过这个标签来判定是否在页面渲染body的节点内容呢?


下面我们看看这个@sec的实现


package com.silvery.core.freemarker;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

import com.silvery.security.shiro.cache.extend.SimpleCacheManager;
import com.silvery.security.variable.Const;

import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;

/**
 * 
 * FreeMarker自定义标签,节点权限控制
 * 
 * @author shadow
 * 
 */
public class SecurityTag implements TemplateDirectiveModel {

	@Autowired
	private SimpleCacheManager simpleCacheManager;

	@SuppressWarnings("unchecked")
	public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody directiveBody)
			throws TemplateException, IOException {

		Object id = params.get("id");
		Object body = params.get("body");

		validate(id, body);

		if (hasRole(id)) {
			env.getOut().write(body.toString());
		} else {
			env.getOut().write("");
		}

	}

	private void validate(Object id, Object body) throws TemplateException {
		if (id == null || id.toString().trim().equals("")) {
			throw new TemplateException("参数[id]不能为空", null);
		}
		if (body == null) {
			throw new TemplateException("参数[body]不能为空", null);
		}
	}

	@SuppressWarnings("unchecked")
	private boolean hasRole(Object id) {
		Cache<Object, Object> cache = simpleCacheManager.getCache(Const.OTHER_PERMISSSION_CACHE_NAME + "_" + id);
		if (cache == null) {
			return false;
		} else {
			Object obj = cache.get(Const.OTHER_PERMISSSION_CACHE_NAME);
			if (obj == null) {
				return false;
			}
			Set<String> authoritySet = (Set<String>) obj;
			Subject subject = SecurityUtils.getSubject();
			for (String authority : authoritySet) {
				if (subject.hasRole(authority)) {
					return true;
				}
			}
		}
		return false;
	}

}

很清晰地看到,我们是用到之前的那个资源角色缓存,通过判定缓存中存在的角色与当前shiro认证用户拥有的角色匹配,如存在任意相同角色则渲染相应节点


如Subject拥有角色[root,user],而x编号资源拥有[user,test]角色,很明显有交集会认证通过,反之则直接渲染空字符


大概流程就是这样,有的人可能会问,如何配置这个tag实现类呢?我帖下spring-mvc.xml的freemarker配置


<!-- FreeMarker配置 -->
	<bean id="freemarkerConfig"
		class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
		<property name="templateLoaderPath" value="/WEB-INF/ftl/" />
		<property name="defaultEncoding" value="UTF-8" />
		<property name="freemarkerVariables">
			<map>
				<entry key="sec">
					<bean class="com.silvery.core.freemarker.SecurityTag">
					</bean>
				</entry>
			</map>
		</property>
		<property name="freemarkerSettings">
			<props>
				<prop key="template_update_delay">10</prop>
				<prop key="locale">zh_CN</prop>
				<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
				<prop key="date_format">yyyy-MM-dd</prop>
				<prop key="number_format">#.####</prop>
			</props>
		</property>
	</bean>

相信懂freemarker的人都能看明白,我也不累赘说明;最后再说一句,谢谢大家支持.
目录
相关文章
|
消息中间件 Unix Linux
Linux进程间通信(IPC)介绍:详细解析IPC的执行流程、状态和通信机制
Linux进程间通信(IPC)介绍:详细解析IPC的执行流程、状态和通信机制
683 1
|
云安全 安全 小程序
无影-阿里云第一款云电脑,它拥有超越PC的完美体验
无影是一款面向数字经济时代的生产力工具,基于流式传输服务和容器化架构,可实现随时随地云上办公、海量算力触手可得、海量应用一网打尽,依托阿里云打造云管端一体化安全防护体系,全面保障企业业务和数据安全,拥有超越PC的便捷、流畅、安全、高效体验。
27363 0
无影-阿里云第一款云电脑,它拥有超越PC的完美体验
|
Shell Docker 容器
docker--从仓库下载镜像到推送自己的项目到仓库步骤详解
怎样从仓库下载的镜像,变成容器,并在容器中制作项目,再将容器变成镜像,然后将镜像推送到仓库?        一:从官网下载镜像        官方的https://hub.docker.
3963 0
|
12月前
|
存储 JavaScript 前端开发
使用JavaScript构建动态交互式网页:从基础到实践
【10月更文挑战第12天】使用JavaScript构建动态交互式网页:从基础到实践
520 1
|
12月前
|
安全 Android开发 iOS开发
安卓vs iOS:探索两种操作系统的独特魅力与技术深度###
【10月更文挑战第16天】 本文旨在深入浅出地探讨安卓(Android)与iOS这两种主流移动操作系统的特色、优势及背后的技术理念。通过对比分析,揭示它们各自如何塑造了移动互联网的生态,并为用户提供丰富多彩的智能体验。无论您是科技爱好者还是普通用户,都能从这篇文章中感受到技术创新带来的无限可能。 ###
435 2
|
12月前
|
开发框架 Oracle Java
【编程基础知识】《Java 世界探秘:JRE、JDK 与 JDK 版本全解析》
JRE(Java Runtime Environment)是运行Java程序所需的环境,包含JVM和Java核心类库,适合普通用户使用。JDK(Java Development Kit)则是Java开发工具包,不仅包含JRE,还提供了编译器、调试器等开发工具,适用于开发者。两者的主要区别在于JDK用于开发,而JRE仅用于运行Java程序。JDK各版本不断引入新特性,如Java 8中的Lambda表达式和默认方法等。环境配置方面,Windows和Linux系统都有详细的步骤,确保Java程序能够顺利编译和运行。
406 1
|
11月前
|
机器学习/深度学习 人工智能 自然语言处理
智能语音交互:AI如何重塑人际沟通###
【10月更文挑战第27天】 一句话 本文将探讨智能语音交互技术如何深刻改变我们的沟通方式,从简单的命令识别到复杂的情感理解和多模态互动,揭示其背后的技术原理与未来趋势。 ###
|
存储 编解码 API
【解码与渲染 异常情况】深入解析视频中绿色竖线现象(一)
【解码与渲染 异常情况】深入解析视频中绿色竖线现象
519 6
【解码与渲染 异常情况】深入解析视频中绿色竖线现象(一)
|
安全 搜索推荐 开发工具
“大厂”角力移动办公系统市场,钉钉和企微向左、WorkPlus向右
随着互联网行业的发展,移动办公软件市场在中国迅速崛起,2021年市场规模达264.2亿元,预计2023年将增长至330.1亿元。钉钉和企业微信成为市场领导者,凭借其内部管理与协同功能吸引大量用户。两者虽有相似之处,但各有特色,钉钉侧重内部管理,企业微信注重内外部连接。然而,SaaS模式的数据安全性和定制化能力成为挑战,部分企业转向私有化部署,如WorkPlus,它提供全私有化、个性化定制和系统集成解决方案。政策推动数字化发展,移动办公行业迎来机遇,不同厂商可根据客户需求提供SaaS或私有化部署服务。
354 1
OA系统档案管理方案及可实施性分析
OA系统以“权限+文档”两大功能板块为基础,帮助组织统一“纸质档案”与“电子档案”,用一套系统高效存储、借阅档案…