Sping实战第十四章:保护方法应用

前面Spring Security是保护应用的Web层,限制URL级别访问,此处是方法级别的保护;

一、使用注解保护方法

Spring Security提供了三种不同的安全注解:

  • Spring Security自带的@Secured注解:能够基于用户所授予的权限限制对方法的访问
  • JSR-250的@RolesAllowed注解:能够基于用户所授予的权限限制对方法的访问
  • 表达式驱动的注解,包括@PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter:@PreAuthorize和@PostAuthorize可以在方法上定义更灵活的安全规则;@PreFilter和@PostFilter能够过滤方法返回的以及传入方法的集合;

1、使用@Secured注解限制方法调用

// GlobalMethodSecurityConfiguration:能够为方法级别的安全性提供更精细的配置
// 和第九章中WebSecurityConfigurerAdapter类似
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true) // 启用基于注解的方法安全性
// 如果securedEnabled属性的值为true的话,将会创建一个切点,
// 这样的话Spring Security切面就会包装带有@Secured注解的方法。
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{

// 在Web层的安全配置中设置认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     auth.inMemoryAuthentication()
            .withUser("user").password("password").roles("USER");
}

}
// @Secured注解会使用一个String数组作为参数。每个String值是一个权限,调用这个方法至少需要具备其中的一个权限。
// 只允许具有ROLE_SPITTER权限的认证用户才能调用addSpittle() 方法
@Secured("ROLE_SPITTER")
public void addSpittle(Spittle spittle) {
    // ...
}

// 必须具备ROLE_SPITTER或ROLE_ADMIN权限才能触发这个方法
@Secured({"ROLE_SPITTER", "ROLE_ADMIN"})
public void addSpittle(Spittle spittle) {
    // ...
}

如果方法被没有认证的用户或没有所需权限的用户调用,保护这个方法的切面将抛出一个Spring Security异常(可能是AuthenticationException或AccessDeniedException的子类)。它们是非检查型异常,但这个异常最终必须要被捕获和处理。如果被保护的方法是在Web请求,这个异常会被Spring Security的过滤器自动处理。否则的话,就需要代码来处理这个异常。

2、在Spring Security中使用JSR-250的@RolesAllowed注解

@RolesAllowed注解和@Secured注解区别:@RolesAllowed是JSR-250定义的Java标准注解;

两者共同不足:只能根据用户有没有授予特定的权限来限制方法的调用。在判断方式是否执行方面,无法使用其他的因素。

@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true) // 使用@RolesAllowed,此处需为true
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{
}

 

二、使用表达式实现方法级别的安全性

SqEL能够在方法调用上实现更加灵活的安全性约束,如果表达式的计算结果为true,那么安全规则通过,否则就会失败。下表描述了这些新的注解:

注解描述
@PreAuthorize在方法调用之前,基于表达式的计算结果来限制对方法的访问
@PostAuthorize允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
@PostFilter允许方法调用,但必须按照表达式来过滤方法的结果
@PreFilter允许方法调用,但必须在进入方法之前过滤输入值

1、启用注解

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法前后注解
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{
}

2、表述方法访问规则

@PreAuthorize和@PostAuthorize:

优点:基于表达式的计算结果来限制方法的访问,更加灵活;

区别:表达式执行的时机

  • @PreAuthorize的表达式会在方法调用之前执行,如果表达式的计算结果为false,会阻止方法执行。
  • @PostAuthorize的表达式直到方法返回才会执行,然后决定是否抛出安全性的异常。

(1)在方法调用前验证权限

// 同@RolesAllowed和@Secured
@PreAuthorize("hasRole('ROLE_SPITTER')")
public void addSpittle(Spittle spittle){
    ...
}


// #spittle直接引用了方法中的同名参数
@PreAuthorize(    
          "(hasRole('ROLE_SPITTER') and #spittle.text.length() <= 140)"
          + " or hasRole('ROLE_PREMIUM')")
public void addSpittle(Spittle spittle){
    ...
}

(2)在方法调用之后验证权限-需要方法有返回值;如果注解判断结果不符合预期,将抛出异常

// returnObject是SqEl提供的变量;principal也是另一个内置特殊名称,代表当前认证用户的主要信息
// 此处如果不匹配,将会排抛出AccessDeniedException
@PostAuthorize("returnObject.spitter.username == principal.username")
public Spittle getSpittleById(long id){
    ...
}

 

3、过滤方法的输入和输出

@PreAuthorize和@PostAuthorize限制方法调用,但有时需要限制传入方法和方法返回的数据;

(1)事后对方法的返回值进行过滤

@PostFilter会使用SqEl表达式计算该方法所返回集合的每个成员,将计算结果为false的成员移除掉。

/*
@PreAuthorize限制只有具备ROLE_SPITTER或ROLE_ADMIN权限的用户才能访问该方法;
@PostFilter会对返回的List进行过滤,确保用户只能看到允许的Spittle;
filterObject引用的是方法返回List中的某一个元素;
在这个Spittle对象中,如果Spitter的用户名于认证用户相同或者用户具有ROLE_ADMIN角色,
那这个元素将会最终包含在过滤的列表中。否则,它将被过滤掉。
*/
@PreAuthorize("hasAnyRole({'ROLE_SPITTER', 'ROLE_ADMIN'})")
@PostFilter("hasRole('ROLE_ADMIN') || filterObject.spitter.username == principal.name" )
public List<Spittle> getOffensiveSpittles(){
   ...
}

(2)事先对方法的参数进行过滤-不常用

如以下以批处理的方式删除Spittle组成的列表(只能由其所有者或管理员删除):

/* 尽管可以在方法内部过滤删除部分spittle,但这样会将安全逻辑直接嵌入到方法中;
相对于删除Spittle,安全逻辑是独立的关注点;如果列表中能够只包含实际要删除的Spittle会更好
@PreFilter可以过滤要进入方法中集合成员
targetObject表示要进行计算的当前列表元素
*/
@PreAuthorize("hasAnyRole({'ROLE_SPITTER', 'ROLE_ADMIN'})")
@PreFilter("hasRole('ROLE_ADMIN') || targetObject.spitter.username == principal.name" )
public void deleteSpittles(List<spittle> spittles){
   ...
}

(3)定义许可计算器-针对表达式比较复杂的场景

@PreAuthorize("hasAnyRole({'ROLE_SPITTER', 'ROLE_ADMIN'})")
@PreFilter("hasPermission(targetObject, 'delete')" )
public void deleteSpittles(List<spittle> spittles){
   ...
}

hasPermission() 函数是Spring Security为SpEL提供的扩展。它为开发者提供了一个时机,能够在执行计算的事后插入任意的逻辑。我们所需要做的就是编写并注册一个自定义的许可计算器。

a、编写许可器:

// 假设使用Spittle对象来评估权限,所以第二个hasPermission方法只是简单地抛出异常
public class SpittlePermissonEvaluator implements PermissionEvaluator {

    private static final GrantedAuthority ADMIN_AUTHORITY = new SimpleGrantedAuthority("ROLE_ADMIN");

// Object target要评估的对象
    public boolean hasPermission(Authentication authentication, Object target, Object permission) {
        if (target instanceof Spittle){
            Spittle spittle = (Spittle) target;
            String username = spittle.getSpitter().getUsername();
            if ("delete".equals(permission)){
                return isAdmin(authentication) ||
                        username.equals(authentication.getName());
            }
        }
        throw new UnsupportedOperationException("hasPermission not supported for object <" + target
                                                + "> and permission <" + permission + ">");
    }

// 只有目标对象的ID可以得到时此方法才有用,并将ID作为Serializable传入第二个参数
    public boolean hasPermission(Authentication authentication, Serializable serializable, String s, Object o) {
        throw new UnsupportedOperationException();
    }

    public boolean isAdmin(Authentication authentication){
        return authentication.getAuthorities().contains(ADMIN_AUTHORITY);
    }
}

b、注册许可器:

默认情况下,Spring Security会配置为使用DefaultMethodSecurityExpressionHandler,它会使用一个DenyAllPermissionEvaluator实例。顾名思义,DenyAllPermissionEvaluator将会在hasPermission()方法中始终返回false,拒绝所有的方法访问。但是,我们可以为Spring Security提供另一个DefaultMethodSecurityExpressionHandler,让它使用我们自定义的SpitterPermissionEvaluator,这需要重载GlobalMethodSecurityConfiguration的createExpressionHandler方法:

@Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler expressionHandler =
                new DefaultMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(new SpittlePermissonEvaluator());
        return expressionHandler;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《Spring实战》第四版是一本介绍Spring框架的经典教材,由Craig Walls编写。本书全面系统地讲解了Spring框架的核心概念、应用场景和各种功能的使用方法,涵盖了Spring的IoC、AOP、JDBC、Web、安全等方面的知识。 本书的特点在于:既适合初学者入门学习,也适合有经验的开发人员深入学习和实践。书中的案例也覆盖了从简单的应用到复杂的企业级系统,有大量的源代码、注释和实际配置,方便读者学习和理解。 在使用本书时,读者不仅可以通过学习Spring框架的原理和应用,更能通过实际的案例体验到该框架的实用性和易用性。本书通过详细的文本说明和插图,以形象的方式解释了Spring框架中的各种组件,帮助读者快速掌握Spring的核心功能和代码开发的技巧。 总的来说,《Spring实战》第四版是一本分析深入,实用性强的Spring框架学习教材。对Java程序员而言,学习Spring框架是必备技能之一,本书将对初学者和有经验的开发人员都具有很高的价值,是深入掌握Spring框架极好的教材。 ### 回答2: 《Spring实战(第四版)》是一本介绍如何使用Spring框架的专业书籍。本书详细地介绍了Spring框架的使用,包括Spring的核心概念、配置、AOP、数据访问、Web开发、测试和安全等方面。 本书的作者是Craig Walls,是一位资深的Java开发者和Spring框架的专家。他在本书中结合自己多年的开发经验详细阐述了Spring框架的用法和实践技巧,并且提供了大量的实例和案例,使读者能够更加深入地理解和应用Spring框架。 整本书分为18章,每章围绕一个主题详细介绍Spring框架的使用和实践方法。比如,在第一章中,作者介绍了Spring的核心概念,如IoC和DI模式,以及Spring容器和Bean的生命周期等重要内容。而在第三章中,则详细介绍了Spring的AOP功能和使用方法。 另外,本书还涵盖了Spring框架在数据访问、Web开发、测试和安全方面的具体应用。比如,在第八章中,作者介绍了Spring对JDBC、ORM和NoSQL等不同类型数据库的支持方法。而在第十三章中,则深入讲解了Spring MVC框架以及REST服务的开发方法。 总的来说,《Spring实战(第四版)》是一本非常权威和实用的Spring框架实践书籍。无论是初学者还是有实际项目经验的开发者,都可以从本书中获得丰富的经验和实践技巧。 ### 回答3: 《Spring实战(第四版)》是经典的Spring全面指南,是程序员必备的学习Spring的权威手册。该书分为三部分,介绍了Spring框架的核心内容、应用与扩展。 第一部分的内容介绍了Spring的基本概念和核心模块,让读者全面了解Spring框架的基础。其中包括了IoC容器、AOP、Spring的配置、Spring MVC、数据访问等等,让读者可以深入了解Spring框架的内部机制,实现更加灵活的开发需求。 第二部分内容主要讲述了Spring在企业级应用中的应用。它阐述了如何将Spring框架应用于大型组织和应用程序中。这里的讨论涉及到了事务处理、安全性、远程访问、批处理和云服务等,让读者能够将Spring应用到复杂的应用程序中。 第三部分内容是Spring的扩展及高级特性讨论。在这里,读者将学习如何扩展Spring的功能,提高应用程序的性能和可用性。具体地讨论了集成Spring Boot、Spring的测试支持、Spring的缓存支持、微服务和Spring的Websockets等方面。 总而言之,《Spring实战(第四版)》作为一部Spring经典权威教材,能够帮助读者系统性地学习Spring框架及其应用,是Java开发人员不可缺少的学习资源。不仅提供了实用的代码案例和技术细节,也传授了高效的开发策略和使用技巧,具有非常高的实用价值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值