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

首先, 若是自己实现用户信息数据库存储的话,需要注意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. Day04_JAVA语言基础第四天

    1.循环(掌握) 1.什么时候使用(理解) 如果我们发现有很多重复内容的时候就要使用循环 2.好处(理解) 让我们的代码看起来更精炼了 3.循环的组成(理解) 1 初始化条件:一般定义的是一个初始变量 ...

  2. 蓝牙SIG

    蓝牙SIG 蓝牙SIG是一个国际性的非营利组织,它的目的是制定蓝牙的技术规范和推广蓝牙技术的应用.该组织由发起会员(Promoter).合作会员(Associate Member)和接受会员(Adop ...

  3. python 处理异常

    try: ...(主要动作,试着执行的程序代码,如果引发异常,执行第一个复合引发异常的except下面的语句.如果没有符合的,就会终止程序,打印出错!) except name:(except 分句的 ...

  4. Eclipse 插件安装方法和插件加载失败解决办法

    一:是利用Eclipse Software  Update 添加网址,让Eclipse 自动的搜索下载最新的插件. 比如安装VE这个可视化编辑UI的插件,其步骤为 Help > Software ...

  5. 222. Count Complete Tree Nodes

    Given a complete binary tree, count the number of nodes. Definition of a complete binary tree from W ...

  6. 修饰符(static、final、abstract)第一篇

    三个修饰符: 一.static: 作用域: 1. 属性 1.1 静态属性不必要创建新对象,可直接用类调用 1.2 其值发生改变,则类中的值也会随之而变并延伸到其他对象中 例子: class Anima ...

  7. SQL参数化查询--最有效可预防SQL注入攻击的防御方式

    参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值. 在使用参 ...

  8. Windows PE 之 MASM32 环境搭建

    操作系统 :Windows 10 IDE   :VS2015 MASM版本   :V11 MASM下载地址:http://masm32.com masm32安装一路确认.OK (由于win10兼容性问 ...

  9. 1-2 ISO/OSI七层模型简介

    相关名词解释: ISO:国际标准化组织 OSI:开放系统互联模型 IOS:苹果操作系统, 但是在计算机网络中,IOS是互联网操作系统,是思科公司为其网络设备开发的操作维护系统 <1>OSI ...

  10. apache开启url rewrite模块

    在把服务器数据转移到本地服务器之后,本地打开首页出现排版紊乱等问题,经过大神指点说是url rewrite的问题. 本篇文章主要写怎样开启apache的url rewrite功能. 打开Apache2 ...