SpringSecurity授权的三种方法详解
编辑@EnableGlobalMethodSecurity三方法详解
要开启Spring
方法级安全,在添加了@Configuration
注解的类上再添加@EnableGlobalMethodSecurity
注解即可
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
其中注解@EnableGlobalMethodSecurity
有几个方法:
prePostEnabled
: 确定 前置注解[@PreAuthorize,@PostAuthorize,..]
是否启用securedEnabled
: 确定安全注解[@Secured]
是否启用jsr250Enabled
: 确定JSR-250注解 [@RolesAllowed..]
是否启用
在同一个应用程序中,可以启用多个类型的注解,但是只应该设置一个注解对于行为类的接口或者类。如:
一个程序启用多个类型注解:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true))
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
但是只应该设置一个注解对于行为类的接口或者类
public interface UserService {
List<User> findAllUsers();
@PreAuthorize("hasAnyRole('user')")
void updateUser(User user);
// 下面不能设置两个注解,如果设置两个,只有其中一个生效
// @PreAuthorize("hasAnyRole('user')")
@Secured({ "ROLE_user", "ROLE_admin" })
void deleteUser();
}
一、启用securedEnabled
先启用securedEnabled
:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true))
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
在调用的接口或方法使用如下:
public interface UserService {
List<User> findAllUsers();
@Secured({"ROLE_user"})
void updateUser(User user);
@Secured({"ROLE_admin", "ROLE_user1"})
void deleteUser();
}
@Secured
注解是用来定义业务方法的安全配置。在需要安全[角色/权限等]的方法上指定 @Secured,并且只有那些角色/权限的用户才可以调用该方法。
@Secured
缺点(限制)就是不支持Spring EL
表达式。不够灵活。并且指定的角色必须以ROLE_
开头,不可省略。
在上面的例子中,updateUser
方法只能被拥有user
权限的用户调用。deleteUser
方法只能够被拥有admin
或者user1
权限的用户调用。而如果想要指定"AND"
条件,即调用deleteUser
方法需同时拥有ADMIN
和DBA
角色的用户,@Secured
便不能实现。
这时就需要使用prePostEnabled
提供的注解@PreAuthorize/@PostAuthorize
二、启用prePostEnabled
先启用prePostEnabled
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
在调用的接口或方法使用:
public interface UserService {
List<User> findAllUsers();
@PostAuthorize ("returnObject.type == authentication.name")
User findById(int id);
@PreAuthorize("hasRole('ADMIN')")
void updateUser(User user);
@PreAuthorize("hasRole('ADMIN') AND hasRole('DBA')")
void deleteUser(int id);
}
该注解更适合方法级的安全,也支持Spring 表达式语言,提供了基于表达式的访问控制。参见常见内置表达式了解支持表达式的完整列表
上面只使用到了一个注解@PreAuthorize
,启用prePostEnabled
后,提供有四个注解:
@PreAuthorize
: 进入方法之前验证授权。可以将登录用户的roles
参数传到方法中验证。
一些用法:
// 只能user角色可以访问
@PreAuthorize ("hasAnyRole('user')")
// user 角色或者 admin 角色都可访问
@PreAuthorize ("hasAnyRole('user') or hasAnyRole('admin')")
// 同时拥有 user 和 admin 角色才能访问
@PreAuthorize ("hasAnyRole('user') and hasAnyRole('admin')")
// 限制只能查询 id 小于 10 的用户
@PreAuthorize("#id < 10")
User findById(int id);
// 只能查询自己的信息
@PreAuthorize("principal.username.equals(#username)")
User find(String username);
// 限制只能新增用户名称为abc的用户
@PreAuthorize("#user.name.equals('abc')")
void add(User user)
@PostAuthorize
: 该注解使用不多,在方法执行后再进行权限验证。 适合验证带有返回值的权限。Spring EL
提供 返回对象能够在表达式语言中获取返回的对象returnObject
。如:
// 查询到用户信息后,再验证用户名是否和登录用户名一致
@PostAuthorize("returnObject.name == authentication.name")
@GetMapping("/get-user")
public User getUser(String name){
return userService.getUser(name);
}
// 验证返回的数是否是偶数
@PostAuthorize("returnObject % 2 == 0")
public Integer test(){
// ...
return id;
}
@PreFilter
: 对集合类型的参数执行过滤,移除结果为false
的元素
// 指定过滤的参数,过滤偶数
@PreFilter(filterTarget="ids", value="filterObject%2==0")
public void delete(List<Integer> ids, List<String> username)
@PostFilter
: 对集合类型的返回值进行过滤,移除结果为false
的元素
@PostFilter("filterObject.id%2==0")
public List<User> findAll(){
...
return userList;
}
对于前面使用@Secured
注解的缺点,现在使用@PreAuthorize/@PostAuthorize
:
public interface UserService {
List<User> findAllUsers();
@PostAuthorize ("returnObject.type == authentication.name")
User findById(int id);
@PreAuthorize("hasRole('ADMIN')")
void updateUser(User user);
@PreAuthorize("hasRole('ADMIN') AND hasRole('DBA')")
void deleteUser(int id);
}
内置表达式:
hasRole([role]) | 如果有当前角色,则返回true(会自动添加 前缀) |
hasAnyRole([role1,role2]) | 如果有任一角色即可通过校验,返回true(会自动添加 前缀) |
hasAuthority([authority]) | 如果有指定权限,则返回true |
hasAnyAuthority([authority1,authority2]) | 如果有指定任一权限,则返回true |
principal | 获取当前用户的主体对象 |
三、启用jsr250Enabled
jsr250Enabled注解比较简单,只有
@DenyAll
: 拒绝所有访问@RolesAllowed({"USER", "ADMIN"})
: 该方法只要具有"USER"
,"ADMIN"
任意一种权限就可以访问。这里可以省略前缀ROLE_
,实际的权限可能是ROLE_ADMIN
@PermitAll
: 允许所有访问
SpringSecurity中角色权限的区别
我们先来看 org.springframework.security.core.userdetails.User 中的一段代码:
public UserBuilder roles(String... roles) {
List<GrantedAuthority> authorities = new ArrayList<>(roles.length);
for (String role : roles) {
Assert.isTrue(!role.startsWith("ROLE_"), () -> role
+ " cannot start with ROLE_ (it is automatically added)");
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
}
return authorities(authorities);
}
代码中 UserBuilder 在设置角色的时候会自动在角色前面添加 "ROLE_" 并把设置的角色信息保存到 List<GrantedAuthority> 权限集合中,从这看出用户的角色权限信息都保证一个集合中,区别就是用户角色有 "ROLE_" 前缀。
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Set<GrantedAuthority> authorities = new HashSet<>();
authorities.add(new SimpleGrantedAuthority("ADMIN"));
User user = new User("admin", "admin123", authorities);
return user;
}
那么我们在鉴权的时候要注意了:如果我们使用 hasRole([role]) 鉴权,系统会自动添加ROLE_
前缀,然后去用户权限集合进行对比,如果我们给用户配置权限是通过上面那个方法 authorities.add(new SimpleGrantedAuthority("ADMIN")) 配置, 这种方式是鉴权不到用户角色的,设置用户角色的时候需要这样authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")) 配置,就可以设置为角色权限了。
如果使用 hasAuthority([authority]) 鉴权,就不用在设置用户权限的时候加ROLE_
前缀了。
- 0
- 0
-
赞助
赞赏 -
分享