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

首先, 若是自己实现用户信息数据库存储的话,需要注意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. 关于OC队列

    GCD中有三种队列类型: The main queue: 与主线程功能相同.实际上,提交至main queue的任务会在主线程中执行.main queue可以调用dispatch_get_main_q ...

  2. Shell 条件表达式汇总

    条件表达式 文件表达式 if [ -f  file ]    如果文件存在if [ -d ...   ]    如果目录存在if [ -s file  ]    如果文件存在且非空 if [ -r f ...

  3. What are the 10 algorithms one must know in order to solve most algorithm challenges/puzzles?

    QUESTION : What are the 10 algorithms one must know in order to solve most algorithm challenges/puzz ...

  4. PCV 学习笔记-ch1 主成分分析实现

    模块名称:pca.py PCA原理与紧致技巧原理待补... #-*-coding:UTF-8-*- ''' Created on 2015年3月2日 @author: Ayumi Phoenix ch ...

  5. Win7下安装双系统Centos,并修复Centos引导加载程序安装在U盘上的问题

    1.使用U盘安装Centos时,磁盘分区划分要注意:系统(包含Win7)只能4个主分区,所以只能在删除一个主分区或者在扩展分区的空闲分区内建立目录. 2.Centos在安装步骤的最后,引导加载程序的选 ...

  6. Codeforces Round #260 (Div. 2) A B C 水 找规律(大数对小数取模) dp

    A. Laptops time limit per test 1 second memory limit per test 256 megabytes input standard input out ...

  7. jsp不能引用js,cs等解决办法

    最近项目中使用到Spring3,在感叹Spring3注解配置清爽的同时竟然出现了这个不和谐的事情,实在无法忍受 问题:部署项目后程序加载或用浏览器访问时出现类似的警告,2011-01-19 10:52 ...

  8. 重启Tomcat的脚本

    说明:一台服务器上跑了8个Tomcat case的方式: #!/bin/bash #reboot tomcat!!! #Author:fansik echo -e "\033[1;42;31 ...

  9. resin的基本操作

    1.什么是resin?     resin是CAUCHO公司的产品,是一个非常流行的支持servlets和jsp的引擎,速度非常快.Resin本身包含了一个支持HTTP/1.1的WEB服务器.虽然它可 ...

  10. Windows编程基础

    主要内容:介绍Windows编程的一些基础概念 1.窗口的概念 <1>一个应用程序的窗口通常包括控制菜单框.下拉菜单. 工作区以及最大化按钮.最小化按钮, 还有垂直滚动条.水平滚动条 &l ...