2017-03-07 6 views
2

私は単純な一対多の関係で春のデータ休憩アプリケーションを持っています。Springデータを避ける方法コントローラ上でAccessDeniedExceptionが発生したときにHTTP 400を投げるRest

(1つの組織には、0人以上の従業員が含まれています)。どのようなセキュリティのないシナリオで

、私はこのような組織に従業員を追加することができます。

従業員のペイロードは、残りのコントローラにプッシュされ
curl -v -H "Content-Type:application/json" -d '{"name":"name1","organization":"http://localhost:8080/api/organizations/1"}' http://localhost:8080/api/employee 

、春データ残りは組織のURIに変換しますエンティティ、従業員にそれを接続し、すべてうまく動作します。

しかし、私は組織APIのセキュリティを開始し、認証されたユーザーには(ビジネスロジックに基づいて)組織を見ることができるようにしたいとします。

@RepositoryRestResource 
public interface OrganizationController extends CrudRepository<Organization, Long> { 

    @PreAuthorize("@securityService.isAllowedToSeeTheOrganization(#id)") 
    Organization findOne(@P("id") Long id); 

} 

SecurityServiceは、ユーザーが自分のプロファイルに基づいて組織を取り出すことができているかどうかを判断するために、いくつかのビジネスロジックを実装しています:

@Service 
public class SecurityService { 

    public boolean isAllowedToSeeOrganization(Long organizationId) { 
     return isAdmin() || belongsToOrganization(organizationId); 
    } 

    private boolean isAdmin() { 
     return SecurityContextHolder.getContext().getAuthentication().getAuthorities().contains(new SimpleGrantedAuthority(ADMIN.getRoleName())); 
    } 

    private boolean belongsToOrganization(Long organizationId) { 
     return organizationId == Long.parseLong(getUserDetails().get(ORGANIZATION_ID_FIELD).toString()); 

    } 

    protected Map<String,Object> getUserDetails() { 
     JwtAuthentication jwtAuthentication = (JwtAuthentication) SecurityContextHolder.getContext().getAuthentication(); 
     return jwtAuthentication.getJwtClaimsSet().getClaims(); 
    } 

} 

このオフコースとしても、上記の呼び出しに影響を持つことになります組織のURIをエンティティに変換することができなくなります。ログに

{ 
    "cause":{ 
     "cause":{ 
      "cause":null, 
      "message":"Access is denied" 
     }, 
     "message":"Access is denied (through reference chain: com.example.Employee[\"organization\"])" 
    }, 
    "message":"Could not read document: Access is denied (through reference chain: com.example.Employee[\"organization\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Access is denied (through reference chain: com.example.Employee[\"organization\"])" 
} 

しかし、代わりに禁断の403を投げるの、APIコールはHTTP 400(不正な要求)と、次のボディで失敗

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Access is denied (through reference chain: com.example.Employee["organization"]) 
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:388) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:348) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1599) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:359) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2922) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:237) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE] 
    ... 97 common frames omitted 
Caused by: org.springframework.security.access.AccessDeniedException: Access is denied 
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-4.1.4.RELEASE.jar:4.1.4.RELEASE] 
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-4.1.4.RELEASE.jar:4.1.4.RELEASE] 
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:65) ~[spring-security-core-4.1.4.RELEASE.jar:4.1.4.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] 
    at com.sun.proxy.$Proxy156.findOne(Unknown Source) ~[na:na] 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_40] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_40] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_40] 
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_40] 
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:216) ~[spring-core-4.3.5.RELEASE.jar:4.3.5.RELEASE] 
    at org.springframework.data.repository.support.ReflectionRepositoryInvoker.invoke(ReflectionRepositoryInvoker.java:265) ~[spring-data-commons-1.12.6.RELEASE.jar:na] 
    at org.springframework.data.repository.support.ReflectionRepositoryInvoker.invokeFindOne(ReflectionRepositoryInvoker.java:140) ~[spring-data-commons-1.12.6.RELEASE.jar:na] 
    at org.springframework.data.repository.support.CrudRepositoryInvoker.invokeFindOne(CrudRepositoryInvoker.java:91) ~[spring-data-commons-1.12.6.RELEASE.jar:na] 
    at org.springframework.data.rest.core.support.UnwrappingRepositoryInvokerFactory$UnwrappingRepositoryInvoker.invokeFindOne(UnwrappingRepositoryInvokerFactory.java:130) ~[spring-data-rest-core-2.5.6.RELEASE.jar:na] 
    at org.springframework.data.rest.core.UriToEntityConverter.convert(UriToEntityConverter.java:123) ~[spring-data-rest-core-2.5.6.RELEASE.jar:na] 
    at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$UriStringDeserializer.deserialize(PersistentEntityJackson2Module.java:516) ~[spring-data-rest-webmvc-2.5.6.RELEASE.jar:na] 
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:499) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:101) ~[jackson-databind-2.8.5.jar:2.8.5] 
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:357) ~[jackson-databind-2.8.5.jar:2.8.5] 

事実クライアントに内部情報が漏洩していると私には間違ったことがあるように思えます。

この場合、403 Forbiddenを返すか、このエラーメッセージをカスタマイズする方法があります。

どうすればいいですか?

+0

securityService、employee、およびspringのセキュリティ設定のコードを共有できますか? JSONマッピングに何らかのエラーがあると思われるため、400がスローされます。 –

+0

スタックトレース/セキュリティサービスの実装を追加しました。データレストUri/Entityコンバーターはリポジトリ呼び出しをトリガーします。認証オブジェクトはその呼び出しを実行できません(PreAuthorize)、AccessDeniedExceptionがスローされます。 AccessDeniedExceptionはスタックの上にプッシュされ、SpringはHTTP 400をクライアントにスローします。私はまだ400の代わりに403を期待しています。 – ddewaele

答えて

0

もちろん、標準Spring exception handling mechanismsを使用する必要がありますが、問題は通常よりも少し複雑です。デモプロジェクトで私のソリューションを試したところ、これはうまくいくはずです。

AccessDeniedExceptionは、JsonMappingExceptionにラップされており、それ自体がHttpMessageNotReadableExceptionにラップされています。これは、春の例外処理が得るものです:

HttpMessageNotReadableException 
|---JsonMappingException 
    |---AccessDeniedException 

@ExceptionHandler方法は春4.3以降が、唯一の深い1つのレベルまで包まれた例外を一致させることができます春の問題追跡中this ticketによります。 AccessDeniedException@ExceptionHandlerを使用すると、Springが受け取った例外の2レベル下にネストされているため、動作しません。

どのように春がハンドラを一致させるために探している例外チェーンの深いあなたは変えることができますが、それだけで、この問題のためだ場合、私はちょうどそれを行う例外ハンドラを定義したい:

@ControllerAdvice 
public class ExceptionAdvice { 

    @ExceptionHandler(HttpMessageNotReadableException.class) 
    public ResponseEntity<String> accessDenied(Exception e) throws Exception { 
     Throwable cause = e.getCause(); 
     if (cause != null) { 
      Throwable nestedCause = cause.getCause(); 
      if (AccessDeniedException.class.isAssignableFrom(nestedCause.getClass())) { 
       return new ResponseEntity<>(HttpStatus.FORBIDDEN); 
      } 
     } 
     throw e; 
    } 
} 

あなたがカスタマイズでき(おそらく、必要な例外について完全な例外チェーンを検索します)、返信メッセージを追加します。

Handlerは独自のクラスで定義されていますが、EmployeeControllerはおそらくOrganizationControllerのようなインターフェイスなので、@ExceptionHandlerメソッドを内部で定義することはできません(デフォルトメソッドも機能しません)。

関連する問題