首页 > 解决方案 > 如何通过一个通用的 UserDetailsS​​ervice 注册不同角色和字段的用户?

问题描述

我有一个超类User及其四个后代。每个继承人对应一个特定的角色,并具有一定的领域。我需要在这样的数据库结构上进行用户注册和身份验证。我尝试通过 执行此@Inheritance(JOINED)操作,它适用于正常的 CRUD 操作,但注册和身份验证存在问题。我尝试通过几个UserDetailsService,@Configuration和来做到这一点@Order,但是一个配置阻止了另一个,一些请求不起作用或UserDetails返回空。身份验证提供程序还有其他内容。

是否可以通过它来实施,或者有更好的建议来实现这一目标?

谢谢。

父类用户及其后代:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "usr", schema = "public")
@Inheritance(strategy = InheritanceType.JOINED)
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;

    @Transient
    private String matchingPassword;
    private String name;
    private String surname;
    private String patronymic;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate birthday;
    private String hometown;
    private String number;
    private String mail;
    private String avatar;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return getRoles();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

@Entity
@Table(name = "student", schema = "public")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class Student extends User implements Serializable {

    private String faculty;
    private Integer rating;

    @Builder
    public Student(Long id,
                   String username,
                   String password,
                   String matchingPassword,
                   String name,
                   String surname,
                   String patronymic,
                   LocalDate birthday,
                   String hometown,
                   String number,
                   String mail,
                   String avatar,
                   Set<Role> roles,
                   String faculty,
                   Integer rating) {
        super(id, username, password, matchingPassword, name, surname, patronymic, birthday, hometown, number, mail, avatar, roles);
        this.faculty = faculty;
        this.rating = rating;
    }
}

@Entity
@Table(name = "director", schema = "public")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class Director extends User implements Serializable {

    private String department;

    @Builder
    public Director(Long id,
                    String username,
                    String password,
                    String matchingPassword,
                    String name,
                    String surname,
                    String patronymic,
                    LocalDate birthday,
                    String hometown,
                    String number,
                    String mail,
                    String avatar,
                    Set<Role> roles,
                    String department) {
        super(id, username, password, matchingPassword, name, surname, patronymic, birthday, hometown, number, mail, avatar, roles);
        this.department = department;
    }
}

实现 UserDetailsS​​ervice 的 UserService:

@Service
public class UserService implements UserDetailsService {

    private final UserRepository userRepository;
    private final FileService fileService;

    @Value("${application.avatar-folder}")
    private String avatarFolder;

    @Autowired
    public UserService(UserRepository userRepository,
                       @Lazy FileService fileService) {
        this.userRepository = userRepository;
        this.fileService = fileService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);

        if(user != null) {
            Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
            for(Role role : user.getRoles()) {
                grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
            }

            return new org.springframework.security.core.userdetails.User(
                    user.getUsername(),
                    user.getPassword(),
                    grantedAuthorities
            );
        }

        throw new UsernameNotFoundException("Пользователя с именем " + username + " не существует!");
    }

弹簧安全配置:

@Configuration
@EnableWebSecurity
@ComponentScan("com.amirdigiev.tsaritsynostudentportfolio")
@SuppressWarnings("Duplicates")
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserService userService;

    @Autowired
    public SecurityConfig(UserService userService) {
        this.userService = userService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
                    .antMatchers("/registration")
                .and()
                    .authorizeRequests()
                    .anyRequest().authenticated()

                .and()
                    .formLogin().permitAll()
                        .loginPage("/login")
                        .usernameParameter("username")
                        .passwordParameter("password")
                        .defaultSuccessUrl("/home", true)
                .and()
                    .logout()
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/login")
                        .invalidateHttpSession(true)
                        .deleteCookies("JSESSIONID");

        http.headers()
                .cacheControl().disable()
                .and()
                .csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**", "/images/**");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }


    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

标签: javaspringspring-bootspring-security

解决方案


最后,我决定将 User 字段(通过组合)添加到所有角色。


推荐阅读