@Secured(), @PreAuthorize()
前面简单的提到过这两个注解的区别,那只是从配置以及原理上做的说明,今天,将从使用即代码层面加以说明这两个的使用注意事项!
首先, 若是自己实现用户信息数据库存储的话,需要注意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()的更多相关文章
- 区别: @Secured(), @PreAuthorize() 及 @RolesAllowed()
在Spring security的使用中,为了对方法进行权限控制,通常采用的三个注解,就是@Secured(), @PreAuthorize() 及 @RolesAllowed(). 但是着三者之间的 ...
- Spring Security 4 Method security using @PreAuthorize,@PostAuthorize, @Secured, EL--转
原文地址:http://websystique.com/spring-security/spring-security-4-method-security-using-preauthorize-pos ...
- Spring Security 4 使用@PreAuthorize,@PostAuthorize, @Secured, EL实现方法安全
[相关已翻译的本系列其他文章,点击分类里面的spring security 4] 上一篇:Spring Security 4 整合Hibernate 实现持久化登录验证(带源码) 原文地址:http: ...
- 【Spring】关于Boot应用中集成Spring Security你必须了解的那些事
Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...
- 关于Boot应用中集成Spring Security你必须了解的那些事
Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...
- Spring Boot中集成Spring Security 专题
check to see if spring security is applied that the appropriate resources are permitted: @Configurat ...
- 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 ...
- @Secured()、 @PreAuthorize() 、 @RolesAllowed()
在Spring security的使用中,为了对方法进行权限控制,通常采用的三个注解,就是@Secured().@PreAuthorize().@RolesAllowed(). 示例,修改用户密码必须 ...
- spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403
1.前言 以前学习的时候使用权限的拦截,一般都是对路径进行拦截 ,要么用拦截器设置拦截信息,要么是在配置文件内设置拦截信息, spring security 支持使用注解的形式 ,写在方法和接口上拦截 ...
随机推荐
- why we need virtual key word
http://stackoverflow.com/questions/2391679/why-do-we-need-virtual-methods-in-c 简言之,声明基类时,而实际指向派生类.如果 ...
- LK 光流法简介
前言 若假定一个局部区域的像素运动是一致的,则可以用这个新的约束条件替代前文中提到的全局速度平滑约束条件.这种光流算法就叫做 LK 光流法. LK 光流法的推导 首先,需要推导出光流约束方程. 这一步 ...
- cuffdiff 和 edgeR 对差异表达基因的描述
ASE又走到了关键的一步 要生成能决定是否有差异表达的table. 准备借鉴一下cuffdiff和edgeR 的结果 cuffdiff对差异表达基因的描述: 一共十四列: 第一列, test_id ...
- Linux的服务器初始优化脚本。
#!/bin/bash #优化服务器启动的脚本.禁用无关账号.无关服务 #Lock User Account passwd -l xfs passwd -l news passwd -l nscd p ...
- xmind的第二天笔记
- this指针基础介绍
=================this指针的由来==================== 一个学生可以有多本书一样,而这些书都是属于这个同学的:同理,如果有很多个同学在一起,那么为了确定他们的书不 ...
- jq中 load()方法 简介
load()方法会在元素的onload事件中绑定一个处理函数.如果处理函数绑定给window对象,则会在所有内容(包括窗口,框架,对象和图像等)加载完毕后触发,如果处理函数绑定在元素上,则会在元素的内 ...
- jq 全选/取消效果
//全选框$('#btnbutton').live('click',function(){ var data = $(this).attr('data'); if(data=='on'){ $(&qu ...
- zboot/piggyback.c
/* * linux/zBoot/piggyback.c * * (C) 1993 Hannu Savolainen */ /* * This program reads the c ...
- resin的基本操作
1.什么是resin? resin是CAUCHO公司的产品,是一个非常流行的支持servlets和jsp的引擎,速度非常快.Resin本身包含了一个支持HTTP/1.1的WEB服务器.虽然它可 ...