Spring Security权限控制可以配合授权注解使用,Spring Security提供了三种不同的安全注解:
- Spring Security自带的@Secured注解;
- JSR-250的@DenyAll、@RolesAllowed、@PermitAll注解;
- 表达式驱动的注解,包括@PreAuthorize、@PostAuthorize、@PreFilter和 @PostFilter;
一、权限控制注解
1、@Secured注解
在方法上指定安全性要求,只有对应角色的用户才可以调用这些方法:
import org.springframework.security.access.annotation.Secured; public interface UserService { ListfindAllUsers(); @Secured("ROLE_ADMIN") void updateUser(User user); @Secured({ "ROLE_DBA", "ROLE_ADMIN" }) void deleteUser(); }
权限不足时,方法抛出Access Denied异常。@Secured注解会使用一个String数组作为参数。每个String值是一个权限,调用这个方法至少需要具备其中的一个权限。
2、@RolesAllowed
- @DenyAll拒绝所有访问;
- @RolesAllowed注解和@Secured注解在各个方面基本上都是一致的,@RolesAllowed({"ROLE_USER","ROLE_ADMIN"})该方法只要具有“ROLE_USER","ROLE_ADMIN"任意一种权限就可以访问;
- @PermitAll允许所有访问;
3、SpEL注解
要开启表达式驱动的注解,只需要在Spring Security配置文件中添加如下注解:
@EnableGlobalMethodSecurity(prePostEnabled = true)
(1)、@PreAuthorize
该注解用于方法前验证权限,比如限制非VIP用户提交blog的note字段字数不得超过1000字:
@PreAuthorize("hasRole('ROLE_ADMIN') and #form.note.length() <= 1000 or hasRole('ROLE_VIP')") public void writeBlog(Form form){ ... }
表达式中的#form部分直接引用了方法中的同名参数。这使得Spring Security能够检查传入方法的参数,并将这些参数用于认证决策的制定。
(2)、@PostAuthorize
方法后调用权限验证,比如校验方法返回值:
@PreAuthorize("hasRole(ROLE_USER)") @PostAuthorize("returnObject.user.userName == principal.username") public User getUserById(long id){ ... }
Spring Security在SpEL中提供了名为returnObject 的变量。在这里方法返回一个User对象,所以这个表达式可以直接访问user对象中的userName属性。
二、权限控制案例
1、@PreAuthorize注解使用案例
接着上一节,要开启这些注解,只需要在Spring Security配置文件中添加如下注解:
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { ... }
在CustomUserDetailsService中,我们给当前登录用户授予了”admin”的权限,我们将这块代码改造一下:当登录用户为admin的时候,其拥有”admin”权限,其它用户则只有”test”权限:
@Service public class CustomUserDetailsService implements UserDetailsService { /** * 点击登录时会调用该函数、并传入登录名 根据用户名查询数据库获取用户信息 * @param username:登录用户名 * @return: 返回用户信息 * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //模拟一个用户 替代数据库获取逻辑 User user = new User(); user.setUsername(username); user.setPassword("123456"); // 输出加密后的密码 System.out.println(user.getPassword()); Listauthorities = new ArrayList<>(); if (StringUtils.equalsIgnoreCase("admin", username)) { authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin"); } else { authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("test"); } return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(), user.isAccountNonLocked(), authorities); } }
在类TestController添加一个方法,并且使用权限注解标明只有拥有“admin”权限的人才能访问:
@GetMapping("/auth/admin") @PreAuthorize("hasAuthority('admin')") public String authenticationTest() { return "您拥有admin权限,可以查看"; }
启动系统,访问http://127.0.0.1:8080/index,使用admin账号登录:
访问http://127.0.0.1:8080/auth/admin:
可看到,admin可以访问该资源。
使用zy账号登录:
可以看到,zy没有权限访问,进入了403页面。
2、自定义权限不足处理逻辑
我们可以自定义权限不足处理逻辑。在包com.goldwind.handler下新增一个类CustomAuthenticationAccessDeniedHandler,实现AccessDeniedHandler接口:
package com.goldwind.handler; import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Service; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @Author: zy * @Description: 自定义权限不足处理逻辑 * @Date: 2020/2/16 */ @Service public class CustomAuthenticationAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); httpServletResponse.setContentType("application/json;charset=utf-8"); httpServletResponse.getWriter().write("很抱歉,您没有该访问权限"); } }
然后进行Spring Security配置: @Autowired private AccessDeniedHandler accessDeniedHandler;
@Override protected void configure(HttpSecurity http) throws Exception { http.exceptionHandling() .accessDeniedHandler(accessDeniedHandler) .and() ... }
重启系统,再次使用zy账号访问/auth/admin:
参考文章:
[1] Spring Security保护方法(转载)
[2] Spring Security权限控制(转载)