@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     }

这样的话,就可以正常实现函数级别的权限控制了。

是不是有点绕?反正这个问题折腾了我差不多一上午。。。。

时间: 2024-10-05 20:56:28

@Secured(), @PreAuthorize()的相关文章

区别: @Secured(), @PreAuthorize() 及 @RolesAllowed()

在Spring security的使用中,为了对方法进行权限控制,通常采用的三个注解,就是@Secured(), @PreAuthorize() 及 @RolesAllowed(). 但是着三者之间的区别,我之前也不是很清楚,现在看看,做个小小的记录,备忘吧! 现在举例,比如修改用户密码,必须是ADMIN的权限才可以.则可以用下面三种方法: @Secured({"ROLE_ADMIN"}) public void changePassword(String username, Stri

Spring Security 4 使用@PreAuthorize,@PostAuthorize, @Secured, EL实现方法安全(带源码)

[相关已翻译的本系列其他文章,点击分类里面的spring security 4] 上一篇:Spring Security 4 整合Hibernate 实现持久化登录验证(带源码) 原文地址:http://websystique.com/spring-security/spring-security-4-method-security-using-preauthorize-postauthorize-secured-el/ 本文探讨Spring Security 4 基于@PreAuthorize

Spring Security应用开发(21)基于方法的授权(五)使用@Secured注解

Spring Security提供了@Secured注解来实现基于方法的授权控制. @Secured注解可以指定一个字符串数组参数作为value的值,表示当前用户具备这些角色中的任何一个角色即可满足授权条件. (1)启用@Secured注解. <sec:global-method-security secured-annotations="enabled" /> (2)使用Secured注解. //getUserByName()方法可以被具备ROLE_ADMIN或者ROLE

Enable Kerberos secured Hadoop cluster with Cloudera Manager

I created an secured Hadoop cluster for P&G with cloudera manager, and this document is to record how to enable kerberos secured cluster with cloudera manager. Firstly we should have a cluster that contains kerberos KDC and kerberos clients 1. Instal

Spring Security @PreAuthorize 拦截无效

1. 在使用spring security的时候使用注解,@PreAuthorize("hasAnyRole('ROLE_Admin')") 放在对方法的访问权限进行控制失效,其中配置如: @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserDetailsService userDetailsService;

spring security中@PreAuthorize注解的使用

添加依赖<!-- oauth --><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId&g

Secured RESTful API that can be used by Web App

You seem to be confusing/merging two different concepts together. We start of talking about encrypting traffic (HTTPS) and then we start talking about different ways to manage authenticated sessions. In a secure application these are not mutually exc

REST Security with JWT using Java and Spring Security

Security Security is the enemy of convenience, and vice versa. This statement is true for any system, virtual or real, from the physical house entrance to web banking platforms. Engineers are constantly trying to find the right balance for the given

007Spring Security

01.基于Spring AOP 和 Servlet规范中Filter实现  的安全框架 <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.2.3.BUILD-SNAPSHOT</version> </dependency> 02.