2017-12-14 16 views
4

내 REST 인터페이스 용으로 Spring-data-rest를 사용하고 노출 된 엔드 포인트에서 맞춤형 보안 검사를 구현했습니다. 모든 것이 잘 작동하지만 지금은 사소한 시나리오를 겪었습니다. 스프링 데이터 나머지가이 문제를 해결할 수 있는지 궁금합니다. 주기를 가져 오는 경우스프링 데이터 나머지 중첩 된 엔티티에 대한 findBy securityCheck

@Entity 
public class Cycle { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "unique_cycle_id") 
    private long id; 

    @Column(name = "user_id") 
    private long userId; 

    ... 

    @OneToMany(cascade = CascadeType.ALL) 
    @JoinTable(name = "cycles_records", joinColumns = @JoinColumn(name = "unique_cycle_id"), 
     inverseJoinColumns = @JoinColumn(name = "unique_record_id")) 
    private List<Record> records; 
} 

@Entity 
public class Record { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "unique_record_id") 
    private long id; 

    ... 
} 

, 내가 확인 로그인 한 사용자의 내주기 엔티티의 userId와 동일한 ID를 가지고

나는 다음과 같은 모델을 가지고있다.

@Slf4j 
@Component 
public class SecurityCheck { 

    public boolean check(Record record, Authentication authentication) { 
    log.debug("===Security check for record==="); 
    if (record == null || record.getCycle() == null) { 
     return false; 
    } 

    return Long.toString(record.getCycle().getUserId()).equals(authentication.getName()); 
    } 

    public boolean check(Cycle cycle, Authentication authentication) { 
    if (cycle == null) { 
     return false; 
    } 

    return Long.toString(cycle.getUserId()).equals(authentication.getName()); 
    } 
} 

을하지만 지금은 기록을 위해 유사한 보안 검사를 구현하기 위해 노력하고 있습니다 :

나는이 같은 cystom 보안 검사를 시행하고있다. 따라서 주어진주기에 대한 레코드를 가져올 때 사이클의 userId가 인증 객체의 id와 일치하는지 확인해야합니다.

나는이 내 RecordRepository에 다음과 같은 방법

@Repository 
public interface RecordRepository extends JpaRepository<Record, Long> { 

     @PreAuthorize("hasRole('ROLE_ADMIN') OR @securityCheck.check(???, authentication)") 
     Page<Record> findByCycle_Id(@Param("id") Long id, Pageable pageable); 
} 

그것이 내가이 securityCheck 내부에이 방법으로 질의하고있는 ID를 가지는 사이클 내부 사용자 ID에 액세스 할 수 있습니까? 그렇지 않다면이 기능을 구현하는 올바른 Spring의 방법은 무엇입니까?

죄송합니다. 제 질문은 명확하지 않습니다. 추가 설명이 필요한지 알려주세요.

편집 : 나는 포스트 필터에서 반환 된 페이지에 액세스하여 빠르고 더러운 솔루션을 발견했습니다. 반환 된 배열이 비어있을 때 내가 제대로 이해했다면 단점은

@PostAuthorize("hasRole('ROLE_ADMIN') OR @securityCheck.check(returnObject, authentication)") 
Page<Record> findByCycle_Id(@Param("id") Long id, Pageable pageable); 

public boolean check(Page<Record> page, Authentication authentication) { 
    log.debug("===Security check for page==="); 
    if (!page.hasContent()) { 
     return true; 
    } 

    long userId = page.getContent().get(0).getCycle().getUserId(); 

    return Long.toString(userId).equals(authentication.getName()); 
    } 
+0

당신이있는 거 프로젝트가 나에게 흥미 보인다. 그것은 github에 있습니까? 나는 불행히도이 질문에 대한 답을 가지고 있지 않지만, 나는 나중에 그것을 보길 원합니다. 필자는 커스터마이징이 필요할 때마다 스프링 데이터를 사용하지 않으려 고했다. 왜냐하면 필자가지지하는 프레임 워크보다 부담이 많았 기 때문이다. 어쩌면 프로젝트가 내 마음을 바꿀 수 있고 새로운 아이디어를 보여줄 수 있습니다 :) –

+0

불행히도이 특정 프로젝트는 opensource가 아니지만 메시지를 보내려는 경우 제 솔루션을 공유하게되어 기쁩니다. 내 문제에 대한 더러운 해결책을 찾았지만 나는 아직도 좀 더 탄력적 인 방법이 있기를 바란다. (나의 우스꽝스러운 질문을 보라). – Smajl

답변

1

(그래서 난 아직도 좀 더 우아한 해결책을 찾고 있어요) 내 로그인 한 사용자의 소유가 아닌 기록에 액세스 할 수 있다는 것입니다 ..

먼저 SpEL EvaluationContext extension이 활성화되어 있는지 확인하십시오.

public interface RecordRepository extends JpaRepository<Record, Long> { 

    @Query("select r from Record r join r.cycle c where c.id = ?1 and (c.userId = ?#{@RecordRepository.toLong(principal)} or 1 = ?#{hasRole('ROLE_ADMIN') ? 1 : 0})") 
    Page<Record> findByCycleId(Long id, Pageable pageable); 

    default Long toLong(String name) { 
     return Long.valueOf(name); 
    } 
} 

내가 principal 내가 다음을 비교 길이로 변환 그래서 여기 userId의 문자열 표현을 포함한다고 가정 ...

은 또한 당신이 내 example을 확인할 수 있습니다

그런 다음 같은 것을 할

UPDATE ...이 문제와 관련된

,536,

SpEL 표현식에서 @RecordRepository.toLong(principal) 대신 T(java.lang.Long).valueOf(principal)을 사용하고 repo에서 toLong 메소드를 삭제하십시오. 그냥 사용자 ID가 로그인 한 사용자와 동일한 것을 확인하면서 "주기"의 특정 레코드를 얻기 위해 필요한 경우

+0

해답을 가져 주셔서 감사합니다. 나는 그것을 확실히 시도 할 것이다. 그러나 사용자 지정 쿼리를 사용할 필요가없고 코드에서 선택해야 할 솔루션이있을 것으로 기대하고있었습니다. 이것은 매우 '봄이 잘 보일 것 같지 않습니다' – Smajl

+0

@Smajl Spring 문서에서 볼 수 있듯이, Spring Security SpEL 함수를 사용하여 문제가 레코드 수준 보안에 관한 것이면 '봄맞춤'입니다. – Cepr0

0

은, 난 그냥 같은 것을 사용하는 것이 :

Page<Record> findByCycle_IdAndUserId(Long cycleId, Long userId, Pageable pageable); 

JPA는 물건을 많이 처리 그 자체로 쿼리를 수동으로 생성하지 않아도됩니다. 검색어 방법 (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods)을 참조하십시오.

물론 이것은 모델 측 대신 스프링 보안으로 컨트롤러 측에서 보안 검사를 처리해야한다는 것을 의미합니다. 귀하의 DAO는 사전에 검사가 이루어지기 때문에 요청이 유효한지/공인되었는지 신경 써서는 안됩니다.

건배

0

당신은 다음과 같은 응용 프로그램에 두 개의 핸들러를 도입 할 수 있습니다 : -

//This is your Request handler 

public class CxfHttpRequestHandler extends AbstractPhaseInterceptor<Message> { 


    public CxfHttpRequestHandler() { 
     super("marshal"); 
    } 

    @Override 
    public void handleMessage(final Message message) throws Fault { 
     //handle your all request here 
     } 
} 

//this is your Response handler 
public class CxfHttpResponseHandler extends AbstractPhaseInterceptor<Message> { 

    public CxfHttpResponseHandler() { 
     super("pre-stream"); 
    } 

    @Override 
    public void handleMessage(final Message message) throws Fault { 
     //handle your outgoing message here 
    } 
} 


Add the following in your spring-bean.xml file:- 

<bean id="cxfHttpRequestHandler" class="com.yourpackage.CxfHttpRequestHandler" > 
     <description> 
      This Bean implements Interceptor for all request 
     </description> 
</bean> 

<bean id="cxfHttpResponseHandler" class="com.yourpackage.CxfHttpResponseHandler"> 
     <description> 
      This Bean implements Interceptor for all response 
     </description> 
    </bean> 

<cxf:bus> 
     <cxf:inInterceptors> 
      <ref bean="cxfHttpRequestHandler" /> 
     </cxf:inInterceptors> 

     <cxf:outInterceptors> 
      <ref bean="cxfHttpResponseHandler" /> 
     </cxf:outInterceptors> 

</cxf:bus>