范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

SpringSecurity中的权限注解很神奇吗?

  最近有个小伙伴在微信群里问 Spring Security 权限注解的问题:
  很多时候事情就是这么巧,松哥最近在做的 tienchin 也是基于注解来处理权限问题的,所以既然大家有这个问题,咱们就一块来聊聊这个话题。
  当然一些基础的知识我就不讲了,对于 Spring Security 基本用法尚不熟悉的小伙伴,可在公众号后台回复 ss,有原创的系列教程。  1. 具体用法
  先来看看 Spring Security 权限注解的具体用法,如下:  @PreAuthorize("@ss.hasPermi("tienchin:channel:query")") @GetMapping("/list") public TableDataInfo getChannelList() {     startPage();     List list = channelService.list();     return getDataTable(list); }
  类似于上面这样,意思就是说,当前用户需要具备  tienchin:channel:query  权限,才能执行当前的接口方法。
  那么要搞明白 @PreAuthorize 注解的原理,我觉得得从两个方面入手:  首先明白 Spring 中提供的 SpEL。  其次搞明白 Spring Security 中对方法注解的处理规则。
  我们一个一个来看。  2. SpEL
  Spring Expression Language(简称 SpEL)是一个支持查询和操作运行时对象导航图功能的强大的表达式语言。它的语法类似于传统 EL,但提供额外的功能,最出色的就是函数调用和简单字符串的模板函数。
  SpEL 给 Spring 社区提供一种简单而高效的表达式语言,一种可贯穿整个 Spring 产品组的语言。这种语言的特性基于 Spring 产品的需求而设计,这是它出现的一大特色。
  在我们离不开 Spring 框架的同时,其实我们也已经离不开 SpEL 了,因为它太好用、太强大了,SpEL 在整个 Spring 家族中也处于一个非常重要的位置。但是很多时候,我们对它的只了解一个大概,其实如果你系统的学习过 SpEL,那么上面 Spring Security 那个注解其实很好理解。
  我先通过一个简单的例子来和大家捋一捋 SpEL。
  为了省事,我就创建一个 Spring Boot 工程来和大家演示,创建的时候不用加任何额外的依赖,就最最基础的依赖即可。
  代码如下:  String expressionStr = "1 + 2"; ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(expressionStr);
  expressionStr 是我们自定义的一个表达式字符串,这个字符串通过一个 ExpressionParser 对象将之解析为一个 Expression,接下来就可以执行这个 exp 了。
  执行的时候有两种方式,对于我们上面这种不带任何额外变量的,我们可以直接执行,直接执行的方式如下:  Object value = exp.getValue(); System.out.println(value.toString());
  这个打印结果为 3。
  我记得之前有个小伙伴在群里问想执行一个字符串表达式,但是不知道怎么办,js 中有 eval 函数很方便,我们 Java 中也有 SpEL,一样也很方便。
  不过很多时候,我们要执行的表达式可能比较复杂,这时候上面这种调用方式就不太够用了。
  此时我们可以为要调用的表达式设置一个上下文环境,这个时候就会用到 EvaluationContext 或者它的子类,如下:  StandardEvaluationContext context = new StandardEvaluationContext(); System.out.println(exp.getValue(context));
  当然上面这个表达式不需要设置上下文环境,我举一个需要设置上下文环境的例子。
  例如我现在有一个 User 类,如下:  public class User {     private Integer id;     private String username;     private String address;     //省略 getter/setter }
  现在我的表达式是这样:  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("javaboy"); user.setId(99); ctx.setVariable("user", user); String value = exp.getValue(ctx, String.class); System.out.println("value = " + value);
  这个表达式就表示获取 user 对象的 username 属性。将来创建一个 user 对象,放到 StandardEvaluationContext 中,并基于此对象执行表达式,就可以打印出来想要的结果。
  如果我们将 user 对象设置为 rootObject,那么表达式中就不需要 user 了,如下:  String expression = "username"; ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(expression); StandardEvaluationContext ctx = new StandardEvaluationContext(); User user = new User(); user.setAddress("广州"); user.setUsername("javaboy"); user.setId(99); ctx.setRootObject(user); String value = exp.getValue(ctx, String.class); System.out.println("value = " + value);
  表达式就一个 username 字符串,将来执行的时候,会自动从 user 中找到 username 的值并返回。
  当然表达式也可以是方法,例如我在 User 类中添加如下两个方法:  public String sayHello(Integer age) {     return "hello " + username + ";age=" + age; } public String sayHello() {     return "hello " + username; }
  我们就可以通过表达式调用这两个方法,如下:
  调用有参的 sayHello:  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("javaboy"); user.setId(99); ctx.setRootObject(user); String value = exp.getValue(ctx, String.class); System.out.println("value = " + value);
  就直接写方法名然后执行就行了。
  调用无参的 sayHello:  String expression = "sayHello"; ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(expression); StandardEvaluationContext ctx = new StandardEvaluationContext(); User user = new User(); user.setAddress("广州"); user.setUsername("javaboy"); user.setId(99); ctx.setRootObject(user); String value = exp.getValue(ctx, String.class); System.out.println("value = " + value);
  这些就都好懂了。
  甚至,我们的表达式也可以涉及到 Spring 中的一个 Bean,例如我们向 Spring 中注册如下 Bean:  @Service("us") public class UserService {     public String sayHello(String name) {         return "hello " + name;     } }
  然后通过 SpEL 表达式来调用这个名为 us 的 bean 中的 sayHello 方法,如下:  @Autowired BeanFactory beanFactory; @Test void contextLoads() {     String expression = "@us.sayHello("javaboy")";     ExpressionParser parser = new SpelExpressionParser();     Expression exp = parser.parseExpression(expression);     StandardEvaluationContext ctx = new StandardEvaluationContext();     ctx.setBeanResolver(new BeanFactoryResolver(beanFactory));     String value = exp.getValue(ctx, String.class);     System.out.println("value = " + value); }
  给配置的上下文环境设置一个 bean 解析器,这个 bean 解析器会自动跟进名字从 Spring 容器中找打响应的 bean 并执行对应的方法。
  当然,关于 SpEL 的玩法还有很多,我就不一一列举了。这里主要是想让小伙伴们知道,有这么个技术,方便大家理解 @PreAuthorize 注解的原理。  3. @PreAuthorize
  接下来我们就回到 Spring Security 中来看 @PreAuthorize 注解。
  权限的实现方式千千万,又有各种不同的权限模型,然而归结到代码上,无非两种:  基于 URL 地址的权限处理  基于方法注解的权限处理
  松哥之前的 vhr 使用的是前者。
  @PreAuthorize 注解当然对应的是后者。这次做的 tienchin 项目就是后者,我们来看一个例子:  @PreAuthorize("@ss.hasPermi("tienchin:channel:query")") @GetMapping("/list") public TableDataInfo getChannelList() {     startPage();     List list = channelService.list();     return getDataTable(list); }
  注解好说,里边的  @ss.hasPermi("tienchin:channel:query")  是啥意思呢? ss 是一个注册在 Spring 容器中的 bean,对应的类位于  org.javaboy.tienchin.framework.web.service.PermissionService  中。 很明显,hasPermi 就是这个类中的方法。
  这个 hasPermi 方法的逻辑其实很简单:  public boolean hasPermi(String permission) {     if (StringUtils.isEmpty(permission)) {         return false;     }     LoginUser loginUser = SecurityUtils.getLoginUser();     if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {         return false;     }     return hasPermissions(loginUser.getPermissions(), permission); } private boolean hasPermissions(Set permissions, String permission) {     return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); }
  这个判断逻辑很简单,就是获取到当前登录的用户,判断当前登录用户的权限集合中是否具备当前请求所需要的权限。具体的判断逻辑没啥好说的,就是看集合中是否存在某个字符串。
  那么这个方法是在哪里调用的呢?
  大家知道,Spring Security 中处理权限的过滤器是 FilterSecurityInterceptor,所有的权限处理最终都会来到这个过滤器中。在这个过滤器中,将会用到各种投票器、表决器之类的工具,这里我就不细说了,之前的 Spring Security 系列教程都有详细介绍。
  在投票器中,我们可以看到专门处理 @PreAuthorize 注解的类 PreInvocationAuthorizationAdviceVoter,我们来看下他里边的核心方法:  @Override public int vote(Authentication authentication, MethodInvocation method, Collection attributes) {  PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);  if (preAttr == null) {   return ACCESS_ABSTAIN;  }  return this.preAdvice.before(authentication, method, preAttr) ? ACCESS_GRANTED : ACCESS_DENIED; }
  框架的源码写的就是好,你一看名字就知道他想干嘛了!这里就进入到最后一句,调用了一个 Advice 中到前置通知,来判断权限是否满足:  public boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute attr) {  PreInvocationExpressionAttribute preAttr = (PreInvocationExpressionAttribute) attr;  EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);  Expression preFilter = preAttr.getFilterExpression();  Expression preAuthorize = preAttr.getAuthorizeExpression();  if (preFilter != null) {   Object filterTarget = findFilterTarget(preAttr.getFilterTarget(), ctx, mi);   this.expressionHandler.filter(filterTarget, preFilter, ctx);  }  return (preAuthorize != null) ? ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx) : true; }
  现在,当你看到这个 before 方法的时候,应该会觉得比较熟悉了吧。  首先获取到 preAttr 对象,这个对象里边其实就保存着你 @PreAuthorize 注解中的内容。  接下来跟进当前登录用户信息 authentication 创建一个上下文对象,此时创建出来的上下文对象中就包含了当前用户具备哪些权限。  获取过滤器(我们这个项目中无)。  获取到权限注解。  最后执行表达式,去查看当前用户权限中是否包含请求所需要的权限。
  就这样,是不是很简单?
  好啦,今天就和小伙伴们分享这么多,在松哥近期推出的 tienchin 项目视频中,也会通过视频的形式跟大家细聊这个知识点。

数字化物料能力是什么?怎么让客户主动找上门想让用户了解自己的企业需要哪几个因素?这个问题的答案有很多,但总结起来可以归类为以下三个因素推广渠道宣传方式以及营销物料,这三者缺一不可!随着数字化营销模式的发展,各大企业之间的竞OPPO与美洲电信合作打入拉美市场,国产手机出海再下一城众所周知,这几年国产手机的发展可以说是越来越顺畅,除了在国内市场上风生水起以外,国产手机品牌的出海之路也捷报频频。来自广东东莞的绿厂就是其中的佼佼者,在海外市场深耕多年以后,大家已2020年Q2东南亚手机市场份额出炉,OPPO排名亮了随着国产手机品牌的不断发展壮大,近两年不少国内手机厂商开始走出国门,纷纷往更广阔的海外市场发展扩张,得到了广大海外消费者的关注。近日,据韩联社的最新报道,在市调机构Counterp数字化市场部打造指南(一)明确市场部到底是什么?对于企业来说,营销推广的流程基本由市场部和销售部两个部门负责,其中销售部们主要是负责将产品与消费者之间的物理距离拉近,将产品卖到客户手中市场部则不同,市场部的工作人员需要负责拉近产数字化市场部打造指南(三)如何搭建能干销售做业务的官网?紧接上文,我们已经了解到搭建数字化市场部的重要性,而想要实现这一目标则需要企业利用好数字化工具,去了解客户信息加深客户沟通以及深入触达客户。在之前我们举例了一种能够做到这点的方式,数字化市场部打造指南(二)如何打造数字化市场部?前文中我们说过了该如何打造企业的市场部,以及市场部应当做到的引导销售引导消费者和引导产品部门的任务,接下来,我们就需要开始下一步操作了。在过去的营销过程中,数字化仅仅作为一个支持性营销物料多次上传太麻烦?没事,我们可以批量解决前不久有用户向我们反映我们现在要做一个新网站,可原来网站上的文章内容要一篇一篇手动上传到后台,真的太费时间了!的确,我们可以想象这么一个场景当你搭建好了自己的新官网,看着空荡荡的内使用官微中心文章编辑器时碰上了这几个问题?别担心工欲善其事,必先利其器。企业想要做好数字化营销,一个强大的企业官网后台是必不可少的。而其中,企业官网后台具备的营销物料的上传和分享功能则是会直接影响到企业官网数字化营销的结果。因此物料官宣,数字化新物种如何让更多客户找到你?如何衡量企业是否成功?我们往往会说一个成功的企业会在哪个方面取得了什么成就,获得了什么奖项,取得了什么突破等等等等。其实这些名号的内核只有一个,那就是这家企业到底有多少客户?拥有一等等党的福利!这几款机型非常合适,网友不会再错过了RedmiK30至尊纪念版价格为2000元价位的产品,首先想到的肯定是刚刚发布的RedmiK30至尊纪念版,这部手机应该是这个价格下无脑购买的代表。总而言之一句话,放心大胆的购买就祭出杀手锏!麒麟990后置三摄,荣耀再次用降价让销量疯狂当一部手机真正引起我们消费者的兴趣时,这是事实。不管是什么,它都无法消除购买欲望。这是一种疯狂盲目的,所以老师一直在谈论手机。这主要是基于疯狂背后的明智态度,它可以略微减少进犯的机
荣耀X30下半年推送?MagicUI6。0发布,升级计划惹争议荣耀于1月10日晚间召开新品发布会,除了带来外界期待MagicV折叠屏手机,同时发布新一代MagicUI6。0定制系统,号称AI进化,续梦之作。当官方介绍完所有升级点之后,网友惊讶新鲜早科技丨游戏行业现127亿美元最大收购案传腾讯拟收购游戏手机厂商黑鲨荣耀发布首款折叠屏手机21世纪经济报道记者杨清清综合报道早上好,新的一天又开始了。在过去24小时内,科技行业发生了哪些有意思的事情?来跟21tech一起看看吧。巨头风向标1游戏行业最大收购案TakeTw新骁龙8实测表现远超纸面数据,游戏优化确实用心了高通全新一代骁龙8移动平台的发布,受到了各大手机厂家和消费者的青睐,因为与上一代骁龙888相比,这并不只是一次简简单单的迭代更新,高通敢将骁龙作为一个独立的产品品牌,说明骁龙翅膀硬痛点营销连载之21互联网巨头崛起的根本原因1998年11月,马化腾和张志东创立腾讯,他们最初的业务构想是通过一些寻呼台做系统集成。软件QQ(之前称作OICQ)在产生之初,是腾讯公司几个技术人员在办公室用于即时沟通的一个小软电脑故障不求人!常见电脑系统故障解答一win10电脑蓝屏的常见问题及解决方式1蓝屏代码1MACHINECHECKEXCEPTION原因分析CPU过于超频。解决办法启动自动动修复程序,修复系统错误后,将CPU降回出厂频三马谁最有钱!三马指的是哪三个人?三马谁最有钱?大家对于这个这个问题想必都是十分好奇的,毕竟豪富的资产大家都感兴趣么,但是也有一些朋友并不知道三马指的是哪三个人,今天小编就带大家一起来了解一下。首先我们先来看看三马治水变智水九大高原湖泊200个电子湖长上岗来源科技日报九大高原湖泊200个电子湖长上岗科技赋能,将治水变成智水本报记者李禾魏山忠水利部副部长在云南滇池洱海阳宗海等九大高原湖泊,200个电子湖长已经开始工作了。在这些湖泊的重TMT晚报腾讯三大产品融合打通B站发布2021百大UP主名单科创板日报11日讯,今日TMT晚报的主要内容有中国移动通信联合会元宇宙产业委员会首批成员名单公布滴滴出行CTO张博卸任滴滴支付董事长三星或于近期宣布大规模并购交易。企业微信腾讯会议会员资讯紫晶存储入选国家通信业节能技术产品推荐目录(2021)为了加快先进适用节能技术推广应用,推动工业和信息化领域节能和能效提升,助力碳达峰碳中和目标实现,工业和信息化部于近日发布了国家通信业节能技术产品推荐目录(2021)。紫晶存储磁光电究竟是安卓系统卡,还是国内app流氓软件卡?国外的app卡吗?没错,我选择iphone最大的理由,就是安卓的生态系统太渣。这个词没用错,就是渣滓横行。例如同款软件,iphone上可以不需要读取联系人不用录音不用GPS,也不会互相唤醒,但在安卓万和和美的燃气热水器哪个更好?在燃气热水器这个单品上,万和美的都是一线品牌,产品的质量真的相差无几。万和本来就是以燃气热水器起家的,从万家乐的零件供应商一步一步做到了现在燃气热水器单品行业老大的位置美的利用自身