@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 支持使用注解的形式 ,写在方法和接口上拦截 ...
随机推荐
- 收藏:关于UseSubmitBehavior和OnClientClick同时使用,导致无法触发后台事件的问题
经常会有正样的需求,在用户做一个不易恢复并且容易误操作的动作时需要给用户以提示,用户确认后继续执行动作. 简单的解决方案是:前台用OnClientClick事件中使用Confirm给用弹出确认提示框, ...
- xampp访问403 Access forbidden 解决办法
本地可以访问,换一台机子就不行了,是因为权限没有开启,安装目录xampp\apache\conf\extra内有个httpd-xampp.conf文件,打开, 最后一段是 # # New XAMPP ...
- PostgresSQL 学习资料记录处
PostgresSQL 学习资料记录处 博客:http://francs3.blog.163.com PostgreSQL9.4 中文手册:http://www.postgres.cn/docs/9 ...
- hdu 5091 Beam Cannon
题目大意: 有n个点(n<=10000),点的坐标绝对值不超过20000,然后问你用一个w*h(1<=w,h<=40000)的矩形,矩形的边平行于坐标轴,最多能盖住多少个点. 刘汝佳 ...
- [转载]NoSQL by Martin Flower
============================================================== URL1 nosql ========================== ...
- 感知机-Python实现
如图3所示的训练数据集,其正实例点是(3,3),(3,4),负实例点是(1,1),试用感知机学习算法的原始形式求感知机模型,即求出w和b.这里, 图3 这里我们取初值,取.具体问题解释不写了,求解的方 ...
- Jlink V7在MDK下使用Cortex-M3-Jlink模式开发STM32的说明
Jlink V7在MDK下使用Cortex-M3-Jlink模式开发STM32的说明 开发环境:STM32F103RB(128K Flash 20K RAM)+MDK3.50+JLINK V7(v ...
- resin的基本操作
1.什么是resin? resin是CAUCHO公司的产品,是一个非常流行的支持servlets和jsp的引擎,速度非常快.Resin本身包含了一个支持HTTP/1.1的WEB服务器.虽然它可 ...
- scala言语基础学习二
使用yield和函数式编程转化数组 算法案例(移除第一个负数之后的所有负数) 改良高校方案
- php下载c
1.php下载中,不认识的类型比如zip,rar,rmvb等可以直接点击链接下载<a href="one.zip">one.zip</a>. 2.php下载 ...