为啥应用程序看不到 Spring Security 中的角色(禁止)
Posted
技术标签:
【中文标题】为啥应用程序看不到 Spring Security 中的角色(禁止)【英文标题】:Why the application does not see the Roles in Spring Security (Forbidden)为什么应用程序看不到 Spring Security 中的角色(禁止) 【发布时间】:2020-08-24 11:51:58 【问题描述】:有一个Spring-MVC
项目,其中有三种类型的用户:Customer
、Admin
、Cook
。它们都继承自类User
。无需ENUM
,只需通过静态字符串常量(在User
类中显示)即可创建角色。添加Spring Security
后,授权成功,但是当我尝试执行其中一个类(Customer、Admin 或 Cook)的方法时,它给出 json 错误 403:
"timestamp": "2020-05-08T19:48:43.999+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/admin/cooks"
请告诉我我做错了什么。哪里出错了。
用户:
package com.tinychiefdelights.model;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.util.Collection;
import java.util.Collections;
@ApiModel
@Data
@Entity
@Table(name = "pg_user", schema = "public")
public class User implements UserDetails
// Roles
//
public static final String ROLE_ADMIN = "ADMIN";
public static final String ROLE_CUSTOMER = "CUSTOMER";
public static final String ROLE_COOK = "COOK";
//
public User() // Пустой конструктор для Hibernate
// Поля
private @Id
@GeneratedValue
Long id;
@Column(name = "login")
private String login;
@Size(min = 5, max = 30)
@Column(name = "password")
private String password;
@Column(name = "role")
private String role;
@Column(name = "name")
private String name;
@Column(name = "last_name")
private String lastName;
// Методы
//
// GrantedAuthority
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role));
// userName == login (одно и тоже)
@Override
public String getUsername()
return login;
// Во всех флагах стоит TRUE, так как они не используются
@Override
public boolean isAccountNonExpired()
return true;
@Override
public boolean isAccountNonLocked()
return true;
@Override
public boolean isCredentialsNonExpired()
return true;
@Override
public boolean isEnabled()
return true;
//
例如我只添加一个类 Admin:
管理员:
package com.tinychiefdelights.model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.*;
@ApiModel
@Data
@Entity
@Table(name = "pg_user", schema = "public")
public class Admin
public Admin() // Пустой конструктор для Hibernate
// Поля
// name, lastName, login, password берем от класса User через связи;
@ApiModelProperty
private @Id
@GeneratedValue
Long id;
// Relationships
//
@ApiModelProperty
@OneToOne
@JoinColumn(name = "id") // Join without Admin in User class
private User user;
管理服务:
@Service
public class AdminService extends UserService
// Поля
//
// Injects in setters
private AdminRepository adminRepository; // Администратор
private OrderRepository orderRepository; // Заказ
private CookRepository cookRepository; // Повар
private CustomerRepository customerRepository; // Заказчик
// Getters and Setters
//
// Делаем inject через сеттеры
@Autowired
public void setAdminRepository(AdminRepository adminRepository)
this.adminRepository = adminRepository;
@Autowired
public void setOrderRepository(OrderRepository orderRepository)
this.orderRepository = orderRepository;
@Autowired
public void setCookRepository(CookRepository cookRepository)
this.cookRepository = cookRepository;
@Autowired
public void setCustomerRepository(CustomerRepository customerRepository)
this.customerRepository = customerRepository;
// Методы
//
// Вывод списка всех заказов
public List<Order> getAllOrders()
return orderRepository.findAll();
// Вывод информации по конкретному заказу
public Order getOrderInfo(Long id)
try
return orderRepository.getById(id);
catch (NotFoundException e)
throw new NotFoundException(id);
// Вывод Повара по ID
public Cook getCook(Long id)
try
return cookRepository.getByIdAndUserRole(id, "COOK");
catch (NotFoundException e)
throw new NotFoundException(id);
catch (IllegalArgumentException e)
throw new IllegalArgumentException();
// Изменить карту повара
public void editCook(Long id, User user, float rating, String aboutCook)
Cook cook = cookRepository.getByIdAndUserRole(id, "COOK");
try
cook.setUser(user);
cook.setRating(rating);
cook.setAboutCook(aboutCook);
catch (IllegalArgumentException e)
throw new IllegalArgumentException();
catch (NotFoundException e)
throw new NotFoundException(id);
// Вывод всех поваров
public List<Cook> getAllCooks()
return cookRepository.findByUserRole("COOK");
// Удалить Повара
public void deleteCook(Long id)
Cook cook = cookRepository.getByIdAndUserRole(id, "COOK");
try
cookRepository.delete(cook);
catch (Exception e)
throw new NotFoundException(id);
// Вывод всех Заказчиков
public List<Customer> getAllCustomers()
return customerRepository.findByUserRole("CUSTOMER");
// Вывод Заказчика по ID
public Customer getCustomer(Long id)
try
return customerRepository.getByIdAndUserRole(id, "CUSTOMER");
catch (NotFoundException e)
throw new NotFoundException(id);
catch (IllegalArgumentException e)
throw new IllegalArgumentException();
管理员控制器:
@Api(value = "Работа с Админом", tags = "Администратор")
@RestController
@RequestMapping("/admin")
@RolesAllowed("ADMIN")
public class AdminController
// Constructor
//
// Inject через конструктор
@Autowired
public AdminController(AdminRepository adminRepository, AdminService adminService, UserService userService)
this.adminRepository = adminRepository;
this.adminService = adminService;
this.userService = userService;
// Поля
// All injects into constructor
private final AdminRepository adminRepository;
private final AdminService adminService;
private final UserService userService;
// Методы
//
// GET MAPPING
//
// Вывод списка всех заказов
@GetMapping("/orders")
List<Order> getAllOrders()
return adminService.getAllOrders();
// Вывод информации по конкретному заказу по ID
@GetMapping("/order/id")
Order getOrderInfo(@PathVariable Long id)
return adminService.getOrderInfo(id);
// Вывод всех Поваров
@GetMapping("/cooks")
List<Cook> getAllCooks()
return adminService.getAllCooks();
// Вывод Повара по ID
@GetMapping("/cook/id")
Cook getCook(@PathVariable Long id)
return adminService.getCook(id);
// Вывод всех пользователей
@GetMapping("/customers")
List<Customer> getAllCustomer()
return adminService.getAllCustomers();
// Вывод Заказчика по ID
@GetMapping("/customer/id")
Customer getCustomer(@PathVariable Long id)
return adminService.getCustomer(id);
// POST MAPPING
//
// PUT MAPPING
//
// Изменяем Повара по ID
@PutMapping("/edit/cook/id")
void editCook(@PathVariable Long id, User user, @PathVariable float rating, String aboutCook)
adminService.editCook(id, user, rating, aboutCook);
// Поменять пароль
@PutMapping("/change/password")
void changePassword(@RequestParam String login, @RequestParam String newPass)
userService.changePassword(login, newPass);
// DELETE MAPPING
//
// Удалить конкретного Повара по ID
@DeleteMapping("/delete/cook/id")
void removeCook(@PathVariable Long id)
adminService.deleteCook(id);
我使用注释@RolesAllowed。
SpringWebConfig:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
// Поля
//
private UserService userService;
private PasswordEncoder passwordEncoder;
// Injects in SETTERS
//
@Autowired
public void setUserService(UserService userService)
this.userService = userService;
@Autowired
public void setPasswordEncoder(PasswordEncoder passwordEncoder)
this.passwordEncoder = passwordEncoder;
// Methods
//
// Тут мы переопределяем метод конфигураций
@Override
protected void configure(HttpSecurity http) throws Exception
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
// Тут мы переопределяем для работы с внешней БД
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(userService).passwordEncoder(passwordEncoder);
// Beans
//
@Bean
public DaoAuthenticationProvider authenticationProvider()
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
@Bean
public static PasswordEncoder getPasswordEncoder()
return new BCryptPasswordEncoder(8);
// Возвращаем сервис пользовател для userDetService
@Bean
public UserDetailsService userDetailsService()
return userService;
【问题讨论】:
尝试调试用户主体对象以查看其中的内容。您可以将 API 设置为已验证并打印出对象SecurityContextHolder.getContext().getAuthentication()
。您的角色映射可能已关闭/角色为空,但它们位于 authorities
下
我添加了一个调试屏幕。我已经不能处理这 4 天了。你想说如果我在你的SecurityContextHolder.getContext().getAuthentication()
上更改SecurityContextHolder.getContext (). GetAuthentication (). GetPrincipal ()
会更好吗?
UserService
是什么,可以贴在这里吗?
您能否粘贴得到响应 403 的确切请求(包括标头)?
需要在 grunted 权限中传递 ROLE_ADMIN.. 我认为您在 userService 类中将 ADMIN 传递给 spring security grunted 权限
【参考方案1】:
您应该将@RolesAllowed("ADMIN")
更改为@RolesAllowed("ROLE_ADMIN")
以匹配角色名称的常规前缀(您在User::getAuthorities
中使用的前缀)。
【讨论】:
Дмитрий, может быть у меня отсутствует SecurityContextHolder.getContext().getAuthentication().getAuthorities() ?【参考方案2】:要解决这个问题,在最开始的configure
方法的WebSecurityConfig
类中添加以下内容:
http
.cors().disable()
.csrf().disable()
【讨论】:
以上是关于为啥应用程序看不到 Spring Security 中的角色(禁止)的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Spring Security 使用默认的预认证检查?
为啥 Spring Security 的 BindAuthenticator 需要用户读取权限?
为啥 https 中使用 Spring Security 添加端口 80?
为啥 Spring Security getAuthentication() 在服务器上返回 null