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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM