使您的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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值