权限开发手册,数据权限和接口权限配置

Posted 清如许.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了权限开发手册,数据权限和接口权限配置相关的知识,希望对你有一定的参考价值。

权限开发手册

一般来说,权限有许多种,我们经常用到的一般有操作权限和数据权限两种。

功能权限

所谓操作权限就是有或者没有做某种操作的权限,具体表现形式就是你看不到某个菜单或按钮,当然也有的是把菜单或按钮灰掉的形式。操作权限一般都在显示界面做一次控制,过滤没有权限的操作菜单或按钮,另外在真正执行操作时,还要进行一次权限检查,确保控制非授权访问。操作权限都是围绕角色来开展的,目的就是要实现操作的角色化。

功能权限控制

在上图所示的ER图关系中,角色(blade_role)菜单表(blade_menu)通过 角色菜单关联表(blade_role_menu)建立多对多联系。

操作权限授权的过程即为给角色授予某一菜单的操作权限,用户与角色通过用户平台扩展表(blade_user_web)建立联系,若用户拥有多个角色,则在平台扩展表的角色字段role_id中多个id用英文逗号分隔。基本关系如图所示:

系统一级菜单代表系统,二极菜单代表功能管理,三级菜单代表操作按钮,如增删改查权限

数据权限

1)、什么是数据权限?

所谓数据权限,就是有或者没有对某些数据的访问权限,具体表现形式就是当某用户有操作权限的时候,但不代表其对所有的数据都有查看或者管理权限。
数据权限有两种表现形式:一种是行权限、另外一种是列权限。

  • 所谓行权限,就是限制用户对某些行的访问权限,比如:只能对本人、本部门、本组织的数据进行访问;也可以是根据数据的范围进行限制,比如:合同额大小来限制用户对数据的访问。
  • 所谓列权限,就是限制用户对某些列的访问权限,比如:某些内容的摘要可以被查阅,但是详细内容就只有VIP用户才能查看。通过数据权限,可以从物理层级限制用户对数据的行或列进行获取。

再比如:同样一个部门经理的角色,看到的数据是不一样的,所以,牵扯到数据二字,就应该不和操作二字等同起来。所以我们是通过职位来解决数据权限的,职位也可以叫岗位,是和数据查看范围有关系的,也就是组织结构里的关系。

所以在设计数据结构的时候,每个有数据范围的数据实体,都需要具备数据拥有人的字段,比如A部门的小b同学,他创建的数据,只能由A部门的部门经理看到,而同样在角色里具有部门经理的B部门的部门经理是不能看到的。所以延伸出来了一个设计思路,根据数据拥有人,圈定查看范围。数据范围的维度有:全部、本集团、本公司、本部门、自己,五个维度,可以满足大部分业务场景。
还有一个维度是自定义维度,可以自定义机构进行设置。这样的设计就达到了数据权限的操作灵活性。

● 系统都离不开权限模块,它是支撑整个系统运行的基础模块。而根据项目类型和需求的不同,权限模块的设计更是大相径庭。但不管怎么变,权限模块从大的方面来说,可以分为三种大的类型:功能权限、接口权限、数据权限。
● 功能权限:也就是我们最熟悉的菜单、按钮权限。可以配置各个角色能看到的菜单、按钮从而从最表层分配好权限
● 接口权限:顾名思义,配置不通角色调用接口的权限。有些敏感接口,是只能有固定的一些角色才能调用,普通角色是不能调用的。这种情况需要有一个明确的系统来控制对应的访问权限
● 数据权限:是大家最为需求也是最广为谈资的一个设计理念。我们需要控制不通的角色、机构人员有查看不通数据范围的权限。如果你动手去设计数据权限,当你去各大平台、百度、谷歌查找设计思路的时候,你会发现很难找到有用的资料,很多设计思路局限性非常大。

详情请参考:https://www.jianshu.com/p/0ab125cf8258

2)、行级别权限

给某一角色授权的数据仅能看到自己所管理的部分数据

3)、列级别权限

某一角色用户只能看到所拥有菜单权限下的部分列字段数据

实现不同人看不同数据,不同人对同一个页面操作不同字段。系统按钮权限和表单权限原来是正控制,只有授权的人才有权限,未授权看不到对应按钮;

数据权限的配置

为解决这一类疑难问题,提供三种方式来实现数据权限。

1.提供代码层配置@DataAuth注解达到脱离数据库的全固定配置方式

● 如果是纯注解配置,那么是不通过数据库的,相当于是离线配置。
● 我们只需要关注column、type、value这三个字段。
● column:需要过滤的数据库字段
● type:数据权限过滤的类型
● value:当数据权限类型为自定义的时候,配置的sql条件语句
1)、所在部门可见
配置DataAuth注解,因为默认字段就是create_dept,所以无需配置column

2)、所在机构及其子集可见
配置DataAuth注解,因为默认字段就是create_dept,所以无需配置column

3)、个人可见
配置DataAuth注解,由于创建人字段为create_user,不是默认,所以需要指定

4)、自定义配置
● 配置DataAuth注解,配置自定义sql
● 在这个配置的sql里我使用里占位符$userId,没错,这么写在底层就可以直接获取到当前登录用户的deptId字段,除此之外我们还可以用更多的参数,比如$deptId$roleId$tenantId$account$userName等等
● 这些参数可以参考BladeUser类,他的所有字段我们都是可以根据占位符来获得的。

2.提供代码层配置@DataAuth注解配置数据权限资源编码来达到依赖数据库的半自动配置方式

这个就需要关联数据库,根据数据权限code码来设置数据权限定义规则。

3.Web可视化全自动动态配置

● 数据权限动态配置需要依赖数据库,所以我们需要前往web端进行配置
● 配置逻辑与纯注解配置一致,其实就是把注解配置拓展,并做成了web可视化

可视化页面开发中,

注解说明

数据权限的核心注解为@DataAuth,它的定义代码如下:

package com.springblade.core.datascope.annotation;

import com.springblade.core.datascope.enums.DataScopeEnum;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DataAuth 
	/**
	 * 资源编号
	 */
	String code() default "";

	/**
	 * 数据权限对应字段
	 */
	String column() default DataScopeConstant.DEFAULT_COLUMN;

	/**
	 * 数据权限规则
	 */
	DataScopeEnum type() default DataScopeEnum.ALL;

	/**
	 * 可见字段
	 */
	String field() default "*";

	/**
	 * 数据权限规则值域
	 */
	String value() default "";

● 可以看到,目前的数据权限类型一共有五种,前面四种都是不需要自定义写sql的,只有选择了CUSTOM类型,才需要定义注解的value属性
● 注解默认过滤的字段名为create_dept,如果有修改,则需要定义对应的字段名。

数据权限拦截器配置


/**
 * mybatis 数据权限拦截器
 * @author L.cm, Chill
 */
@Slf4j
@RequiredArgsConstructor
@SuppressWarnings("rawtypes")
public class DataScopeInterceptor implements QueryInterceptor 

	private final ConcurrentMap<String, DataAuth> dataAuthMap = new ConcurrentHashMap<>(8);

	private final DataScopeHandler dataScopeHandler;
	private final DataScopeProperties dataScopeProperties;

	@Override
	public void intercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 
		//未启用则放行
		if (!dataScopeProperties.getEnabled()) 
			return;
		

		//未取到用户则放行
		BladeUser bladeUser = AuthUtil.getUser();
		if (bladeUser == null) 
			return;
		

		if (SqlCommandType.SELECT != ms.getSqlCommandType() || StatementType.CALLABLE == ms.getStatementType()) 
			return;
		

		String originalSql = boundSql.getSql();

		//查找注解中包含DataAuth类型的参数
		DataAuth dataAuth = findDataAuthAnnotation(ms);

		//注解为空并且数据权限方法名未匹配到,则放行
		String mapperId = ms.getId();
		String className = mapperId.substring(0, mapperId.lastIndexOf(StringPool.DOT));
		String mapperName = ClassUtil.getShortName(className);
		String methodName = mapperId.substring(mapperId.lastIndexOf(StringPool.DOT) + 1);
		boolean mapperSkip = dataScopeProperties.getMapperKey().stream().noneMatch(methodName::contains)
			|| dataScopeProperties.getMapperExclude().stream().anyMatch(mapperName::contains);
		if (dataAuth == null && mapperSkip) 
			return;
		

		//创建数据权限模型
		DataScopeModel dataScope = new DataScopeModel();

		//若注解不为空,则配置注解项
		if (dataAuth != null) 
			dataScope.setResourceCode(dataAuth.code());
			dataScope.setScopeColumn(dataAuth.column());
			dataScope.setScopeType(dataAuth.type().getType());
			dataScope.setScopeField(dataAuth.field());
			dataScope.setScopeValue(dataAuth.value());
		

		//获取数据权限规则对应的筛选Sql
		String sqlCondition = dataScopeHandler.sqlCondition(mapperId, dataScope, bladeUser, originalSql);
		if (!StringUtil.isBlank(sqlCondition)) 
			PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
			mpBoundSql.sql(sqlCondition);
		
	

	/**
	 * 获取数据权限注解信息
	 *
	 * @param mappedStatement mappedStatement
	 * @return DataAuth
	 */
	private DataAuth findDataAuthAnnotation(MappedStatement mappedStatement) 
		String id = mappedStatement.getId();
		return dataAuthMap.computeIfAbsent(id, (key) -> 
			String className = key.substring(0, key.lastIndexOf(StringPool.DOT));
			String mapperBean = StringUtil.firstCharToLower(ClassUtil.getShortName(className));
			Object mapper = SpringUtil.getBean(mapperBean);
			String methodName = key.substring(key.lastIndexOf(StringPool.DOT) + 1);
			Class<?>[] interfaces = ClassUtil.getAllInterfaces(mapper);
			for (Class<?> mapperInterface : interfaces) 
				for (Method method : mapperInterface.getDeclaredMethods()) 
					if (methodName.equals(method.getName()) && method.isAnnotationPresent(DataAuth.class)) 
						return method.getAnnotation(DataAuth.class);
					
				
			
			return null;
		);
	


数据权限处理规则

需要注意的是,下面的DataScopeEnum这个判断,如果角色是ADMINISTRATOR的话也是不执行直接返回null的,我就是在这里掉坑,所以还是要看源码了解执行过程找问题。


/**
 * 默认数据权限规则
 * 获取过滤sql
 * @param mapperId    数据查询类
 * @param dataScope   数据权限类
 * @param bladeUser   当前用户信息
 * @param originalSql 原始Sql
 * @author Chill
 */
@RequiredArgsConstructor
public class BladeDataScopeHandler implements DataScopeHandler 

	private final ScopeModelHandler scopeModelHandler;

	@Override
	public String sqlCondition(String mapperId, DataScopeModel dataScope, BladeUser bladeUser, String originalSql) 

		//数据权限资源编号
		String code = dataScope.getResourceCode();

		//根据mapperId从数据库中获取对应模型
		DataScopeModel dataScopeDb = scopeModelHandler.getDataScopeByMapper(mapperId, bladeUser.getRoleId());

		//mapperId配置未取到则从数据库中根据资源编号获取
		if (dataScopeDb == null && StringUtil.isNotBlank(code)) 
			dataScopeDb = scopeModelHandler.getDataScopeByCode(code);
		

		//未从数据库找到对应配置则采用默认
		dataScope = (dataScopeDb != null) ? dataScopeDb : dataScope;

		//判断数据权限类型并组装对应Sql
		Integer scopeRule = Objects.requireNonNull(dataScope).getScopeType();
		DataScopeEnum scopeTypeEnum = DataScopeEnum.of(scopeRule);
		List<Long> ids = new ArrayList<>();
		String whereSql = "where scope. in ()";
        //需要注意的是,下面的这个判断,如果角色是ADMINISTRATOR的话也是不执行直接返回null的,我就是在这里掉坑
		if (DataScopeEnum.ALL == scopeTypeEnum || StringUtil.containsAny(bladeUser.getRoleName(), RoleConstant.ADMINISTRATOR)) 
			return null;
		 else if (DataScopeEnum.CUSTOM == scopeTypeEnum) 
			whereSql = PlaceholderUtil.getDefaultResolver().resolveByMap(dataScope.getScopeValue(), BeanUtil.toMap(bladeUser));
		 else if (DataScopeEnum.OWN == scopeTypeEnum) 
			ids.add(bladeUser.getUserId());
		 else if (DataScopeEnum.OWN_DEPT == scopeTypeEnum) 
			ids.addAll(Func.toLongList(bladeUser.getDeptId()));
		 else if (DataScopeEnum.OWN_DEPT_CHILD == scopeTypeEnum) 
			List<Long> deptIds = Func.toLongList(bladeUser.getDeptId());
			ids.addAll(deptIds);
			deptIds.forEach(deptId -> 
				List<Long> deptIdList = scopeModelHandler.getDeptAncestors(deptId);
				ids.addAll(deptIdList);
			);
		
		return StringUtil.format(" select  from () scope " + whereSql, Func.toStr(dataScope.getScopeField(), "*"), originalSql, dataScope.getScopeColumn(), StringUtil.join(ids));
	


纯注解我们只需要关注下面三个字段即可,当中的数据权限规则枚举类我们来看下构成:
● 可以看到,目前的数据权限类型一共有五种,前面四种都是不需要自定义写sql的,只有选择了CUSTOM类型,才需要定义注解的value属性
● 注解默认过滤的字段名为create_dept,如果有修改,则需要定义对应的字段名

接口权限配置

1)、功能介绍

  • 接口权限:顾名思义,配置不通角色调用接口的权限。有些敏感接口,是只能有固定的一些角色才能调用,普通角色是不能调用的。这种情况需要有一个明确的系统来控制对应的访问权限
  • 接口权限系统,可以控制某些接口只能由固定的角色调用,可以动态控制不同的角色对不同接口的访问权限

通过接口配置实现,对接口的访问权限控制和数据权限控制,

接口是REST接口,接口权限认证机制使用Json web token (JWT)

接口权限调用流程:

(1)通过接口用户的用户名密码,调用鉴权token接口获取接口用户的token
该token,2个小时内有效
(2)把获取的token作为参数,调用接口的时候,会根据token去鉴权
(3)鉴权通过,接口会根据接口定义的编码,检验是否有访问权限
有则可以继续访问,无则提示访问受限
(4)有访问权限,则获取接口的数据权限规则,根据授权的数据权限规则返回需要的数据
实现一个新的接口,无需关注token的鉴权机制,

接口权限判断方式

使用AOP实现接口拦截:@PreAuth

鉴权配置注解名称为 @PreAuth ,在需要进行鉴权配置的方法加上 @PreAuth 注解,并在注解内写 入相关的鉴权方法。

@PreAuth的注解定义:

package com.springblade.core.secure.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD, ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuth 
    String value();

具体实现方法:

@Aspect
public class AuthAspect implements ApplicationContextAware 

	/**
	 * 表达式处理
	 */
	private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

	/**
	 * 切 方法 和 类上的 @PreAuth 注解
	 *
	 * @param point 切点
	 * @return Object
	 * @throws Throwable 没有权限的异常
	 */
	@Around(
		"@annotation(com.springblade.core.secure.annotation.PreAuth) || " +
			"@within(com.springblade.core.secure.annotation.PreAuth)"
	)
	public Object preAuth(ProceedingJoinPoint point) throws Throwable 
		if (handleAuth(point)) 
			return point.proceed();
		
		throw new SecureException(ResultCode.UN_AUTHORIZED);
	

	/**
	 * 处理权限
	 *
	 * @param point 切点
	 */
	private boolean handleAuth(ProceedingJoinPoint point) 
		MethodSignature ms = (MethodSignature) point.getSignature();
		Method method = ms.getMethod();
		// 读取权限注解,优先方法上,没有则读取类
		PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
		// 判断表达式
		String condition = preAuth.value();
		if (StringUtil.isNotBlank(condition)) 
			Expression expression = EXPRESSION_PARSER.parseExpression(condition);
			// 方法参数值
			Object[] args = point.getArgs();
			StandardEvaluationContext context = getEvaluationContext(method, args);
			return expression.getValue(context, Boolean.class);
		
		return false;
	

	授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限。 
如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等。 

一、授权的三要素 

授权有着三个核心元素:权限、角色和用户。 

权限 
权限是Apache Shiro安全机制最核心的元素。它在应用程序中明确声明了被允许的行为和表现。一个格式良好的权限声明可以清晰表达出用户对该资源拥有的权限。 
大多数的资源会支持典型的CRUD操作(create、read、update、delete),但是任何操作建立在特定的资源上才是有意义的。因此,权限声明的根本思想就是建立在资源以及操作上。 
而我们通过权限声明仅仅能了解这个权限可以在应用程序中做些什么,而不能确定谁拥有此权限。 
于是,我们就需要在应用程序中对用户和权限建立关联。 
通常的做法就是将权限分配给某个角色,然后将这个角色关联一个或多个用户。 

权限声明及粒度 
Shiro权限声明通常是使用以冒号分隔的表达式。就像前文所讲,一个权限表达式可以清晰的指定资源类型,允许的操作,可访问的数据。同时,Shiro权限表达式支持简单的通配符,可以更加灵活的进行权限设置。 
下面以实例来说明权限表达式。 
可查询用户数据 
User:view 
可查询或编辑用户数据 
User:view,edit 
可对用户数据进行所有操作 
User:* 或 user 
可编辑id为123的用户数据 
User:edit:123 

角色 
Shiro支持两种角色模式: 
1、传统角色:一个角色代表着一系列的操作,当需要对某一操作进行授权验证时,只需判断是否是该角色即可。这种角色权限相对简单、模糊,不利于扩展。 
2、权限角色:一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有该权限。这种角色权限可以对该角色进行详细的权限描述,适合更复杂的权限设计。 
下面将详细描述对两种角色模式的授权实现。 

二、授权实现 

Shiro支持三种方式实现授权过程: 
  • 编码实现
  • 注解实现
  • JSP Taglig实现
1、基于编码的授权实现 

1.1基于传统角色授权实现 
当需要验证用户是否拥有某个角色时,可以调用Subject 实例的hasRole*方法验证。 
Java代码  技术分享
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. if (currentUser.hasRole("administrator")) {  
  3.     //show the admin button  
  4. else {  
  5.     //don‘t show the button?  Grey it out?  
  6. }  

相关验证方法如下: 
Subject方法描述
hasRole(String roleName)当用户拥有指定角色时,返回true
hasRoles(List<String> roleNames)按照列表顺序返回相应的一个boolean值数组
hasAllRoles(Collection<String> roleNames)如果用户拥有所有指定角色时,返回true

断言支持 
Shiro还支持以断言的方式进行授权验证。断言成功,不返回任何值,程序继续执行;断言失败时,将抛出异常信息。使用断言,可以使我们的代码更加简洁。 
Java代码  技术分享
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. //guarantee that the current user is a bank teller and  
  3. //therefore allowed to open the account:  
  4. currentUser.checkRole("bankTeller");  
  5. openBankAccount();  

断言的相关方法: 
Subject方法描述
checkRole(String roleName)断言用户是否拥有指定角色
checkRoles(Collection<String> roleNames)断言用户是否拥有所有指定角色
checkRoles(String... roleNames)对上一方法的方法重载

1.2 基于权限角色授权实现 
相比传统角色模式,基于权限的角色模式耦合性要更低些,它不会因角色的改变而对源代码进行修改,因此,基于权限的角色模式是更好的访问控制方式。 
它的代码实现有以下几种实现方式: 
1、基于权限对象的实现 
创建org.apache.shiro.authz.Permission的实例,将该实例对象作为参数传递给Subject.isPermitted()进行验证。 
Java代码  技术分享
  1. Permission printPermission = new PrinterPermission("laserjet4400n""print");  
  2. Subject currentUser = SecurityUtils.getSubject();  
  3. if (currentUser.isPermitted(printPermission)) {  
  4.     //show the Print button  
  5. else {  
  6.     //don‘t show the button?  Grey it out?  
  7. }  
  8. Permission printPermission = new PrinterPermission("laserjet4400n""print");  
  9. Subject currentUser = SecurityUtils.getSubject();  
  10. if (currentUser.isPermitted(printPermission)) {  
  11.     //show the Print button  
  12. else {  
  13.     //don‘t show the button?  Grey it out?  
  14. }  

相关方法如下: 
Subject方法描述
isPermitted(Permission p)Subject拥有制定权限时,返回treu
isPermitted(List<Permission> perms)返回对应权限的boolean数组
isPermittedAll(Collection<Permission> perms)Subject拥有所有制定权限时,返回true

2、 基于字符串的实现 
相比笨重的基于对象的实现方式,基于字符串的实现便显得更加简洁。 
Java代码  技术分享
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. if (currentUser.isPermitted("printer:print:laserjet4400n")) {  
  3.     //show the Print button  
  4. else {  
  5.     //don‘t show the button?  Grey it out?  
  6. }  

使用冒号分隔的权限表达式是org.apache.shiro.authz.permission.WildcardPermission 默认支持的实现方式。 
这里分别代表了 资源类型:操作:资源ID 

类似基于对象的实现相关方法,基于字符串的实现相关方法: 
isPermitted(String perm)、isPermitted(String... perms)、isPermittedAll(String... perms) 

基于权限对象的断言实现 
Java代码  技术分享
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. //guarantee that the current user is permitted  
  3. //to open a bank account:  
  4. Permission p = new AccountPermission("open");  
  5. currentUser.checkPermission(p);  
  6. openBankAccount();  

基于字符串的断言实现 
Java代码  技术分享
  1. Subject currentUser = SecurityUtils.getSubject();  
  2. //guarantee that the current user is permitted  
  3. //to open a bank account:  
  4. currentUser.checkPermission("account:open");  
  5. openBankAccount();  

断言实现的相关方法 
Subject方法说明
checkPermission(Permission p)断言用户是否拥有制定权限
checkPermission(String perm)断言用户是否拥有制定权限
checkPermissions(Collection<Permission> perms)断言用户是否拥有所有指定权限
checkPermissions(String... perms)断言用户是否拥有所有指定权限

2、基于注解的授权实现 
Shiro注解支持AspectJ、Spring、Google-Guice等,可根据应用进行不同的配置。 

相关的注解: 
@ RequiresAuthentication 
可以用户类/属性/方法,用于表明当前用户需是经过认证的用户。 
Java代码  技术分享
  1. @RequiresAuthentication  
  2. public void updateAccount(Account userAccount) {  
  3.     //this method will only be invoked by a   
  4.     //Subject that is guaranteed authenticated  
  5.     ...  
  6. }  

@ RequiresGuest 
表明该用户需为”guest”用户 

@ RequiresPermissions 
当前用户需拥有制定权限 
Java代码  技术分享
  1. @RequiresPermissions("account:create")  
  2. public void createAccount(Account account) {  
  3.     //this method will only be invoked by a Subject  
  4.     //that is permitted to create an account  
  5.     ...  
  6. }  

@RequiresRoles 
当前用户需拥有制定角色 

@ RequiresUser 
当前用户需为已认证用户或已记住用户 

3、基于JSP  TAG的授权实现 
Shiro提供了一套JSP标签库来实现页面级的授权控制。 
在使用Shiro标签库前,首先需要在JSP引入shiro标签: 
Java代码  技术分享
  1. <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>  

下面一一介绍Shiro的标签: 
guest标签 
验证当前用户是否为“访客”,即未认证(包含未记住)的用户 
Xml代码  技术分享
  1. <shiro:guest>  
  2.     Hi there!  Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today!  
  3. </shiro:guest>  

user标签 
认证通过或已记住的用户 
Xml代码  技术分享
  1. <shiro:user>  
  2.     Welcome back John!  Not John? Click <a href="login.jsp">here<a> to login.  
  3. </shiro:user>  

authenticated标签 
已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。 
Xml代码  技术分享
  1. <shiro:authenticated>  
  2.     <a href="updateAccount.jsp">Update your contact information</a>.  
  3. </shiro:authenticated>  

notAuthenticated标签 
未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。 
Xml代码  技术分享
  1. <shiro:notAuthenticated>  
  2.     Please <a href="login.jsp">login</a> in order to update your credit card information.  
  3. </shiro:notAuthenticated>  

principal 标签 
输出当前用户信息,通常为登录帐号信息 
Xml代码  技术分享
  1. Hello, <shiro:principal/>, how are you today?  

hasRole标签 
验证当前用户是否属于该角色 
Xml代码  技术分享
  1. <shiro:hasRole name="administrator">  
  2.     <a href="admin.jsp">Administer the system</a>  
  3. </shiro:hasRole>  

lacksRole标签 
与hasRole标签逻辑相反,当用户不属于该角色时验证通过 
Xml代码  技术分享
  1. <shiro:lacksRole name="administrator">  
  2.     Sorry, you are not allowed to administer the system.  
  3. </shiro:lacksRole>  

hasAnyRole标签 
验证当前用户是否属于以下任意一个角色。 
Xml代码  技术分享
  1. <shiro:hasAnyRoles name="developer, project manager, administrator">  
  2.     You are either a developer, project manager, or administrator.  
  3. </shiro:lacksRole>  

hasPermission标签 
验证当前用户是否拥有制定权限 
Xml代码  技术分享
  1. <shiro:hasPermission name="user:create">  
  2.     <a href="createUser.jsp">Create a new User</a>  
  3. </shiro:hasPermission>  

lacksPermission标签 
与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过 
Xml代码  技术分享
  1. <shiro:hasPermission name="user:create">  
  2.     <a href="createUser.jsp">Create a new User</a>  
  3. </shiro:hasPermission>  


三、Shiro授权的内部处理机制 
技术分享 
1、在应用程序中调用授权验证方法(Subject的isPermitted*或hasRole*等) 
2、Sbuject的实例通常是DelegatingSubject类(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用相应的isPermitted*或hasRole*方法。 
3、接下来SecurityManager会委托内置的Authorizer的实例(默认是ModularRealmAuthorizer 类的实例,类似认证实例,它同样支持一个或多个Realm实例认证)调用相应的授权方法。 
4、每一个Realm将检查是否实现了相同的 Authorizer 接口。然后,将调用Reaml自己的相应的授权验证方法。 

当使用多个Realm时,不同于认证策略处理方式,授权处理过程中: 
1、当调用Realm出现异常时,将立即抛出异常,结束授权验证。 
2、只要有一个Realm验证成功,那么将认为授权成功,立即返回,结束认证。

















































































































































以上是关于权限开发手册,数据权限和接口权限配置的主要内容,如果未能解决你的问题,请参考以下文章

Apache Shiro 使用手册Shiro 授权

mysql 开发进阶篇系列 55 权限与安全(安全事项 )

OpenHarmony 源码解析之安全子系统 (应用权限管理)

微信公众号开发15正式公众号的配置

Dubbo3高级特性「提升系统安全性」服务鉴权和权限控制方案及开发实现

Android开发中权限配置