【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制

本文主要是介绍【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制

  • Spring SpEL
    • 基本表达式
    • 类相关表达式
    • 表达式模板
  • SpEL表达式实现权限控制
    • PreAuth
    • AuthFun
    • PreAuthAspect
    • UserController
    • SpelParserUtils

Spring SpEL

Spring 表达式语言 SpEL 是一种非常强大的表达式语言,它支持在运行时查询和操作对象图。 它提供了许多高级功能,例如方法调用和基本的字符串模板功能。表达式语言给静态Java语言增加了动态功能。

Spring 表达式语言最初是为 Spring 社区创建的,它拥有一种受良好支持的表达式语言,可用于 Spring 产品组合中的所有产品。 虽然 SpEL 是 Spring 产品组合中表达式评估的基础,但它不直接与 Spring 绑定,可以独立使用

Spring Security框架中启用prePost注解的支持就是使用SpEL表达式实现的权限控制

  • @PreAuthorize(“hasAuthority(‘save’)”)
  • @PreAuthorize(“isAnonymous()”)
  • @PreAuthorize(“isAuthenticated()”)

SpEL支持如下表达式:

  • 基本表达式: 字面量表达式、关系,逻辑与算数运算表达式、字符串连接及截取表达式、三目运算及Elivis表达式、正则表达式、括号优先级表达式
  • 类相关表达式: 类类型表达式、类实例化、instanceof表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean引用
  • 集合相关表达式:内联List、内联数组、集合,字典访问、列表,字典,数组修改、集合投影、集合选择;不支持多维内联数组初始化;不支持内联字典定义
  • 其他表达式:模板表达式。

注:SpEL表达式中的关键字是不区分大小写的。

基本表达式

/**
* 基本表达式 : 字面量表达式
*/
@Test
public void test01() {// 1)创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现;ExpressionParser parser = new SpelExpressionParser();// 2)解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。Expression expression = parser.parseExpression("1+2");// 3)求值:通过Expression接口的getValue方法根据上下文获得表达式值。System.out.println(expression.getValue());String str1 = parser.parseExpression("'Hello World!'").getValue(String.class);int int1 = parser.parseExpression("1").getValue(Integer.class);long long1 = parser.parseExpression("-1L").getValue(long.class);float float1 = parser.parseExpression("1.1").getValue(Float.class);double double1 = parser.parseExpression("1.1E+2").getValue(double.class);int hex1 = parser.parseExpression("0xa").getValue(Integer.class);long hex2 = parser.parseExpression("0xaL").getValue(long.class);boolean true1 = parser.parseExpression("true").getValue(boolean.class);boolean false1 = parser.parseExpression("false").getValue(boolean.class);Object null1 = parser.parseExpression("null").getValue(Object.class);System.out.println("str1=" + str1);System.out.println("int1=" + int1);System.out.println("long1=" + long1);System.out.println("float1=" + float1);System.out.println("double1=" + double1);System.out.println("hex1=" + hex1);System.out.println("hex2=" + hex2);System.out.println("true1=" + true1);System.out.println("false1=" + false1);System.out.println("null1=" + null1);
}
/**
* 基本表达式 : 字面量表达式
*/
@Test
public void test02() {// 1)创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现;ExpressionParser parser = new SpelExpressionParser();// 2)解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。Expression expression = parser.parseExpression("('Hello' + ' World').concat(#end)");// 3)构造上下文:准备比如变量定义等等表达式需要的上下文数据。EvaluationContext context = new StandardEvaluationContext();context.setVariable("end", "!");// 4)求值:通过Expression接口的getValue方法根据上下文获得表达式值。System.out.println(expression.getValue(context));
}

类相关表达式

@Data
@Component("ss")
public class User {private String username;private String address;private Integer age;public String sayHello(String username) {return "hello " + username;}public String sayHello(Integer age) {return "hello " + username + ";age=" + age;}public String sayHello() {return "hello " + username;}
}

/**
* 类相关表达式: 变量定义及引用
*/
@Test
public void test03() {String expression = "#user.username";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);;ctx.setVariable("user", user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
@Test
public void test04() {String expression = "username";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);// user 对象设置为 rootObject,那么表达式中就不需要 #user.ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: 对象方法调用
*/
@Test
public void test05() {String expression = "sayHello(99)";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: 对象方法调用
*
* 调用无参的 sayHello
*/
@Test
public void test06() {String expression = "sayHello()";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: Bean引用
*/
@Test
public void test07() {// 通过 SpEL 表达式来调用这个名为 ss 的 bean 中的 sayHello 方法String expression = "@ss.sayHello('spel bean引用')";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();// 给配置的上下文环境设置一个 bean 解析器,这个 bean 解析器会自动跟进名字从 Spring 容器中找打响应的 bean 并执行对应的方法ctx.setBeanResolver(new BeanFactoryResolver(beanFactory));String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}

表达式模板

/**
* 表达式模板:
* 模板表达式就是由字面量与一个或多个表达式块组成。每个表达式块由“前缀+表达式+后缀”形式组成,如“${1+2}”即表达式块
*
*/
@Test
public void test08() {//创建解析器SpelExpressionParser parser = new SpelExpressionParser();//创建解析器上下文ParserContext context = new TemplateParserContext("%{", "}");Expression expression = parser.parseExpression("你好:%{#name},我们正在学习:%{#lesson}", context);//创建表达式计算上下文EvaluationContext evaluationContext = new StandardEvaluationContext();evaluationContext.setVariable("name", "zysheep");evaluationContext.setVariable("lesson", "SpEL表达式!");//获取值String value = expression.getValue(evaluationContext, String.class);System.out.println(value);
}

SpEL表达式实现权限控制

PreAuth

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuth {/***** permissionAll()-----只要配置了角色就可以访问* hasPermission("MENU.QUERY")-----有MENU.QUERY操作权限的角色可以访问* permitAll()-----放行所有请求* denyAll()-----只有超级管理员角色才可访问* hasAuth()-----只有登录后才可访问* hasTimeAuth(1,,10)-----只有在1-10点间访问* hasRole(‘管理员’)-----具有管理员角色的人才能访问* hasAllRole(‘管理员’,'总工程师')-----同时具有管理员、总工程师角色的人才能访问** Spring el* 文档地址:https://docs.spring.io/spring/docs/5.1.6.RELEASE/spring-framework-reference/core.html#expressions*/String value();}

AuthFun

/*** <p>* 类相关表达式: Bean引用,参数要用单引号包括** @PreAuth("@af.hasPermission('ADMIN, USER')")* </p>** @author : lyw* @since : 2023/11/23 16:01*/
@Component("af")
public class AuthFun {/*** 判断角色是否具有接口权限** @return {boolean}*/public boolean permissionAll() {//TODO  读取数据库权限数据return true;}/*** 判断角色是否具有接口权限** @param permission 权限编号,对应菜单的MENU_CODE* @return {boolean}*/public boolean hasPermission(String permission) {return hasRole(permission);}/*** 放行所有请求** @return {boolean}*/public boolean permitAll() {return true;}/*** 只有超管角色才可访问** @return {boolean}*/public boolean denyAll() {return hasRole("ADMIN");}/*** 是否已授权** @return {boolean}*/public boolean hasAuth() {return true;}/*** 是否有时间授权** @param start 开始时间* @param end   结束时间* @return {boolean}*/public boolean hasTimeAuth(Integer start, Integer end) {Integer hour = DateUtil.hour(new Date(), true);return hour >= start && hour <= end;}/*** 判断是否有该角色权限** @param role 单角色* @return {boolean}*/public boolean hasRole(String role) {return hasAnyRole(role);}/*** 判断是否具有所有角色权限** @param role 角色集合* @return {boolean}*/public boolean hasAllRole(String... role) {for (String r : role) {if (!hasRole(r)) {return false;}}return true;}/*** 判断是否有该角色权限** @param role 角色集合* @return {boolean}*/public boolean hasAnyRole(String... role) {return Arrays.stream(role).anyMatch(item -> StringUtils.equals("ADMIN", item));}
}

PreAuthAspect

@Aspect
@Component
public class PreAuthAspect {@Autowiredprivate BeanFactory applicationContext;private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();@Pointcut("@annotation(cn.zysheep.annotation.PreAuth) || @within(cn.zysheep.annotation.PreAuth)")public void pointcut() {}@Around("pointcut()")public Object preAuth(ProceedingJoinPoint point) throws Throwable {if (handleAuth(point)) {return point.proceed();}throw new BizException("用户无权限,非法用户!");}private boolean handleAuth(ProceedingJoinPoint point) {MethodSignature ms = point.getSignature() instanceof MethodSignature? (MethodSignature) point.getSignature():null;Method method = ms.getMethod();// 读取权限注解,优先方法上,没有则读取类PreAuth preAuth = method.getAnnotation( PreAuth.class);// 判断表达式String condition = preAuth.value();if (StringUtils.isNotBlank(condition)) {// @PreAuth("@af.hasPermission('ADMIN, USER')")Expression expression = EXPRESSION_PARSER.parseExpression(condition);// 方法参数值Object[] args = point.getArgs();StandardEvaluationContext context = getEvaluationContext(method, args);// 获取解析计算的结果return expression.getValue(context, Boolean.class);}return false;}/*** 获取方法上的参数** @param method 方法* @param args   变量* @return {SimpleEvaluationContext}*/private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {// 初始化Spel表达式上下文,并设置 AuthFunStandardEvaluationContext context = new StandardEvaluationContext();// 设置表达式支持spring beancontext.setBeanResolver(new BeanFactoryResolver(applicationContext));// 可以从session中获取登录用户的权限
//        for (int i = 0; i < args.length; i++) {
//            // 读取方法参数
//            MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
//            // 设置方法 参数名和值 为spel变量
//            context.setVariable(methodParam.getParameterName(), args[i]);
//        }return context;}
}

UserController

@RestController
@RequestMapping("/api")
@Slf4j
public class UserController {@GetMapping("/save")@PreAuth("@af.hasPermission('ADMIN')")public R save() {log.info("====执行保存业务逻辑=====");return R.success();}@GetMapping("/get")@PreAuth("@af.hasPermission('USER')")public R get() {log.info("====执行保存业务逻辑=====");return R.success();}}

SpelParserUtils

public final class SpelParserUtils {private static final String EXPRESSION_PREFIX = "#{";private static final String EXPRESSION_SUFFIX = "}";/*** 表达式解析器*/private static ExpressionParser expressionParser = new SpelExpressionParser();/***  参数名解析器,用于获取参数名*/private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();private SpelParserUtils(){}/*** 解析spel表达式** @param method 方法* @param args 参数值* @param spelExpression  表达式* @param clz  返回结果的类型* @param defaultResult 默认结果* @return 执行spel表达式后的结果*/public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz, T defaultResult) {String[] params = parameterNameDiscoverer.getParameterNames(method);EvaluationContext context = new StandardEvaluationContext();//设置上下文变量for (int i = 0; i < params.length; i++) {context.setVariable(params[i], args[i]);}T result = getResult(context,spelExpression,clz);if(Objects.isNull(result)){return defaultResult;}return result;}/*** 解析spel表达式** @param method  方法* @param args 参数值* @param spelExpression  表达式* @param clz  返回结果的类型* @return 执行spel表达式后的结果*/public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz) {String[] params = parameterNameDiscoverer.getParameterNames(method);EvaluationContext context = new StandardEvaluationContext();//设置上下文变量for (int i = 0; i < params.length; i++) {context.setVariable(params[i], args[i]);}return getResult(context,spelExpression,clz);}/*** 解析spel表达式** @param param  参数名* @param paramValue 参数值* @param spelExpression  表达式* @param clz  返回结果的类型* @return 执行spel表达式后的结果*/public static <T> T parse(String param, Object paramValue, String spelExpression, Class<T> clz) {EvaluationContext context = new StandardEvaluationContext();//设置上下文变量context.setVariable(param, paramValue);return getResult(context,spelExpression,clz);}/*** 解析spel表达式** @param param 参数名* @param paramValue 参数值* @param spelExpression  表达式* @param clz  返回结果的类型* @param defaultResult 默认结果* @return 执行spel表达式后的结果*/public static <T> T parse(String param, Object paramValue,String spelExpression, Class<T> clz, T defaultResult) {EvaluationContext context = new StandardEvaluationContext();//设置上下文变量context.setVariable(param, paramValue);T result = getResult(context,spelExpression,clz);if(Objects.isNull(result)){return defaultResult;}return result;}/*** 获取spel表达式后的结果** @param context 解析器上下文接口* @param spelExpression  表达式* @param clz  返回结果的类型* @return 执行spel表达式后的结果*/private static <T> T getResult(EvaluationContext context,String spelExpression, Class<T> clz){try {//解析表达式Expression expression = parseExpression(spelExpression);//获取表达式的值return expression.getValue(context, clz);} catch (Exception e) {log.error(e.getMessage(),e);}return null;}/*** 解析表达式* @param spelExpression spel表达式* @return*/private static Expression parseExpression(String spelExpression){// 如果表达式是一个#{}表达式,需要为解析传入模板解析器上下文if(spelExpression.startsWith(EXPRESSION_PREFIX) && spelExpression.endsWith(EXPRESSION_SUFFIX)){return expressionParser.parseExpression(spelExpression,new TemplateParserContext());}return expressionParser.parseExpression(spelExpression);}}

这篇关于【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/528336

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory