如何保护 JpaRepository
How to secure JpaRepository
我想保护将我的 OAuth 用户引用到授权角色的 JpaRepository 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
方法并检索有关任何用户的所有用户信息。但我希望用户只能检索自己的信息。
在我看来,将存储库发布为休息资源是一个糟糕的选择。最好有调用存储库方法的控制器和服务。此控制器将仅发布您想要的方法。
您可以有两个控制器:LoginController
处理登录请求,UserController
处理关于用户的请求。然后,您可以在每个控制器上应用安全策略。
如果您发布存储库,如果新的 Spring 数据版本包含 JpaRepository class 上的新方法,这些方法将被发布并且没有任何安全策略
我想保护将我的 OAuth 用户引用到授权角色的 JpaRepository 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
方法并检索有关任何用户的所有用户信息。但我希望用户只能检索自己的信息。
在我看来,将存储库发布为休息资源是一个糟糕的选择。最好有调用存储库方法的控制器和服务。此控制器将仅发布您想要的方法。
您可以有两个控制器:LoginController
处理登录请求,UserController
处理关于用户的请求。然后,您可以在每个控制器上应用安全策略。
如果您发布存储库,如果新的 Spring 数据版本包含 JpaRepository class 上的新方法,这些方法将被发布并且没有任何安全策略