前面简单的提到过这两个注解的区别,那只是从配置以及原理上做的说明,今天,将从使用即代码层面加以说明这两个的使用注意事项!

首先, 若是自己实现用户信息数据库存储的话,需要注意UserDetails的函数(下面代码来自于Spring boot 1.2.7 Release的依赖 Spring security 3.2.8):

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

在我的MUEAS项目中,这个接口函数的实现是下面这个样子的:

 public class SecuredUser extends User implements UserDetails{

     private static final long serialVersionUID = -1501400226764036054L;

     private User user;
public SecuredUser(User user){
if(user != null){
this.user = user;
this.setUserId(user.getId());
this.setUserId(user.getUserId());
this.setUsername(user.getUsername());
this.setPassword(user.getPassword());
this.setRole(user.getRole().name());
//this.setDate(user.getDate()); this.setAccountNonExpired(user.isAccountNonExpired());
this.setAccountNonLocked(user.isAccountNonLocked());
this.setCredentialsNonExpired(user.isCredentialsNonExpired());
this.setEnabled(user.isEnabled());
}
} public void setUser(User user){
this.user = user;
} public User getUser(){
return this.user;
} @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
Preconditions.checkNotNull(user, "user在使用之前必须给予赋值");
Role role = user.getRole(); if(role != null){
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.name());
authorities.add(authority);
}
return authorities;
}
}

注意,我在创建SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.name());的时候没有添加“ROLE_”这个rolePrefix前缀,也就是说,我没有像下面这个样子操作:

         @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
Preconditions.checkNotNull(user, "user在使用之前必须给予赋值");
Role role = user.getRole(); if(role != null){
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(“ROLE_”+role.name());
authorities.add(authority);
}
return authorities;
}

不要小看这一区别,这个将会影响后面权限控制的编码方式。

具体说来, 若采用@EnableGlobalMethodSecurity(securedEnabled = true)注解,对函数访问进行控制,那么,就会有一些问题(不加ROLE_),因为,这个时候,AccessDecissionManager会选择RoleVoter进行vote,但是RoleVoter默认的rolePrefix是“ROLE_”。

当函数上加有@Secured(),我的项目中是@Secured({"ROLE_ROOT"})

     @RequestMapping(value = "/setting/username", method = RequestMethod.POST)
@Secured({"ROLE_ROOT"})
@ResponseBody
public Map<String, String> userName(User user, @RequestParam(value = "username") String username){
Map<String, String> modelMap = new HashMap<String, String>();
System.out.println(username); user.setUsername(username);
userService.update(user); modelMap.put("status", "ok");
return modelMap;
}

而RoleVoter选举时,会检测是否支持。如下函数(来自Spring Security 3.2.8 Release默认的RoleVoter类)

 public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
}
else {
return false;
}
}

上面的函数会返回true,因为传递进去的attribute是来自于@Secured({"ROLE_ROOT"})注解。不幸的时,当进入RoleVoter的vote函数时,就失败了:

 public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
int result = ACCESS_ABSTAIN;
Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication); for (ConfigAttribute attribute : attributes) {
if (this.supports(attribute)) {
result = ACCESS_DENIED; // Attempt to find a matching granted authority
for (GrantedAuthority authority : authorities) {
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
} return result;
}

原因在于,authority.getAuthority()返回的将是ROOT,而并不是ROLE_ROOT。然而,即使将@Secured({"ROLE_ROOT"})改为@Secured({"ROOT"})也没有用, 所以,即使当前用户是ROOT权限用户,也没有办法操作,会放回403 Access Denied Exception.

解决的办法:有两个。

第一个: 就是将前面提到的UserDetails的接口函数getAuthorities()的实现中,添加前缀,如上面提到的,红色"ROLE_"+role.name()

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

 /**
* Method Security Configuration.
*/
@EnableGlobalMethodSecurity(prePostEnabled = true) //替换掉SecuredEnabled = true
@Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { }

上面的修改,将会实现AccessDecissionManager列表中AccessDecisionVoter,多出一个voter,即PreInvocationAuthorizationAdviceVoter.

并且修改函数上的注解:

     @RequestMapping(value = "/setting/username", method = RequestMethod.POST)
@PreAuthorize("hasRole('ROOT')") //或则@PreAuthorize("hasAuthority('ROOT')")
@ResponseBody
public Map<String, String> userName(User user, @RequestParam(value = "username") String username){
Map<String, String> modelMap = new HashMap<String, String>();
System.out.println(username); user.setUsername(username);
userService.update(user); modelMap.put("status", "ok");
return modelMap;
}

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

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

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

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

    在Spring security的使用中,为了对方法进行权限控制,通常采用的三个注解,就是@Secured(), @PreAuthorize() 及 @RolesAllowed(). 但是着三者之间的 ...

  2. Spring Security 4 Method security using @PreAuthorize,@PostAuthorize, @Secured, EL--转

    原文地址:http://websystique.com/spring-security/spring-security-4-method-security-using-preauthorize-pos ...

  3. Spring Security 4 使用@PreAuthorize,@PostAuthorize, @Secured, EL实现方法安全

    [相关已翻译的本系列其他文章,点击分类里面的spring security 4] 上一篇:Spring Security 4 整合Hibernate 实现持久化登录验证(带源码) 原文地址:http: ...

  4. 【Spring】关于Boot应用中集成Spring Security你必须了解的那些事

    Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...

  5. 关于Boot应用中集成Spring Security你必须了解的那些事

    Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...

  6. Spring Boot中集成Spring Security 专题

    check to see if spring security is applied that the appropriate resources are permitted: @Configurat ...

  7. Security注解:@PreAuthorize,@PostAuthorize, @Secured, EL实现方法安全

     说明 (1)JDK版本:1.8(2)Spring Boot 2.0.6(3)Spring Security 5.0.9(4)Spring Data JPA 2.0.11.RELEASE(5)hibe ...

  8. @Secured()、 @PreAuthorize() 、 @RolesAllowed()

    在Spring security的使用中,为了对方法进行权限控制,通常采用的三个注解,就是@Secured().@PreAuthorize().@RolesAllowed(). 示例,修改用户密码必须 ...

  9. spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403

    1.前言 以前学习的时候使用权限的拦截,一般都是对路径进行拦截 ,要么用拦截器设置拦截信息,要么是在配置文件内设置拦截信息, spring security 支持使用注解的形式 ,写在方法和接口上拦截 ...

随机推荐

  1. MySQL优化—工欲善其事,必先利其器之EXPLAIN(转)

    最近慢慢接触MySQL,了解如何优化它也迫在眉睫了,话说工欲善其事,必先利其器.最近我就打算了解下几个优化MySQL中经常用到的工具.今天就简单介绍下EXPLAIN. 内容导航 id select_t ...

  2. php中将url中的参数含有%20进行转换或解码

    我的url: .......index.php?action=search&start=12&search=star wave&orderby=categories&s ...

  3. Flume NG之Interceptor简介

    转载地址:http://www.cnblogs.com/lxf20061900/p/3658172.html 有的时候希望通过Flume将读取的文件再细分存储,比如讲source的数据按照业务类型分开 ...

  4. Winform 关于委托与Invoke和Begin Invoke的使用

    这方面的文章已经写得很详细了,特地摘引两篇文章 http://www.cnblogs.com/c2303191/articles/826571.html http://www.cnblogs.com/ ...

  5. python内存管理机制

    主要分为三部分: (1)内存池机制(2)引用计数(3)垃圾回收 (1)内存池机制对于python来说,对象的类型和内存都是在运行时确定的,所以python对象都是动态类型简单来说,python内存分为 ...

  6. uva562 Dividing coins 01背包

    link:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  7. vimrc配置文件_version1.0_+pathogen, taglist, wordcomplete插件说明

    为了表示对Ruchee的感谢,首先这是Ruchee的个人网站:http://www.ruchee.com/index.html,他的以前很多的代码都放到Git里面了,里面有链接. 看了整整一天,刚开始 ...

  8. HTML DOM部分---做竖向横向的下拉导航 下拉菜单 图片轮播(圆点、箭头) 选项卡 进度条;

    1,竖向下拉导航 鼠标单击打开 再打击关闭 <style> *{ margin:0px auto; padding:0px;} div{ width:100px; height:50px; ...

  9. codevs 1704 卡片游戏

    题目描述 Description 桌面上有一叠牌,从第一张牌(即位于顶面的牌)开始从上往下依次编号为1~n.当至少还剩两张排时进行一下操作:把第一张牌扔掉,然后把新的第一张牌放到整叠牌的最后.输入n. ...

  10. linux 下查看机器是cpu是几核的(转)

    几个cpu more /proc/cpuinfo |grep "physical id"|uniq|wc -l 每个cpu是几核(假设cpu配置相同) more /proc/cpu ...