Make your Spring Security @Secured annotations more DRY
2016-04-15 15:51
531 查看
RecentlyauserontheGrailsUsermailinglistwantedtoknowhow
toreducerepetitionwhendefining@Securedannotations.TherulesforspecifyingattributesinJavaannotationsareprettyrestrictive,
soIcouldn’tseeadirectwaytodowhathewasasking.
UsingGroovydoesn’treallyhelpheresinceforthemostpartannotationsinaGroovyclassareprettymuchthesameasinJava(exceptforthesyntaxforarrayvalues).OfcourseGroovynowsupportsclosuresinannotations,butthiswouldrequireacodechange
intheplugin.ButthenIthoughtaboutsomeworkJeffBrowndidrecentlyinthecacheplugin.
Spring’scacheabstractionAPIincludesthreeannotations;
and
Wewerethinkingaheadaboutsupportingmoreconfigurationoptionsthantheseannotationsallow,butsinceyoucan’tsubclassannotationswedecidedtouseanASTtransformationtofindourversionsoftheseannotations(currentlywiththesameattributes
astheSpringannotations)andconvertthemtovalidSpringannotations.SoIlookedatJeff’s
codeanditendedupbeingthebasisforafixforthisproblem.
It’snotpossibletousecodetoexternalizetheauthoritylistsbecauseyoucan’tcontrolthecompilationorder.SoIendedupwithasolutionthatisn’tperfectbutworks–Ilookforapropertiesfileintheprojectroot(
Theformatissimple–thekeysarenamesforeachauthoritylistandthevaluesarethelistsofauthoritynames,comma-delimited.Here’sanexample:
Thesekeysarethevaluesyouuseforthenew
Forexamplehere’sacontrollerusingthenewannotation:
Thisistheequivalentofthiscontroller(andifyoudecompiletheonewith
seebothannotations):
TheASTtransformationclasslooksfor
loadsthepropertiesfile,andaddsanew
(the
isn’tremoved)usingtherolenamesspecifiedinthepropertiesfile:
I’llprobablyincludethisinthepluginatsomepoint–IcreatedaJIRA
issue
asareminder–butfornowyoucanjustcopythesetwoclassesinto
yourapplication’ssrc/javafolderandcreatea
intheprojectroot.Anytimeyouwanttoaddorremoveanentryoraddorremovearolenamefromanentry,updatethepropertiesfile,run
Reference:Make
yourSpringSecurity@SecuredannotationsmoreDRYfromourJCG
partnerBurtBeckwithattheAnArmyofSolipsistsblog.
toreducerepetitionwhendefining@Securedannotations.TherulesforspecifyingattributesinJavaannotationsareprettyrestrictive,
soIcouldn’tseeadirectwaytodowhathewasasking.
UsingGroovydoesn’treallyhelpheresinceforthemostpartannotationsinaGroovyclassareprettymuchthesameasinJava(exceptforthesyntaxforarrayvalues).OfcourseGroovynowsupportsclosuresinannotations,butthiswouldrequireacodechange
intheplugin.ButthenIthoughtaboutsomeworkJeffBrowndidrecentlyinthe
Spring’scacheabstractionAPIincludesthreeannotations;
@Cacheable,
@CacheEvict,
and
@CachePut.
Wewerethinkingaheadaboutsupportingmoreconfigurationoptionsthantheseannotationsallow,butsinceyoucan’tsubclassannotationswedecidedtouseanASTtransformationtofindourversionsoftheseannotations(currentlywiththesameattributes
astheSpringannotations)andconvertthemtovalidSpringannotations.SoIlookedatJeff’s
codeanditendedupbeingthebasisforafixforthisproblem.
It’snotpossibletousecodetoexternalizetheauthoritylistsbecauseyoucan’tcontrolthecompilationorder.SoIendedupwithasolutionthatisn’tperfectbutworks–Ilookforapropertiesfileintheprojectroot(
roles.properties).
Theformatissimple–thekeysarenamesforeachauthoritylistandthevaluesarethelistsofauthoritynames,comma-delimited.Here’sanexample:
1 | admins=ROLE_ADMIN, ROLE_SUPERADMIN |
2 | switchUser=ROLE_SWITCH_USER |
3 | editors=ROLE_EDITOR, ROLE_ADMIN |
@Authoritiesannotation:
01 | package grails.plugins.springsecurity.annotation; |
02 |
03 | import java.lang.annotation.Documented; |
04 | import java.lang.annotation.ElementType; |
05 | import java.lang.annotation.Inherited; |
06 | import java.lang.annotation.Retention; |
07 | import java.lang.annotation.RetentionPolicy; |
08 | import java.lang.annotation.Target; |
09 |
10 | import org.codehaus.groovy.transform.GroovyASTTransformationClass; |
11 |
12 | /** |
13 | * |
14 | */ |
15 | @Target ({ElementType.FIELD, |
16 | @Retention (RetentionPolicy.RUNTIME) |
17 | @Inherited |
18 | @Documented |
19 | @GroovyASTTransformationClass ( |
20 | "grails.plugins.springsecurity.annotation.AuthoritiesTransformation" ) |
21 | public @interface Authorities { |
22 | /** |
23 | * |
24 | * |
25 | * |
26 | */ |
27 | String value(); |
28 | } |
1 | @Authorities ( 'admins' ) |
2 | class SecureController { |
3 |
4 | @Authorities ( 'editors' ) |
5 | def |
6 | ... |
7 | } |
8 | } |
@Authoritiesyou’ll
seebothannotations):
1 | @Secured ([ 'ROLE_ADMIN' , 'ROLE_SUPERADMIN' ]) |
2 | class SecureController { |
3 |
4 | @Secured ([ 'ROLE_EDITOR' , 'ROLE_ADMIN' ]) |
5 | def |
6 | ... |
7 | } |
8 | } |
@Authoritiesannotations,
loadsthepropertiesfile,andaddsanew
@Securedannotation
(the
@Authoritiesannotation
isn’tremoved)usingtherolenamesspecifiedinthepropertiesfile:
001 | package grails.plugins.springsecurity.annotation; |
002 |
003 | import grails.plugins.springsecurity.Secured; |
004 |
005 | import java.io.File; |
006 | import java.io.FileReader; |
007 | import java.io.IOException; |
008 | import java.util.ArrayList; |
009 | import java.util.List; |
010 | import java.util.Properties; |
011 |
012 | import org.codehaus.groovy.ast.ASTNode; |
013 | import org.codehaus.groovy.ast.AnnotatedNode; |
014 | import org.codehaus.groovy.ast.AnnotationNode; |
015 | import org.codehaus.groovy.ast.ClassNode; |
016 | import org.codehaus.groovy.ast.expr.ConstantExpression; |
017 | import org.codehaus.groovy.ast.expr.Expression; |
018 | import org.codehaus.groovy.ast.expr.ListExpression; |
019 | import org.codehaus.groovy.control.CompilePhase; |
020 | import org.codehaus.groovy.control.SourceUnit; |
021 | import org.codehaus.groovy.transform.ASTTransformation; |
022 | import org.codehaus.groovy.transform.GroovyASTTransformation; |
023 | import org.springframework.util.StringUtils; |
024 |
025 | /** |
026 | * |
027 | */ |
028 | @GroovyASTTransformation (phase=CompilePhase.CANONICALIZATION) |
029 | public class AuthoritiesTransformation implements ASTTransformation { |
030 |
031 | protected static final ClassNode |
032 | new ClassNode(Secured. class ); |
033 |
034 | public void visit(ASTNode[] |
035 | try { |
036 | ASTNode 0 ]; |
037 | ASTNode 1 ]; |
038 | if (!(firstNode instanceof AnnotationNode) || |
039 | !(secondNode instanceof AnnotatedNode)) { |
040 | throw new RuntimeException( "Internal + |
041 | firstNode.getClass().getName() + |
042 | " + secondNode.getClass().getName()); |
043 | } |
044 |
045 | AnnotationNode |
046 | AnnotatedNode |
047 |
048 | AnnotationNode |
049 | if (secured null ) { |
050 | annotatedNode.addAnnotation(secured); |
051 | } |
052 | } |
053 | catch (Exception |
054 | // TODO |
055 | e.printStackTrace(); |
056 | } |
057 | } |
058 |
059 | protected AnnotationNode |
060 | throws IOException { |
061 | Expression "value" ); |
062 | if (!(value instanceof ConstantExpression)) { |
063 | // TODO |
064 | System.out.println( |
065 | "annotation + |
066 | value); |
067 | return null ; |
068 | } |
069 |
070 | String |
071 | String[] |
072 | if (authorityNames null ) { |
073 | return null ; |
074 | } |
075 |
076 | return buildAnnotationNode(authorityNames); |
077 | } |
078 |
079 | protected AnnotationNode |
080 | AnnotationNode new AnnotationNode(SECURED); |
081 | List<Expression> new ArrayList<Expression>(); |
082 | for (String |
083 | nameExpressions.add( new ConstantExpression(authorityName)); |
084 | } |
085 | securedAnnotationNode.addMember( "value" , |
086 | new ListExpression(nameExpressions)); |
087 | return securedAnnotationNode; |
088 | } |
089 |
090 | protected String[] |
091 | throws IOException { |
092 |
093 | Properties new Properties(); |
094 | File new File( "roles.properties" ); |
095 | if (!propertyFile.exists()) { |
096 | // TODO |
097 | System.out.println( "Property ); |
098 | return null ; |
099 | } |
100 |
101 | properties.load( new FileReader(propertyFile)); |
102 |
103 | Object |
104 | if (value null ) { |
105 | // TODO |
106 | System.out.println( "No + "'" ); |
107 | return null ; |
108 | } |
109 |
110 | List<String> new ArrayList<String>(); |
111 | String[] |
112 | value.toString()) |
113 | for (String |
114 | auth |
115 | if (auth.length() 0 ) { |
116 | names.add(auth); |
117 | } |
118 | } |
119 |
120 | return names.toArray( new String[names.size()]); |
121 | } |
122 | } |
issue
asareminder–butfornowyoucanjustcopythesetwoclassesinto
yourapplication’ssrc/javafolderandcreatea
roles.propertiesfile
intheprojectroot.Anytimeyouwanttoaddorremoveanentryoraddorremovearolenamefromanentry,updatethepropertiesfile,run
grails cleanand
grails compiletobesurethatthelatestvaluesareused.
Reference:Make
yourSpringSecurity@SecuredannotationsmoreDRYfromourJCG
partnerBurtBeckwithatthe
相关文章推荐
- Java学习笔记
- SpringMVC入门(搭建SpringMVC)
- Creating Your Own Java Annotations
- 【java开发系列】—— spring简单入门示例
- 在Eclipse中使用JUnit4进行单元测试(中级篇)
- spring配置datasource三种方式
- spring+mybatis一个方法执行多条更新语句,实现批量DML
- java反射调用private方法,获取修改private属性值
- 【java开发系列】—— struts2简单入门示例
- 在 java 中创建自己的 shapes +java实现边框
- SpringMVC 基础教程 helloworld
- [java]java字符串如何保存到数据库日期类型的列?
- 【Java集合源码剖析】Vector源码剖析
- [疯狂Java]JDBC:ResultSet的升级RowSet、离线的CachedRowSet、离线分页查询
- Java 4000 static关键字
- Synch4j使用指南
- REST实现(Spring下实现+JDK6机制实现)
- Struts中防止表单重复提交
- java常用sql处理函数类
- HBASE遇到的java.lang.OutOfMemoryError: unable to create new native thread解决方法