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

这篇具有很好参考价值的文章主要介绍了【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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表达式中的关键字是不区分大小写的。文章来源地址https://www.toymoban.com/news/detail-830346.html

基本表达式

/**
* 基本表达式 : 字面量表达式
*/
@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 {

    @Autowired
    private 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表达式上下文,并设置 AuthFun
        StandardEvaluationContext context = new StandardEvaluationContext();
        // 设置表达式支持spring bean
        context.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表达式基于注解实现权限控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring表达式语言(SPEL)学习(03)

    在表达式中直接写name和getName(),这时候Expression是无法解析的,因为其不知道name和getName()对应什么意思 当表达式是基于某一个对象时,我们可以把对应的对象作为一个rootObject传递给对应的Experssion进行取值 通过指定EvaluationContext我们可以让name和getName()变得有意义,指定了Ev

    2024年02月02日
    浏览(47)
  • 揭秘Spring依赖注入和SpEL表达式

    摘要: 在本文中,我们深入探讨了Spring框架中的属性注入技术,包括setter注入、构造器注入、注解式属性注入,以及使用SpEL表达式进行属性注入。 本文分享自华为云社区《Spring高手之路3——揭秘Spring依赖注入和SpEL表达式》,作者:砖业洋__ 。 在本文中,我们深入探讨了

    2024年02月08日
    浏览(39)
  • Spring高手之路3——揭秘Spring依赖注入和SpEL表达式

    本篇会给大家举出各种 Spring 属性依赖注入的例子,方便大家理解。 我们在前面的文章中已经使用过 XML 进行 setter 方法的属性注入了,下面让我们再来回顾一下: 我们在前面的文章中也学习过如何在 bean 创建时通过编程方式设置属性: 使用XML进行setter方法注入 首先,我们需

    2024年02月08日
    浏览(40)
  • Spring判断方法名是符合给定的SPEL+表达式的+API

    org.springframework.expression.spel.standard.SpelExpressionParser解析SPEL表达式 org.springframework.expression.spel.support.StandardEvaluationContext 验证方法名是否符合表达式 我们先使用SpelExpressionParser类来解析表达式,然后再创建一个StandardEvaluationContext对象,并将方法名作为变量设置到上下文中。最后

    2024年02月10日
    浏览(41)
  • Web攻防--Java_SQL注入--XXE注入-- SSTI模板注入--SPEL表达式注入

    编译器在编译sql语句时,会依次进行词法分析、语法分析、语义分析等操作, 预编译技术会让数据库跳过编译阶段,也就无法就进行词法分析,不会被拆开,注入语句也就不会被识别为SQL的,从而防止恶意注入语句改变原有SQL语句本身逻辑。 在使用JDBC进行数据

    2024年02月09日
    浏览(59)
  • 最新最全面的Spring详解(三)——Resources,验证、数据绑定和类型转换与Spring表达式语言(SpEL)

    本文为 【Spring】Resources与Spring表达式语言(SpEL) 等相关知识,下边将对 Resources (包含: Resource接口 、 内置的 Resource的实现 、 ResourceLoader接口 、 应用环境和资源路径 ), 验证、数据绑定和类型转换 (包含: BeanWrapper 、 PropertyEditor属性编辑器 、 类型转换 、 配置 DataB

    2023年04月26日
    浏览(43)
  • Day66:WEB攻防-Java安全&SPEL表达式&SSTI模版注入&XXE&JDBC&MyBatis注入

    目录 JavaSec搭建 Hello-Java-Sec搭建 Java安全-SQL注入-JDBCMyBatis JDBC:Java语言连接数据库操作 MyBatis( mybatis是一个优秀的基于java的持久层框架,它内部封装了 jdbc) 代码审计案例:inxedu后台MyBatis注入 Java安全-XXE注入-ReaderBuilder 配置XML允许引入外部解析实体 白盒测试-XXE Java安全-SSTI模版

    2024年04月25日
    浏览(45)
  • Cron表达式简单介绍 + Springboot定时任务的应用

    前言 表达式是一个字符串,主要分成6或7个域,但至少需要6个域组成,且每个域之间以空格符隔开。 以7个域组成的,从右往左是【年 星期 月份 日期 小时 分钟 秒钟】 秒 分 时 日 月 星期 年 以6个域组成的,从右往左是【星期 月份 日期 小时 分钟 秒钟】 秒 分 时 日 月 星

    2023年04月20日
    浏览(35)
  • Spring AOP切入点表达式

    先来认识两个概念吧(其实Spring AOP实现功能增强的方式就是代理模式) 目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的 代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现 回归主

    2023年04月24日
    浏览(44)
  • spring AOP中pointcut表达式详解

    📢📢📢📣📣📣 哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步🤝🤝 一位上进心十足的【Java ToB端大厂领域博主】!😜😜😜 喜欢java和python,平时比较懒,能用程序解决的坚决不手动解决😜😜😜 ✨ 如果有对【java】感兴趣的【小可

    2024年02月13日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包