web-dev-qa-db-ja.com

Spring Securityを使用して、現在ログインしているユーザー情報をJWTトークンから抽出する

Spring Security Oauth2を使用してJWTおよびLDAP認証を実装しました。正常に機能しているようで、LDAP資格情報でログインできます。

ここで、現在ログインしているユーザー情報を使用してデータベースに詳細を保存する必要があるという要件が1つあります。特に、そのユーザーが新しいレコードを追加/更新するときなどです。私はSpringセキュリティ方法を使用してそれを取得しようとしました

SecurityContextHolder.getContext().getAuthentication().getDetails() 

しかし、JWTにあるすべての情報を返すわけではありません。リモートIP、JWTトークン値、認証済みtrueを返すだけです。 name()さえも返しません。

私はJWTが初めてなので、そのトークンを読み取ることでそれを抽出する必要があるかどうか、またどのようにそれを達成できるかさえもわかりません。

すべてのポインタが高く評価されます。

ありがとう。

12
mayur tanna

最初に行う必要があるのは、JWTが作成されたときにJWT内にユーザー情報を保存し、使用時にそれを抽出する必要があることです。同様の状況があり、TokenEnhancerJwtAccessTokenConverterの両方を拡張することで解決しました。


JWT追加情報内にTokenEnhancer型を使用して、CustomUserDetails型の拡張プリンシパルを埋め込みます。

public class CustomAccessTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Authentication userAuthentication = authentication.getUserAuthentication();
        if (userAuthentication != null) {
            Object principal = authentication.getUserAuthentication().getPrincipal();
            if (principal instanceof CustomUserDetails) {
                Map<String, Object> additionalInfo = new HashMap<>();
                additionalInfo.put("userDetails", principal);
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            }
        }
        return accessToken;
    }
}


そして、認証された要求を処理するときにAuthenticationオブジェクトを構築するときに、拡張されたプリンシパルを手動で抽出します。

public class CustomJwtAccessTokenConverter extends JwtAccessTokenConverter {

    @Override
    public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
        OAuth2Authentication authentication = super.extractAuthentication(map);
        Authentication userAuthentication = authentication.getUserAuthentication();

        if (userAuthentication != null) {
            LinkedHashMap userDetails = (LinkedHashMap) map.get("userDetails");
            if (userDetails != null) {

                // build your principal here
                String localUserTableField = (String) userDetails.get("localUserTableField");
                CustomUserDetails extendedPrincipal = new CustomUserDetails(localUserTableField);

                Collection<? extends GrantedAuthority> authorities = userAuthentication.getAuthorities();

                userAuthentication = new UsernamePasswordAuthenticationToken(extendedPrincipal,
                        userAuthentication.getCredentials(), authorities);
            }
        }
        return new OAuth2Authentication(authentication.getOAuth2Request(), userAuthentication);
    }
}


AuthorizationServer構成は、すべてを結び付けます。

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        CustomJwtAccessTokenConverter accessTokenConverter = new CustomJwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("a1b2c3d4e5f6g");
        return accessTokenConverter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomAccessTokenEnhancer();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource).passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
        endpoints
                .tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancerChain)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.passwordEncoder(passwordEncoder());
        security.checkTokenAccess("isAuthenticated()");
    }
}


このようにして、リソースコントローラーの拡張プリンシパルにアクセスできます。

@RestController
public class SomeResourceController {

    @RequestMapping("/some-resource")
    public ResponseEntity<?> someResource(Authentication authentication) {
        CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
        return ResponseEntity.ok("woo hoo!");
    }

}

お役に立てれば!

10
zero01alpha

RESTサービスで、OAuth2Authenticationクラスを引数として追加します

@RequestMapping(value = "/{id}/products", method = RequestMethod.POST)
    public ResourceResponse<String> processProducts(OAuth2Authentication auth) {

Springbootは、ログインしたユーザーの詳細をこのオブジェクトに自動的にマッピングします。これで、以下を実行してユーザー名にアクセスできます

auth.getPrincipal().toString()
2
Chids