【问题标题】:Why the application does not see the Roles in Spring Security (Forbidden)为什么应用程序看不到 Spring Security 中的角色(禁止)
【发布时间】:2020-08-24 11:51:58
【问题描述】:

有一个Spring-MVC 项目,其中有三种类型的用户:CustomerAdminCook。它们都继承自类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 权限

标签: java spring-boot spring-security roles


【解决方案1】:

您应该将@RolesAllowed("ADMIN") 更改为@RolesAllowed("ROLE_ADMIN") 以匹配角色名称的常规前缀(您在User::getAuthorities 中使用的前缀)。

【讨论】:

  • Дмитрий, может быть у меня отсутствует SecurityContextHolder.getContext().getAuthentication().getAuthorities() ?
【解决方案2】:

要解决这个问题,在最开始的configure 方法的WebSecurityConfig 类中添加以下内容:

http
.cors().disable()
.csrf().disable()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-25
    • 2020-06-14
    • 2014-10-11
    • 2021-10-13
    • 1970-01-01
    • 2016-07-16
    • 2015-07-29
    • 2018-10-14
    相关资源
    最近更新 更多