Spring Security 之方法级的权限管控 @PreAuthorize 使用详解

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

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

启用方法级的管控代码是, 新建一个 WebSecurityConfigurerAdapter Configuration 类, 加上 @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 表达式)

3.1 SPEL 表达试(bean 引用)

如果解析上下文已经配置,那么 bean 解析器能够 从表达式使用(@)符号查找 bean 类。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());

// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@foo").getValue(context);

@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();

3.2 @PreAuthorize 表达式

3.2.1. returnObject 保留名

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

比如:

@PostAuthorize ("returnObject.owner == authentication.name")
public Book getBook();
3.2.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);

3.3. 内置表达式有:

表达式 备注
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)

例子

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

SecurityConfig 配置类

SecurityConfig 配置类开启方法级管控 (仅启用 prePostEnabled 类的注解), 并 hard coded 了一个内置的用户清单.

因为没有重载 configure(HttpSecurity http) 方法, 用的是 Spring security 的 basic Authentication 和内置的 login form.

@EnableWebSecurity@Configuation
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Override
    public void configure(AuthenticationManagerBuilder builder)
            throws Exception {
        builder.inMemoryAuthentication()
               .withUser("123").password("123").roles("USER")
               .and()
               .withUser("ADMIN").password("ADMIN").roles("ADMIN")
               .and()
               .withUser("124").password("124").roles("USER2");
    }

    @SuppressWarnings("deprecation")
    @Bean
    public NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }
}

BookService 配置类

为 BookService Service 类的方法加上 @PreAuthorize 注解, 对权限进行控制.

@Service
class BookService{
    @PreAuthorize("hasRole('ADMIN')")
    public void addBook(Book book) {
        System.out.println("you have added a book successfully");
    }

    @PreAuthorize("hasAnyRole('ADMIN','USER')")
    public Book getBook() {
        Book book=new Book("A");
        return book ;
    }

    @PreAuthorize("hasRole('ADMIN')")
    public void deleteBook(Book book) {
        System.out.println("Book deleted");
    }
}
  • spring security 添加自定义动态权限

  • spring security 兼容新版本 5.7

  • security CorsFilter 添加 order 最新执行,避免其他filter提前返回

  • elasticsearch 添加 JsonpMapper 扩展

  • web-spring-boot-starter: 添加404全局错误

  • e6f0bc99(feat(mybatis-plus-boot-starter): 添加租户开关配置,默认关)

  • 2632aa7f(fix(oss): 修改私有访问的 bucket previewUrl 链接的拼接)

  • 29d34be9(fix(分页工厂类): 当前页数错误)

  • 622293bb(pagehelper 版本升级)

相关推荐
郑州吴彦祖77212 分钟前
【java】数据类型与变量以及操作符
java·intellij-idea
程序员大金13 分钟前
基于SpringBoot+Vue+MySQL的在线学习交流平台
java·vue.js·spring boot·后端·学习·mysql·intellij-idea
吹老师个人app编程教学18 分钟前
阿里巴巴_java开发规范手册详解
java·开发语言
天上掉下来个程小白19 分钟前
Stream流的终结方法(一)
java·windows
qq_25183645720 分钟前
基于SpringBoot vue 医院病房信息管理系统设计与实现
vue.js·spring boot·后端
天上掉下来个程小白41 分钟前
请求响应-08.响应-案例
java·服务器·前端·springboot
大白_dev41 分钟前
数据校验的总结
java·开发语言
失落的香蕉1 小时前
Java第二阶段---10方法带参---第三节 面向对象和面向过程的区别
java·开发语言
qq_2518364571 小时前
基于springboot vue3 在线考试系统设计与实现 源码数据库 文档
数据库·spring boot·后端
哎呀呀嗯呀呀1 小时前
class 031 位运算的骚操作
java·算法·位运算