Как извлечь информацию из входящего JWT, созданного внешней службой? - PullRequest
0 голосов
/ 13 ноября 2018

Как извлечь информацию из входящего JWT, созданного внешней службой?( Okta )

Мне нужно выполнить поиск в базе данных пользовательской информации на основе одного из полей в JWT.(Мне также нужна защита на уровне метода, основанная на области действия JWT.)

Секрет заключается в использовании от AccessTokenConverter до extractAuthentication(), а затем использовать его для поиска UserDetails.Я застрял, потому что каждый пример, который я могу найти, включает в себя настройку сервера авторизации, которого у меня нет, и я не могу сказать, будет ли JwtAccessTokenConverter работать на сервере ресурсов.

Мой сервер ресурсовзапускает и обрабатывает запросы, но мой пользовательский JwtAccessTokenConverter никогда не вызывается во время входящих запросов;Все мои запросы приходят с принципалом anonymousUser.

Я использую Spring 5.1.1.

Моя конфигурация сервера ресурсов

@Configuration
@EnableResourceServer
public class OauthResourceConfig extends ResourceServerConfigurerAdapter {

    @Value("${oauth2.audience}")
    String audience;

    @Value("${oauth2.baseUrl}/v1/keys")
    String jwksUrl;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .authorizeRequests()
                .anyRequest().authenticated()
                .antMatchers("/api/**").permitAll();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources
                .tokenServices(tokenServices())
                .resourceId(audience);
    }

    @Primary
    @Bean
    public DefaultTokenServices tokenServices() throws Exception {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore());

        return tokenServices;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwkTokenStore(jwksUrl, accessTokenConverter());
    }

    @Bean
    public AccessTokenConverter accessTokenConverter() {
        return new CustomJwtAccessTokenConverter();
    }
}

Мой токен пользовательского доступаКонвертер

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) {

                ... Do the database lookup here ...

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

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

И мой ресурс

@GET
@PreAuthorize("#oauth2.hasScope('openid')")
public Response getRecallsByVin(@QueryParam("vin") String vin,
                                @QueryParam("page") Integer pageNumber,
                                @QueryParam("pageSize") Integer pageSize) {
    List<VehicleNhtsaCampaign> nhtsaCampaignList;
    List<OpenRecallsDto> nhtsaCampaignDtoList;
    SecurityContext securityContext = SecurityContextHolder.getContext();


    Object principal = securityContext.getAuthentication().getPrincipal();

 ... More irrelevant code follows ...

Прежде всего, аннотация @PreAuthorize ничего не делает.Если я изменю его на @PreAuthorize("#oauth2.hasScope('FooBar')"), он все равно пропустит запрос.

Во-вторых, мне нужно извлечь другую информацию из JWT, чтобы я мог выполнить поиск пользователя в моей базе данных.Я думал, что, добавив accessTokenConverter() в конфигурацию сервера ресурсов, JWT будет проанализирован и помещен в ответ securityContext.getAuthentication().Вместо этого все, что я получаю, это «anonymousUser».

ОБНОВЛЕНИЕ: позже я узнал, что мне нужны данные, поступающие в пользовательский заголовок, поэтому мне не нужно ничего извлекать из JWT.Я так и не смог подтвердить ни один из предложенных ответов.

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Вы можете создать фильтр, который проверяет и устанавливает токен на SecurityContextHolder. Это то, что я сделал в своем проекте, используя jsonwebtoken зависимость:

        public class JWTFilter extends GenericFilterBean {

            private String secretKey = 'yoursecret';

            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
                throws IOException, ServletException {
                HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
                String jwt = resolveToken(httpServletRequest);
                if (validateToken(jwt)) {
                    Authentication authentication = getAuthentication(jwt);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
                filterChain.doFilter(servletRequest, servletResponse);
            }

            private String resolveToken(HttpServletRequest request){
                String bearerToken = request.getHeader("Authorization");
                if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
                    return bearerToken.substring(7, bearerToken.length());
                }
                return null;
            }

    public Authentication getAuthentication(String token) {
            Claims claims = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody();

            Collection<? extends GrantedAuthority> authorities =
                Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());

            User principal = new User(claims.getSubject(), "", authorities);

            return new UsernamePasswordAuthenticationToken(principal, token, authorities);
        }

public boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException e) {
        } catch (MalformedJwtException e) {
        } catch (ExpiredJwtException e) {
        } catch (UnsupportedJwtException e) {
        } catch (IllegalArgumentException e) {      
        }
        return false;
    }
        }

Вы можете получить доступ к своему токену с SecurityContextHolder.

Для более чистого способа доступа к полям токена я создал POJO-модели своего токена с http://www.jsonschema2pojo.org/

0 голосов
/ 13 ноября 2018

Вы используете Spring Boot?

Spring Security 5.1 имеет поддержку токенов доступа JWT. Например, вы можете просто указать новый JwtDecoder: https://github.com/okta/okta-spring-boot/blob/spring-boot-2.1/oauth2/src/main/java/com/okta/spring/boot/oauth/OktaOAuth2ResourceServerAutoConfig.java#L62-L84

...