zoukankan      html  css  js  c++  java
  • Spring Security 之方法级的安全管控

    默认情况下, Spring Security 并不启用方法级的安全管控. 启用方法级的管控后, 可以针对不同的方法通过注解设置不同的访问条件.
    Spring Security 支持三种方法级注解, 分别是 JSR-205 注解/@Secured 注解/prePostEnabled注解. 这些注解不仅可以直接加 controller 方法上, 也可以注解 Service 或 DAO 类中的方法. 

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

    ===================================
    JSR-205 注解
    ===================================
    通过 @EnableGlobalMethodSecurity(jsr250Enabled=true), 开启 JSR-205 注解.

    @DenyAll 注解, 拒绝所有的访问
    @PermitAll 注解, 运行所有访问
    @RolesAllowed({"USER","ADMIN"}), 该方法只允许有 ROLE_USER 或 ROLE_ADMIN 角色的用户访问.


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


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

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


    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)   
       

    ===================================
    示例代码
    ===================================

    ----------------------------
    pom.xml
    ----------------------------

    <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");
        }
    }

    ----------------------------
    Entity 和 Controller 类
    ----------------------------
    Entity 和 Controller 类没有特别之处, 这里不用太关注.

    @RestController("/")
    class BookController{
    
        @Autowired
        private BookService bookService ;
    
        @GetMapping("/get")
        public String getBook() {
            Book book =bookService.getBook() ;
            return book.getName() ;
        }
    }
    
    class Book{
        private String name;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Book(String name) {
            this.name=name ;
        }
    }
    View Code

    ----------------------------
    测试结果
    ----------------------------
    使用 123 用户(角色是 USER )登陆, 可以访问 http://localhost:8080/get ; 使用 124 用户(角色是 USER2 )登陆, 访问 http://localhost:8080/get 报下面的403权限问题. 符合预期

     

    ===================================
    参考
    ===================================
    https://www.concretepage.com/spring/spring-security/preauthorize-postauthorize-in-spring-security
    https://www.jianshu.com/p/bcb3c6445b2b
    https://www.jianshu.com/p/41b7c3fb00e0
    https://blog.csdn.net/l18767118724/article/details/72934564
    https://spring.io/guides/topicals/spring-security-architecture/
    https://www.logicbig.com/tutorials/spring-framework/spring-security/roles-allowed-annotation.html

  • 相关阅读:
    Zepto源码分析-动画(fx fx_method)模块
    CSS3用法理解
    移动端网页meta设置和响应式
    javascript基础-正则表达式
    net core 2.x
    angular
    .net core 2.x
    .net core 2.x
    DDD
    DDD
  • 原文地址:https://www.cnblogs.com/harrychinese/p/SpringBoot_security1.html
Copyright © 2011-2022 走看看