浅析SpringSecurity的方法级安全管控

摘要:
SpringSecurity支持三种方法级注释:JSR-205注释、@Secured注释和pre-PostEnabled注释。这些注释不仅可以直接添加到控制器方法中,还可以注释Service或DAO类中的方法。启用方法级别的控制代码是:创建一个新的WebSecurityConfigurerAdapterConfiguration类,添加@EnableGlobalMethodSecurity()注释,并通过@EnableGlobalMethod Security参数启用相应的方法级别控制。

一、方法安全

  除了基于URL的认证与授权,开发者也可以通过注解来灵活地配置方法安全,要使用相关注解,首先要通过@EnableGlobalMethodSecurity注解开启基于注解的安全配置:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig {
}

  prePostEnabled=true会解锁@PreAuthorize和@PostAuthorize两个注解:

(1)@PreAuthorize注解会在方法执行前进行验证

(2)@PostAuthorize注解在方法执行后进行验证

  securedEnabled=true会解锁@Secured注解

// 创建一个MethodService进行测试:
@Service
public class MethodService {

    //表示该访问该方法需要ADMIN角色,注意:需要加前缀ROLE_
    @Secured("ROLE_ADMIN")
    public String admin(){
        return "hello admin";
    }

    //表示访问该方法需要ADMIN和DBA角色
    @PreAuthorize("hasRole('ADMIN') and hasRole('DBA')")
    public String adminAndDba(){
        return "hello admin and dba";
    }

    //表示访问该方法需要ADMIN、DBA或者USER角色
    @PreAuthorize("hasAnyRole('ADMIN', 'DBA', 'USER')")
    public String adminOrDbaOrUser(){
        return "hello admin or dba or user";
    }
}

// 配置角色:
@Configuration
public class MyWebSecurityConfig6 extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("root").password("123").roles("ADMIN", "DBA")
                .and()
                .withUser("admin").password("123").roles("ADMIN", "USER")
                .and()
                .withUser("sang").password("123").roles("USER");
    }
}

二、方法级注解

  默认情况下, Spring Security 并不启用方法级的安全管控。启用方法级的管控后, 可以针对不同的方法通过注解设置不同的访问条件。

  Spring Security 支持三种方法级注解,分别是:JSR-205 注解、@Secured 注解、prePostEnabled注解。这些注解不仅可以直接加在 controller 方法上,也可以注解 Service 或 DAO 类中的方法。

  启用方法级的管控代码是:新建一个 WebSecurityConfigurerAdapterConfiguration 类,加上 @EnableGlobalMethodSecurity() 注解,通过@EnableGlobalMethodSecurity 参数开启相应的方法级的管控。

1、JSR-205 注解

// 通过 @EnableGlobalMethodSecurity(jsr250Enabled=true), 开启 JSR-205 注解
@DenyAll 注解, 拒绝所有的访问
@PermitAll 注解, 运行所有访问
@RolesAllowed({"USER","ADMIN"}), 该方法只允许有 ROLE_USER 或 ROLE_ADMIN 角色的用户访问

2、@Secured 注解

// 通过 @EnableGlobalMethodSecurity(securedEnabled=true), 开启 @Secured 注解
// 只有满足角色的用户才能访问被注解的方法, 否则将会抛出 AccessDenied 异常
// 例子:
@Secured("ROLE_TELLER","ROLE_ADMIN"), 该方法只允许 ROLE_TELLER 或 ROLE_ADMIN 角色的用户访问
@Secured("IS_AUTHENTICATED_ANONYMOUSLY"), 该方法允许匿名用户访问. 

3、@PreAuthorize 类型的注解(支持 Spring 表达式)

  @EnableGlobalMethodSecurity(prePostEnabled=true), 开启 prePostEnabled 相关的注解

  JSR-205 和 @Secured 注解功能较弱, 不支持 Spring EL 表达式. 推荐使用 @PreAuthorize 类型的注解

  具体有4个注解:

@PreAuthorize 注解, 在方法调用之前, 基于表达式结果来限制方法的使用

@PostAuthorize 注解, 允许方法调用, 但是如果表达式结果为 false, 将抛出一个安全性异常

@PostFilter 注解, 允许方法调用, 但必要按照表达式来过滤方法的结果

@PreFilter 注解, 允许方法调用, 但必须在进入方法之前过来输入值

// 例子:
@PreAuthorize("hasRole('ADMIN')") //必须有 ROLE_ADMIN 角色
public void addBook(Book book);

//必须同时具备 ROLE_ADMIN 和 ROLE_DBA 角色
@PreAuthorize("hasRole('ADMIN') AND hasRole('DBA')")
public void addBook(Book book);

@PreAuthorize ("#book.owner == authentication.name")
public void deleteBook(Book book);

@PostAuthorize ("returnObject.owner == authentication.name")
public Book getBook();

4、@PreAuthorize 表达式

(1)returnObject 保留名

  对于 @PostAuthorize 和 @PostFilter 注解, 可以在表达式中使用 returnObject 保留名, returnObject 代表着被注解方法的返回值, 我们可以使用 returnObject 保留名对注解方法的结果进行验证。比如:

@PostAuthorize ("returnObject.owner == authentication.name")
public Book getBook();

(2) 表达式中的 # 号

  在表达式中, 可以使用 #argument123 的形式来代表注解方法中的参数 argument123

@PreAuthorize ("#book.owner == authentication.name")
public void deleteBook(Book book);

// 还有一种 #argument123 的写法, 即使用 Spring Security @P注解来为方法参数起别名, 然后在 @PreAuthorize 等注解表达式中使用该别名
不推荐这种写法, 代码可读性较差.
@PreAuthorize("#c.name == authentication.name") public void doSomething(@P("c") Contact contact);

5、内置表达式有:

表达式备注
hasRole([role])如果有当前角色, 则返回 true(会自动加上 ROLE_ 前缀)
hasAnyRole([role1, role2])如果有任一角色即可通过校验, 返回true,(会自动加上 ROLE_ 前缀)
hasAuthority([authority])如果有指定权限, 则返回 true
hasAnyAuthority([authority1, authority2])如果有任一指定权限, 则返回true
principal 获取当前用户的 principal 主体对象 
authentication 获取当前用户的 authentication 对象, 
permitAll  总是返回 true, 表示全部允许
denyAll 总是返回 false, 代表全部拒绝
isAnonymous() 如果是匿名访问, 返回true
isRememberMe() 如果是remember-me 自动认证, 则返回 true
isAuthenticated() 如果不是匿名访问, 则返回true
isFullAuthenticated() 如果不是匿名访问或remember-me认证登陆, 则返回true
hasPermission(Object target, Object permission) 
hasPermission(Object target, String targetType, Object permission)  
  

三、实例代码

  Spring Security默认是禁用注解的,要想开启注解,需要在继承WebSecurityConfigurerAdapter的类上加@EnableGlobalMethodSecurity注解,来判断用户对某个控制层的方法是否具有访问权限

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
 ...........................
}

  或者是直接在继承 GlobalMethodSecurityConfiguration 的类上加上 @EnableGlobalMethodSecurity 注解即可。

package com.enmox.emcs.security;

@Configuration
@EnableGlobalMethodSecurity(
    prePostEnabled = true,
    securedEnabled = true,
    jsr250Enabled = true
)
public class RoleConfig extends GlobalMethodSecurityConfiguration {
    public static final String ROLE_PREFIX = "ROLE_";
    ......
}
    @ApiOperation("媒资列表")
    @GetMapping("/findVideo")
    @PreAuthorize("hasRole('sys') or hasAuthority('video')")
    public PageInfo<VideoOrAudioVO> findVideo(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
                                              @RequestParam(required = false, defaultValue = "10") Integer pageSize,
                                              String type) {
        return vodService.findVideo(pageNum, pageSize,type);
    }

免责声明:文章转载自《浅析SpringSecurity的方法级安全管控》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Xcode的控制台调试命令(转)tune2fs命令详解下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

SQLserver 获取当前时间

选择CONVERT(varchar,GETDATE())--2017selectDATENAME(YEAR,GETDATE())--2017selectDATEPART。获取当前月份--05或5selectDATENAME(MM,...

zookeeper 日志输出到指定文件夹

最近,我在学习ZookeperStormKafka。顺便说一下,我在本地建立了一个集群。我遇到了Zookeeper日志输出路径的问题。我发现设置log4j。Zookeeper中的属性无法解决日志路径问题。我发现解决方案如下:1.修改log4j属性,您应该能够更改它。我更改了红色粗体,但仍然没有生效。#定义要移动的默认值...

HTML5表单之input 类型- Date Pickers(日期选择器)

HTML5有几种新的输入类型用于选择日期和时间:日期:选择日期、月份、年份月份:选择月份、年份星期:选择星期和年份时间:选择时间datetime local:选择时间、日期、月份和年份datetime:选择时间、,年示例1:日期示例2:月示例3:周示例4:时间˂inputtype=“time”name=“tart_time”value=“”//示例5:dat...

JS获取当前时间

如果有更好的方法,请提出建议。进一步解释如下:varmyDate=newDate();我的日期。getYear();//获取当前年份(2位数)myDate getFullYear();//获取完整的年份(4位数,1970-???=0)||);}//----------------------------------------------//日期格式//格式...

flutter vscode+第三方安卓模拟器

1.首先打开夜曲模拟器2.Win+R,选择cmd,在第三方模拟器安装目录的bin目录下输入夜曲模拟器,然后运行命令:nox_Adb.execonnect127.0.0.1:620013。打开项目终端的vscode并建立连接:adbconnect127.00.1:62001(夜神模拟器的默认端口)4。查看连接:adbdevices或不使用第三方模拟器:1.打开...

adb

ADB(AndroidDebugBridge)ANR(ApplicationNoResponding)ADB实际上是Android调试桥AndroidDebugBridge的缩写。adb是C/S体系结构的命令行工具。这里我们介绍一些常用的命令:adbdevices,获取设备列表和设备状态[xuxu:~]$adbdevicesList-devicesattac...