详解Spring Security中权限注解的使用
概述
在使用Spring Security处理权限控制时,通常有两种方式:
- 基于URL拦截,对每个URL设置对应的权限
- 基于注解,对Controller或方法设置对应的权限
本篇攻略将详细讲解如何使用Spring Security中的权限注解进行权限控制。
Spring Security中的权限注解
Spring Security提供了3个注解用于权限控制:
- @Secured
- @PreAuthorize
- @PostAuthorize
这些注解可以用于方法级别和类级别。在使用注解进行权限控制前,需要先设置Spring Security的角色层次结构和用户授权方式,这里不再赘述,读者可以先学习Spring Security的入门教程。
@Secured注解
@Secured注解表示只有传入的用户角色包含在该注解的参数中,才能访问被注解的方法或类。
例如,定义了一个类:
@Secured("ROLE_ADMIN")
public class AdminController {
// 省略其他方法
}
在这个例子中,只有拥有ROLE_ADMIN角色的用户才能访问AdminController中声明的所有方法。
同时,@Secured注解还可以用在方法级别:
public class UserController {
@Secured({"ROLE_USER", "ROLE_ADMIN"})
public void editUser(User user) {
// 省略方法体
}
}
在这个例子中,只有拥有ROLE_USER或ROLE_ADMIN角色的用户才能调用editUser方法。
@PreAuthorize和@PostAuthorize注解
@PreAuthorize和@PostAuthorize注解更加灵活,可以调用Spring SPEL表达式来进行更复杂的权限控制。
- @PreAuthorize注解表示在方法调用之前进行权限检查,如果检查结果为false,则不允许该方法执行。
- @PostAuthorize注解表示在方法调用之后进行权限检查,如果检查结果为false,则不允许该方法的返回值被返回。
例如,我们定义了一个类:
@PreAuthorize("hasRole('USER')")
public class UserController {
@PostAuthorize("returnObject.capitalized == true")
public User createUser(User user) {
// 省略方法体
}
}
在这个例子中,@PreAuthorize的参数使用了SPEL表达式来检查用户是否拥有USER角色。@PostAuthorize的参数使用了SPEL表达式来检查该方法返回的User对象是否以大写开头。
示例说明
我们通过两条示例说明如何使用Spring Security中的权限注解进行权限控制。
示例一:@PreAuthorize注解
假设我们正在开发一个在线商城应用,用户可以通过Web界面购买商品。在购买商品时,需要检查用户是否有足够的余额进行购买。我们希望只有已登录且余额足够的用户才能完成购买。
首先,我们要在Spring Security中设置用户角色和授权方式。这里假设我们已经创建了名为"user",密码为"password"的用户,并为该用户设置了"ROLE_USER"角色,授权方式为基于表达式。
在购买商品的Controller方法上添加@PreAuthorize注解,使用SPEL表达式检查用户是否已登录、是否拥有ROLE_USER角色和余额是否足够。
@RestController
@RequestMapping("/shop")
public class ShopController {
@Autowired
private UserRepository userRepository;
@Autowired
private ProductRepository productRepository;
@PostMapping("/buy")
@PreAuthorize("isAuthenticated() and hasRole('ROLE_USER') and #user.balance >= #product.price")
public void buyProduct(@RequestParam("productId") Long productId, @AuthenticationPrincipal User user) {
User currentUser = userRepository.findById(user.getId()).orElseThrow(() -> new RuntimeException("User not found!"));
Product product = productRepository.findById(productId).orElseThrow(() -> new RuntimeException("Product not found!"));
currentUser.setBalance(currentUser.getBalance() - product.getPrice());
userRepository.save(currentUser);
}
}
在上面的例子中,我们使用了isAuthenticated()检查用户是否已登录,使用hasRole('ROLE_USER')检查用户是否拥有ROLE_USER角色,使用#user 获取传入的User对象,使用#product 获取商品对象,比较两者的余额和价格是否满足要求。
如果用户没有登录、没有ROLE_USER角色或者余额不足,则会抛出AccessDeniedException异常。
示例二:@PostAuthorize注解
假设我们正在开发一个博客应用,用户可以发布文章。在发布文章时,需要检查文章标题是否唯一。
首先,我们要在Spring Security中设置用户角色和授权方式。这里假设我们已经创建了名为"user",密码为"password"的用户,并为该用户设置了"ROLE_USER"角色,授权方式为基于表达式。
在发布文章的Controller方法上添加@PostAuthorize注解,使用SPEL表达式检查返回的Article对象的标题是否唯一。
@RestController
@RequestMapping("/article")
public class ArticleController {
@Autowired
private ArticleRepository articleRepository;
@PostMapping("/publish")
@PreAuthorize("isAuthenticated() and hasRole('ROLE_USER')")
@PostAuthorize("returnObject.title == #title")
public Article publishArticle(@RequestBody Article article, @RequestParam("title") String title) {
if (articleRepository.existsByTitle(title)) {
throw new RuntimeException("Title already exists!");
}
return articleRepository.save(article);
}
}
在上面的例子中,我们使用了isAuthenticated()检查用户是否已登录,使用hasRole('ROLE_USER')检查用户是否拥有ROLE_USER角色,使用#title 获取请求参数中的标题,检查返回的Article对象的标题是否与传入的标题相等。
如果检查失败,则会抛出AccessDeniedException异常。
总结
本篇攻略详细讲解了Spring Security中权限注解的使用方法。在实际应用中,应根据具体业务需求选择合适的注解进行权限控制。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Spring Security中权限注解的使用 - Python技术站