【问题标题】:How to secure JpaRepository如何保护 JpaRepository
【发布时间】:2019-07-29 09:21:59
【问题描述】:

我想保护一个 JpaRepository,它将我的 OAuth 用户引用到授权角色 ROLE_ADMIN。现在我想知道我需要注释哪些方法。 因此,当我想保护整个用户存储库时,登录不再起作用,因为它调用了findByUsername(String username) 方法。是否有可能从登录过程中删除此方法并将参数限制为主体用户名?

所以首先我需要在 JpaRepository 中发现我需要授权的标准方法。当我浏览代码时,我发现了一堆,但我认为我不需要明确提及所有这些。

List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
<S extends T> List<S> findAll(Example<S> example);
<S extends T> List<S> findAll(Example<S> example, Sort sort);
Page<T> findAll(Pageable pageable);
Iterable<T> findAll(Sort sort);
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
<S extends T> Optional<S> findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example, Sort sort);
<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);
<S extends T> long count(Example<S> example);
<S extends T> boolean exists(Example<S> example);

到目前为止,我的存储库如下所示:

@RepositoryRestResource(collectionResourceRel = "internal:users", path = "users")
public interface UserRepository extends JpaRepository<User, Long>
{
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    Page<User> findAll(Pageable pageable);

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    User findOne(String id);

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    User save(User u);

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    void delete(User u);

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    User insert(User u);

    User findByUsername(String username);
}

我还需要添加一些方法吗?

关于保护findByUsername(String username) 方法的第二个问题,我尝试使用@PreAuthorize("#username == authentication.principal.username") 进行注释,但由于该方法是在登录过程中调用的,因此无法再登录。

这里还有一些代码:

@Component
public class AppUserDetailsService implements UserDetailsService{
    @Autowired
    private UserRepository userRepository;


    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(s);

        if(user == null) {
            throw new UsernameNotFoundException(String.format("The username %s doesn't exist", s));
        }

        List<GrantedAuthority> authorities = new ArrayList<>();
        user.getRoles().forEach(role -> {
            authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
        });

        UserDetails userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);

        return userDetails;
    }
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  [...]

    @Autowired
    private AppUserDetailsService userDetailsService;

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
         DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
         authenticationProvider.setUserDetailsService(userDetailsService);
         authenticationProvider.setPasswordEncoder(passwordEncoder());
         return authenticationProvider;
    }
}
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "username")
    private String username;

    @Column(name = "password")
    @JsonIgnore
    private String password;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "user_has_roles", joinColumns
            = @JoinColumn(name = "user_id",
            referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "role_id",
                    referencedColumnName = "id"))
    private List<Role> roles;
}

因此,现在拥有令牌的用户能够调用findByUsername 方法并检索有关任何用户的所有用户信息。但我希望用户只能检索自己的信息。

【问题讨论】:

  • 发送令牌时,会查找与该令牌关联的用户。令牌无法查找其他用户。所以我不太明白你的问题。
  • @Thomas 没错。但由于搜索方法对所有用户开放,我以test 用户身份登录并使用此令牌调用users/search/findByUsername?username=admin,因此我获得了有关管理员用户的所有信息。
  • 然后是需要保护的 url,而不是方法本身
  • @dur 你说的都对,这会起作用,但我认为按照 Jaume 的回答建议,使用控制器会更方便。

标签: java spring spring-boot spring-security spring-data-rest


【解决方案1】:

在我看来,将存储库作为休息资源发布是一个糟糕的选择。最好有调用存储库方法的控制器和服务。这个控制器只会发布你想要的方法。

您可以有两个控制器:LoginController 处理登录请求,UserController 处理关于用户的请求。然后,您可以在每个控制器上应用安全策略。

如果您发布存储库,如果新的 Spring 数据版本包含 JpaRepository 类上的新方法,则此方法将被发布并且不会有任何安全策略

【讨论】:

    猜你喜欢
    • 2018-12-02
    • 2018-01-13
    • 2015-08-10
    • 1970-01-01
    • 2013-10-06
    • 2011-05-23
    • 2015-09-22
    • 2010-12-27
    相关资源
    最近更新 更多