使您的Spring Security @Secured注释更干燥

最近,Grails用户邮件列表中的一个用户想知道在定义@Secured批注时如何减少重复 。 在Java批注中指定属性的规则非常严格,因此我看不到直接执行他所要求的方法的方法。

使用Groovy并没有真正的帮助,因为Groovy类中的注释大部分与Java中的注释几乎相同(数组值的语法除外)。 当然,Groovy现在支持注释中的闭包,但这需要在插件中进行代码更改。 但是后来我想到了Jeff Brown最近在缓存插件中所做的一些工作。

Spring的缓存抽象API包括三个注释。 @Cacheable@CacheEvict@CachePut 。 我们正在考虑支持比这些注释所允许的更多的配置选项,但是由于您无法对注释进行子类化,因此我们决定使用AST转换来查找这些注释的版本(当前具有与Spring注释相同的属性)并进行转换有效的Spring注释。 因此,我查看了Jeff的代码 ,它最终成为解决此问题的基础。

无法使用代码外部化权限列表,因为您无法控制编译顺序。 因此,我最终得到了一个不完美但可以使用的解决方案–我在项目根目录中寻找一个属性文件( roles.properties )。 格式很简单–密钥是每个权限列表的名称,值是权限名称的列表,以逗号分隔。 这是一个例子:

 admins=ROLE_ADMIN, ROLE_SUPERADMIN  switchUser=ROLE_SWITCH_USER  editors=ROLE_EDITOR, ROLE_ADMIN 

这些键是用于新@Authorities批注的值:

 package grails.plugins.springsecurity.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;  import org.codehaus.groovy.transform.GroovyASTTransformationClass;  /** * @author Burt Beckwith */  @Target ({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})  @Retention (RetentionPolicy.RUNTIME)  @Inherited  @Documented  @GroovyASTTransformationClass ( "grails.plugins.springsecurity.annotation.AuthoritiesTransformation" )  public @interface Authorities { /** * The property file key; the property value will be a * comma-delimited list of role names. * @return the key */ String value();  } 

例如,这是一个使用新注释的控制器:

 @Authorities ( 'admins' )  class SecureController { @Authorities ( 'editors' ) def someAction() { ... }  } 

这等效于此控制器(如果使用@Authorities反编译,则会看到两个注释):

 @Secured ([ 'ROLE_ADMIN' , 'ROLE_SUPERADMIN' ])  class SecureController { @Secured ([ 'ROLE_EDITOR' , 'ROLE_ADMIN' ]) def someAction() { ... }  } 

AST转换类使用属性文件中指定的角色名称查找@Authorities批注,加载属性文件,并添加新的@Secured批注(未删除@Authorities批注):

 package grails.plugins.springsecurity.annotation;  import grails.plugins.springsecurity.Secured;  import java.io.File;  import java.io.FileReader;  import java.io.IOException;  import java.util.ArrayList;  import java.util.List;  import java.util.Properties;  import org.codehaus.groovy.ast.ASTNode;  import org.codehaus.groovy.ast.AnnotatedNode;  import org.codehaus.groovy.ast.AnnotationNode;  import org.codehaus.groovy.ast.ClassNode;  import org.codehaus.groovy.ast.expr.ConstantExpression;  import org.codehaus.groovy.ast.expr.Expression;  import org.codehaus.groovy.ast.expr.ListExpression;  import org.codehaus.groovy.control.CompilePhase;  import org.codehaus.groovy.control.SourceUnit;  import org.codehaus.groovy.transform.ASTTransformation;  import org.codehaus.groovy.transform.GroovyASTTransformation;  import org.springframework.util.StringUtils;  /** * @author Burt Beckwith */  @GroovyASTTransformation (phase=CompilePhase.CANONICALIZATION)  public class AuthoritiesTransformation implements ASTTransformation { protected static final ClassNode SECURED = new ClassNode(Secured. class ); public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) { try { ASTNode firstNode = astNodes[ 0 ]; ASTNode secondNode = astNodes[ 1 ]; if (!(firstNode instanceof AnnotationNode) || !(secondNode instanceof AnnotatedNode)) { throw new RuntimeException( "Internal error: wrong types: " + firstNode.getClass().getName() + " / " + secondNode.getClass().getName()); } AnnotationNode rolesAnnotationNode = (AnnotationNode) firstNode; AnnotatedNode annotatedNode = (AnnotatedNode) secondNode; AnnotationNode secured = createAnnotation(rolesAnnotationNode); if (secured != null ) { annotatedNode.addAnnotation(secured); } } catch (Exception e) { // TODO e.printStackTrace(); } } protected AnnotationNode createAnnotation(AnnotationNode rolesNode) throws IOException { Expression value = rolesNode.getMembers().get( "value" ); if (!(value ConstantExpression)) { (!(value instanceof ConstantExpression)) { // TODO System.out.println( "annotation @Authorities value isn't a ConstantExpression: " + value); return null ; } String fieldName = value.getText(); String[] authorityNames = getAuthorityNames(fieldName); if (authorityNames == null ) { return null ; } return buildAnnotationNode(authorityNames); } protected AnnotationNode buildAnnotationNode(String[] names) { AnnotationNode securedAnnotationNode = new AnnotationNode(SECURED); List<Expression> nameExpressions = new ArrayList<Expression>(); for (String authorityName : names) { nameExpressions.add( new ConstantExpression(authorityName)); } securedAnnotationNode.addMember( "value" , new ListExpression(nameExpressions)); return securedAnnotationNode; } protected String[] getAuthorityNames(String fieldName) throws IOException { Properties properties = new Properties(); File propertyFile = new File( "roles.properties" ); if (!propertyFile.exists()) { // TODO System.out.println( "Property file roles.properties not found" ); return null ; } properties.load( new FileReader(propertyFile)); Object value = properties.getProperty(fieldName); if (value == null ) { // TODO System.out.println( "No value for property '" + fieldName + "No value for property '" + fieldName + "'" ); return null ; } List<String> names = new ArrayList<String>(); String[] nameArray = StringUtils.commaDelimitedListToStringArray( value.toString()) for (String auth : nameArray) { auth = auth.trim(); if (auth.length() > 0 ) { names.add(auth); } } return names.toArray( new String[names.size()]); }  } 

我可能会在某个时候将其包含在插件中-我已提醒您创建了JIRA问题 -但现在您可以将这两个类复制到应用程序的src / java文件夹中,并在项目根目录中创建一个roles.properties文件。 每当您要添加或删除条目或从条目添加或删除角色名称时,请更新属性文件,运行grails cleangrails compile以确保使用最新值。

参考:在An Solipsists博客上,我们的JCG合作伙伴 Burt Beckwith 让您的Spring Security @Secured注释更加干燥 。


翻译自: https://www.javacodegeeks.com/2012/06/make-your-spring-security-secured.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372414.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阅读《大型网站技术架构》 第三章心得

今天阅读了《大型网站技术架构》 的第三章&#xff0c;这一章主要讲解了大型网站核心架构要素&#xff0c;并且概括的讲解了相应的实现方法。 软件架构除了系统功能需求外&#xff0c;还需要关注性能、可用性、伸缩性、扩展性、安全性。 其中性能是网站的重要指标。优化网站性能…

easyui数据表格重置_数据库三种删除方式

第一种 使用delete 语句特点&#xff1a;delete 属于数据库操纵语言DML&#xff0c;表示删除表中的数据&#xff0c;删除过程是每次从表中删除一行&#xff0c;并把该行删除操作作为事务记录在日志中保存可以配合事件&#xff08;transaction&#xff09;和 回滚&#xff08;ro…

main函数之间的代码操作

全局对象的构造函数会在main函数之前执行。转载于:https://www.cnblogs.com/yingl/p/5817123.html

Windows 自启动总结《转》

开机启动项  【启动项目就是开机的时候系统会在前台或者后台运行的程序】  当Windows&#xff08;操作系统&#xff09;完成登录过程&#xff0c;进程表中出现了很多的进程&#xff01;Windows在启动的时候&#xff0c;自动加载了很多程序。  许多程序的自启动&#xff0…

win10多合一原版系统_微软Win10专业版制作多合一系统安装盘教程

微软Win10怎么制作多合一系统安装盘?和Win10家庭版、win10企业版&#xff0c;win10教育版相比&#xff0c;微软Win10专业版是最受大家喜欢的操作系统&#xff0c;那么在安装Win10操作系统的时候&#xff0c;我们就不得不准备多个不同版本的系统安装盘。可是你知道微软Win10专业…

Oracle Coherence:分布式数据管理

本文介绍如何使用Oracle Coherence提供分布式&#xff08;分区&#xff09;数据管理。 在下面的示例应用程序中&#xff0c;创建了一个名为OTV的新集群&#xff0c;并且在该集群的两个成员之间分配了一个名为user-map的缓存对象。 二手技术&#xff1a; JDK 1.6.0_21 Maven的…

Ajax学习笔记

Ajax 什么是ajax AJAX即“Asynchronous JavaScript and XML”&#xff08;异步的JavaScript与XML技术&#xff09;&#xff0c;指的是一套综合了多项技术的浏览器端网页开发技术。以前&#xff0c;几乎所有的网站都由HTML页面实现&#xff0c;服务器处理每一个用户请求都需要重…

美团点评DBProxy读写分离使用说明

目的 因为业务架构上需要实现读写分离&#xff0c;刚好前段时间美团点评开源了在360Atlas基础上开发的读写分离中间件DBProxy&#xff0c;关于其介绍在官方文档已经有很详细的说明了&#xff0c;其特性主要有&#xff1a;读写分离、负载均衡、支持分表、IP过滤、sql语句黑名单、…

apriori算法c++_关联分析——基于Apriori算法实现

电子商务推荐系统主要是通过统计和挖掘技术&#xff0c;根据用户在网站上的行为,主动为用户提供推荐服务&#xff0c;从而提高网站体验。而根据不同的业务场景&#xff0c;推荐系统需要满足不同的推荐粒度&#xff0c;包括搜索推荐,商品类目推荐,商品标签推荐&#xff0c;店铺推…

在Oracle Coherence中分发Spring Bean

本文展示了如何通过使用Oracle Coherence中的EntryProcessor和可移植对象格式&#xff08;POF&#xff09;功能来分发Spring Bean。 Coherence通过EntryProcessor API支持无锁编程模型。 此功能通过减少网络访问并在条目上执行隐式的低级锁定来提高系统性能。 此隐式低级锁定功…

postman测试实例--断言

让我们来看看postman测试的一些例子。 其中大部分是作为内部postman片段。 大多数测试是为单行的JavaScript语句一样简单。 只要你想一个请求&#xff0c;你可以有很多的测试。注意&#xff1a;一个响应已从服务器接收后测试脚本运行。测试实例1.设置环境变量 postman.setEnvir…

python实现单例模式的几种方式_基于Python中单例模式的几种实现方式及优化详解...

单例模式单例模式(Singleton Pattern)是一种常用的软件设计模式&#xff0c;该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中&#xff0c;某个类只能出现一个实例时&#xff0c;单例对象就能派上用场。比如&#xff0c;某个服务器程序的配置信息存放在一…

android-铃声的设置与播放

在android系统中&#xff0c;不同铃声存放的铃声路径&#xff1a;/system/media/audio/ringtones 来电铃声/system/media/audio/notifications 短信通知铃声/system/media/audio/alarms 闹钟铃声铃声的设置&#xff1a;import java.io.File; import andr…

Apache Commons SCXML:有限状态机实现

本文提到有限状态机&#xff08;FSM&#xff09;&#xff0c;SCXML&#xff08;状态图可扩展标记语言&#xff09;和Apache Common的SCXML库。 本文还提供了基本的ATM有限状态机示例代码。 有限状态机&#xff1a; 您可能还记得计算机科学课程中的有限状态机。 FSM用于设计计算…

第二十章、分离应用程序逻辑并处理事件

理解委托 委托是对方法的引用。&#xff08;之所以称为委托&#xff0c;是因为一旦被调用&#xff0c;就将具体的处理“委托”给引用的方法&#xff09; 委托对象引用了方法&#xff0c;和将int赋值给int变量一样&#xff0c;是将方法引用赋给委托对象。 Processor p new Proc…

pymol怎么做底物口袋表面_怎么从文献中发掘一篇新文章?

本文来自微信公众号&#xff1a;X-MOLNews可能你的导师也曾说过这样的话——盯着Nature、Science级别的文章做&#xff0c;可能最终会中十分的文章&#xff1b;如果盯着十分的文章做&#xff0c;可能最终发出来也就五六分&#xff1b;但如果就为了发个文章混毕业&#xff0c;很…

如何分析线程转储– IBM VM

本文是我们的线程转储分析系列的第4部分&#xff0c;它将为您概述什么是IBM VM的JVM线程转储以及您将找到的不同线程和数据点。 您将看到和学习​​到&#xff0c;IBM VM Thread Dump格式是不同的&#xff0c;但是提供了更多现成的故障排除数据。 在这一点上&#xff0c;您应该…

VMware vSphere克隆虚拟机

参考资料&#xff1a;http://blog.csdn.net/shen_jz2012/article/details/484167711. 首先将你所要克隆的虚拟机关掉2. 选择你的ESXI服务器选中"配置"&#xff0c;然后选中存储器右键你的存储介质&#xff0c;比如我的是datastore1&#xff0c;选择“浏览数据存储”。…

将本地jar包倒入maven项目类库中

有两种方法&#xff1a;1.本地下载maven并配置环境变量&#xff0c;然后运行cmd控制台输入 mvn install:install-file -Dfile本地jar路径 -DgroupId -DartifactId -Dpackagingjar -Dversion -DgeneratePomtrue. 2.直接在pom.xml中对应的依赖下面添加<scope>system&l…

Spring和JSF集成:分页

处理大型数据集时&#xff0c;通常需要以分页格式显示数据。 分页是一个有趣的问题&#xff0c;因为它倾向于跨越应用程序的所有层&#xff0c;从视图层通过应用程序服务一直到对数据库的原始调用。 在获取分页数据时&#xff0c;有一些非常好的解决方案。 如果您使用的是JPA&a…