Существует много способов решения этой проблемы, но я выбрал три решения для ее устранения.
Настраиваемое выражение безопасности
Я бы порекомендовал подход на основе пользовательских аннотаций безопасности.Это может включать реализацию пользовательского выражения безопасности, связанного обработчика выражений и конфигурации безопасности метода.Следующая аппроксимация будет немного проще, если это слишком много для вас.
public class UserIdentityMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public UserIdentityMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
public boolean userIdentity(Long userId) {
User user = ((UserPrincipal) this.getPrincipal()).getUser();
return user.getId() == userId;
}
}
Остальные конечные точки или методы обслуживания могут быть аннотированы вновь созданным выражением безопасности:
@PreAuthorize("userIdentity(#userId)")
@GetMapping
@ResponseBody
public Resource fineOne(@PathVariable Long userId) {
return resourceService.findOne(id);
}
Обратите внимание, что userId
должен быть предоставлен где-то , как @PathVariable
или @RequestParam
.Затем Spring Security проверит, соответствует ли текущий пользователь указанному userId
и возвращает 403
в противном случае.
Полный пример доступен здесь и был адаптирован в этом вопросе для вашегоцели.
SpEL
Вы также можете использовать SpEL, что немного проще:
@PreAuthorize("#userId == principal.getId()")
@GetMapping
@ResponseBody
public Resource fineOne(@PathVariable Long userId) {
return resourceService.findOne(id);
}
Другие соображения
Вы также можете делать всеработать самостоятельно и получить более быстрый результат, не определяя пользовательское выражение с помощью SecurityContextHolder
.
public static void checkUserIdentity(Long userId) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// user did not provide a token
if(auth == null) {
throw new AccessDeniedException();
}
UserDetails details = (UserDetails) auth.getPrincipal();
if(userId != details.getId()) {
throw new AccessDeniedException();
}
}
и используя его как:
@GetMapping
@ResponseBody
public Resource fineOne(@PathVariable Long userId) {
SecurityUtils.checkUserIdentity(userId)
return resourceService.findOne(id);
}
Почему это работает?SecurityContextHolder
будет вводить текущий участник, если вы правильно настроили безопасность Spring.По умолчанию аутентификация привязана к текущему потоку выполнения и будет сброшена, если запрос был обработан или обнаружил исключение.