2016-04-19 25 views
2

私たちは、サービス間の認証にJWTを使用するマイクロサービスアーキテクチャを持っています。私は簡単にJWTのより多くのフィールドをつかむために探しています。現在のところ、当局はSpring Securityによって直接公開されています。私たちのエッジサービス/ APIゲートウェイが下流サービスに渡すために作成されますSpring Security stateleless JWT認証 - 他のjwtフィールドを取得するには?

例JWT(いくつかのフィールドは、我々のアプリのためにカスタムされている):

{ 
    "projectId": "project1", 
    "group": "client", 
    "iss": "login.company.com", 
    "aud": "company.com", 
    "sub": "testguy", 
    "exp": 1461074284992, 
    "projectRoleId": "ADMINPAG", 
    "contentAccessGroupId": "CAG1", 
    "authorities": [ 
    "client", 
    "PROJECT_ADD_USER", 
    "PROJECT_ADD_CAG", 
    "PROJECT_DOCUMENT_VIEW", 
    "PROJECT_EDIT_CAG", 
    "PROJECT_LIST_USERS", 
    "PROJECT_SEARCH" 
    ], 
    "user_name": "[email protected]" 
} 

それはこの春ブーツでの作業を取得するために、驚くほど簡単だった:

compile("org.springframework.boot:spring-boot-starter-security:1.3.1") 
compile('org.springframework.security.oauth:spring-security-oauth2:2.0.8.RELEASE') 
compile('org.springframework.security:spring-security-jwt:1.0.3.RELEASE') 

1)注釈アプリケーションクラス

@EnableResourceServer 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
public class MyApp { 

2)財産トンを追加o application.yml:無視されたパスとJWTキーを設定する:

security: 
    basic: 
    enabled: false 
    oauth2: 
    resource.jwt.keyValue: itsasecret 
    ignored: 
    -/
    - /swagger-ui.html 
    - /webjars/** 
    - /swagger-resources 
    - /swagger/** 

この結果、ユーザはJWTの検出時に自動的にログインします。私は@PreAuthorize("hasAuthority('PROJECT_SEARCH')")と彼らの許可を主張することができます。

残っている唯一のことは、JWTから他のフィールド( "sub" - ユーザーIDなど)を取得できることです。この情報は、OAuth2AccessToken#getAdditionalInformation()フィールドに格納されているようです。カスタムのUserDetails実装はうまく動作しますが、UserDetailsS​​ervice(ユーザー名に基づいて作成する)ではなく、JWTからUserDetailsを直接作成できるようにしたいと考えています。

この主な目標は、PreAuthorizeアノテーションからこれらのJWTフィールドの一部を参照して、現在のユーザーのIDが「userId」引数などで渡されたものと同じであることをアサートできるようにすることです。これに必要な設定変更を共有モジュールのAutoConfigurationクラスに分離することができれば、私はすべてのサービスに触れる必要はありません。 JWT自体の構造は100%フレキシブルです。

答えて

3

Spring Cloud Docsで解決策が見つかりました。

私がここで欲しかったのは、JwtAccessTokenConverterConfigurerでした。検出されたとJWTが読み込まれた後、あなたのためのカスタムOAuth2Authenticationオブジェクトを行います

下のクラスはautoです:

@Component 
public class JwtConverter extends DefaultAccessTokenConverter implements 
     JwtAccessTokenConverterConfigurer { 

    @Override 
    public void configure(JwtAccessTokenConverter converter) { 
     converter.setAccessTokenConverter(this); 
    } 

    @Override 
    public OAuth2Authentication extractAuthentication(Map<String, ?> map) { 
     OAuth2Authentication auth = super.extractAuthentication(map); 
     MyAppCustomUserDetails details = new MyAppCustomUserDetails(); 
     //populate details from the provided map, which contains the whole JWT. 
     auth.setDetails(details); 
     return auth; 
    } 
} 

ここで注意すべき唯一のことは私のカスタムユーザー詳細オブジェクトはに余分なホップを取ることですに到達する。実際には、デフォルトのユーザー詳細オブジェクトのフィールドとして、decodedDetailsという名前で作成されます。私はちょうどそれを取得を支援するためのカスタムメソッドの引数リゾルバを作っ:

@Configuration 
@ConditionalOnBean(JwtConverter.class) 
public class WebConfigurer extends WebMvcConfigurerAdapter { 
    @Override 
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { 
     argumentResolvers.add(currentUserHandlerMethodArgumentResolver()); 
     super.addArgumentResolvers(argumentResolvers); 
    } 

    @Bean 
    public HandlerMethodArgumentResolver currentUserHandlerMethodArgumentResolver() { 
     return new HandlerMethodArgumentResolver() { 
      @Override 
      public boolean supportsParameter(MethodParameter parameter) { 
       return parameter.getParameterType().equals(MyAppCustomUserDetails.class); 
      } 

      @Override 
      public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 
       return ((OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails()).getDecodedDetails(); 
      } 
     }; 
    } 

} 

これだけで、あなたのコントローラへの引数としてのあなたのカスタムユーザの詳細情報を渡すために、それが自動的にJWTから移入されていることができます:

@RequestMapping("/currentUser") 
public UserInfoResponse getCurrentUser(MyAppCustomUserDetails user){ 
//do stuff 
}