pdf 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 clean
和grails compile
以确保使用最新值。
参考:在An Solipsists博客上,由我们的JCG合作伙伴 Burt Beckwith 使您的Spring Security @Secured注释更加干燥 。
翻译自: https://www.javacodegeeks.com/2012/06/make-your-spring-security-secured.html
pdf secured