@Secured(), @PreAuthorize()


前面簡單的提到過這兩個注解的區別,那只是從配置以及原理上做的說明,今天,將從使用即代碼層面加以說明這兩個的使用注意事項!

 

首先, 若是自己實現用戶信息數據庫存儲的話,需要注意UserDetails的函數(下面代碼來自於Spring boot 1.2.7 Release的依賴 Spring security 3.2.8):

1     /**
2      * Returns the authorities granted to the user. Cannot return <code>null</code>.
3      *
4      * @return the authorities, sorted by natural key (never <code>null</code>)
5      */
6     Collection<? extends GrantedAuthority> getAuthorities();

在我的MUEAS項目中,這個接口函數的實現是下面這個樣子的:

 1 public class SecuredUser extends User implements UserDetails{
 2 
 3     private static final long serialVersionUID = -1501400226764036054L;
 4     
 5     private User user;    
 6     public SecuredUser(User user){
 7         if(user != null){
 8             this.user = user;    
 9             this.setUserId(user.getId());
10             this.setUserId(user.getUserId());
11             this.setUsername(user.getUsername());    
12             this.setPassword(user.getPassword());
13             this.setRole(user.getRole().name());
14             //this.setDate(user.getDate());
15             
16             this.setAccountNonExpired(user.isAccountNonExpired());
17             this.setAccountNonLocked(user.isAccountNonLocked());
18             this.setCredentialsNonExpired(user.isCredentialsNonExpired());
19             this.setEnabled(user.isEnabled());            
20         }
21     }
22     
23     public void setUser(User user){
24         this.user = user;
25     }
26     
27     public User getUser(){
28         return this.user;
29     }
30 
31     @Override
32     public Collection<? extends GrantedAuthority> getAuthorities() {
33         Collection<GrantedAuthority> authorities = new ArrayList<>();
34         Preconditions.checkNotNull(user, "user在使用之前必須給予賦值");
35         Role role = user.getRole();
36         
37         if(role != null){
38             SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.name());
39             authorities.add(authority);        
40         }
41         return authorities;
42     }    
43 }

注意,我在創建SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.name());的時候沒有添加“ROLE_”這個rolePrefix前綴,也就是說,我沒有像下面這個樣子操作:

 1         @Override
 2     public Collection<? extends GrantedAuthority> getAuthorities() {
 3         Collection<GrantedAuthority> authorities = new ArrayList<>();
 4         Preconditions.checkNotNull(user, "user在使用之前必須給予賦值");
 5         Role role = user.getRole();
 6         
 7         if(role != null){
 8             SimpleGrantedAuthority authority = new SimpleGrantedAuthority(“ROLE_”+role.name());
 9             authorities.add(authority);        
10         }
11         return authorities;
12     }    

不要小看這一區別,這個將會影響后面權限控制的編碼方式。

具體說來, 若采用@EnableGlobalMethodSecurity(securedEnabled = true)注解,對函數訪問進行控制,那么,就會有一些問題(不加ROLE_),因為,這個時候,AccessDecissionManager會選擇RoleVoter進行vote,但是RoleVoter默認的rolePrefix是“ROLE_”。

當函數上加有@Secured(),我的項目中是@Secured({"ROLE_ROOT"})

 1     @RequestMapping(value = "/setting/username", method = RequestMethod.POST)    
 2     @Secured({"ROLE_ROOT"})  3     @ResponseBody
 4     public Map<String, String> userName(User user, @RequestParam(value = "username") String username){    
 5         Map<String, String> modelMap = new HashMap<String, String>();    
 6         System.out.println(username);    
 7                 
 8         user.setUsername(username);
 9         userService.update(user);
10         
11         
12         modelMap.put("status", "ok");
13         return modelMap;
14     }

而RoleVoter選舉時,會檢測是否支持。如下函數(來自Spring Security 3.2.8 Release默認的RoleVoter類)

1 public boolean supports(ConfigAttribute attribute) {
2         if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
3             return true;
4         }
5         else {
6             return false;
7         }
8     }

上面的函數會返回true,因為傳遞進去的attribute是來自於@Secured({"ROLE_ROOT"})注解。不幸的時,當進入RoleVoter的vote函數時,就失敗了:

 1 public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
 2         int result = ACCESS_ABSTAIN;
 3         Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
 4 
 5         for (ConfigAttribute attribute : attributes) {
 6             if (this.supports(attribute)) {
 7                 result = ACCESS_DENIED;
 8 
 9                 // Attempt to find a matching granted authority
10                 for (GrantedAuthority authority : authorities) {
11                     if (attribute.getAttribute().equals(authority.getAuthority())) {
12                         return ACCESS_GRANTED;
13                     }
14                 }
15             }
16         }
17 
18         return result;
19     }

原因在於,authority.getAuthority()返回的將是ROOT,而並不是ROLE_ROOT。然而,即使將@Secured({"ROLE_ROOT"})改為@Secured({"ROOT"})也沒有用, 所以,即使當前用戶是ROOT權限用戶,也沒有辦法操作,會放回403 Access Denied Exception.

解決的辦法:有兩個。

第一個: 就是將前面提到的UserDetails的接口函數getAuthorities()的實現中,添加前綴,如上面提到的,紅色"ROLE_"+role.name()

第二個: 就是不用@Secured()注解,采用@PreAuthorize():

1 /**
2  * Method Security Configuration.
3  */
4 @EnableGlobalMethodSecurity(prePostEnabled = true) //替換掉SecuredEnabled = true
5 @Configuration
6 public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
7 
8 }

上面的修改,將會實現AccessDecissionManager列表中AccessDecisionVoter,多出一個voter,即PreInvocationAuthorizationAdviceVoter.

並且修改函數上的注解:

 1     @RequestMapping(value = "/setting/username", method = RequestMethod.POST)    
 2     @PreAuthorize("hasRole('ROOT')") //或則@PreAuthorize("hasAuthority('ROOT')")  3     @ResponseBody
 4     public Map<String, String> userName(User user, @RequestParam(value = "username") String username){    
 5         Map<String, String> modelMap = new HashMap<String, String>();    
 6         System.out.println(username);    
 7                 
 8         user.setUsername(username);
 9         userService.update(user);
10         
11         
12         modelMap.put("status", "ok");
13         return modelMap;
14     }

這樣的話,就可以正常實現函數級別的權限控制了。

 

是不是有點繞?反正這個問題折騰了我差不多一上午。。。。

 


免責聲明!

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



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