Как получить строку токена jwt на сервисном уровне, когда пользователь впервые запрашивает использование jwt, Oauth2, Spring Security? - PullRequest
0 голосов
/ 26 апреля 2018

Я новичок в разработке микроуслуг с JWT.Вот моя структура проекта:

Первый микро-сервис используется для аутентификации пользователей с помощью jwt и Oauth2 с использованием имени пользователя и пароля.Этот микросервис называется auth-service.

URL запроса на вход в систему выглядит так:

[http://localhost:9092/oauth/token?username=s@sawai.com&password=randomPassword&grant_type=password][1]

При вызове этого URL мы получили токен jwt, подобный:

{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyX2lkIl0sInVzZXJfbmFtZSI6InNAc2F3YWkuY29tIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sInRlbmFudElkIjoic2F3YWkuY29tIiwic3lzdGVtR2VuZXJhdGVkUGFzc3dvcmQiOnRydWUsImlkIjoiNTYzOTFhYzAtZDc4OC00ODEyLThmYWMtODEwZTIyMjdjYmI1IiwiZXhwIjoxNTI0NzMxNzgwLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6ImY0ZTNmNTM5LWRkNDgtNGMxMy05OTg5LTcwM2E1NWYxMjNlYyIsImVtYWlsIjoic0BzYXdhaS5jb20iLCJjbGllbnRfaWQiOiJ0cnVzdGVkLWFwcCJ9.AS1tXpUcPMgEw63FrvPP-xGBz7qCi14Eqe29rDzTXPg","token_type":"bearer","refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyX2lkIl0sInVzZXJfbmFtZSI6InNAc2F3YWkuY29tIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6ImY0ZTNmNTM5LWRkNDgtNGMxMy05OTg5LTcwM2E1NWYxMjNlYyIsInRlbmFudElkIjoic2F3YWkuY29tIiwic3lzdGVtR2VuZXJhdGVkUGFzc3dvcmQiOnRydWUsImlkIjoiNTYzOTFhYzAtZDc4OC00ODEyLThmYWMtODEwZTIyMjdjYmI1IiwiZXhwIjoxNTI0NzQ2MTgwLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjI1ZmJlY2IyLWRhODgtNDY1ZS1iM2I2LTFlN2NmYjBlYmVjMiIsImVtYWlsIjoic0BzYXdhaS5jb20iLCJjbGllbnRfaWQiOiJ0cnVzdGVkLWFwcCJ9.jSG5zUBzu9yGqnBueU7fkIZV6XhXD8oCkYCerwHkkOw","expires_in":14399,"scope":"read write","tenantId":"sawai.com","systemGeneratedPassword":true,"id":"56391ac0-d788-4812-8fac-810e2227cbb5","email":"s@sawai.com","jti":"f4e3f539-dd48-4c13-9989-703a55f123ec"}

В базе данных службы авторизации мы просто создаем одну таблицу с именем users со следующими полями:

id varchar (255)

create_on datetime

last_modified_date datetime

emailvarchar (255)

бит включен (1)

пароль varchar (255)

роль varchar (255)

бит system_generated_password (1)

tenant_id varchar (255)

бит подтверждения (1)

OK.

Теперь еще один микросервис называется company и в сервисе компании у нас есть список пользователей (не такой как пользователи сервиса аутентификации, потому что сервис аутентификации содержит пользователей для нескольких микро-сервисов, таких как: компания, кандидат и т. д.).

Теперь мы хотим сохранить last_logged_on для пользователей компании.Таким образом, администратор может проверить, когда пользователь вошел в систему в последний раз.

Что мы пытаемся сделать: Когда пользователь входит в систему с использованием службы аутентификации и тип пользователя - это пользователь компании, тогда позвоните в службу компании и обновите пользователей last_logged_on .Для вызова службы компании нам нужен токен jwt-access, потому что URL-адреса безопасны на стороне компании.Итак, как мы можем получить токен доступа в сервисе аутентификации, когда мы запрашиваем токен jwt.

Вот конфигурация для jwt с пружинной загрузкой на стороне аутентификации.

package com.cs.je.auth.config.jwt;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import com.cs.je.auth.enums.Role;
import com.cs.je.auth.model.User;
/**
 * @author sawai
 *
 */

@Configuration
@EnableAuthorizationServer
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {

    @Value("${auth.token.time}")
    private int accessTokenValiditySeconds;

    @Value("${refresh.token.time}")
    private int refreshTokenValiditySeconds;

    @Value("${security.oauth2.resource.id}")
    private String resourceId;

    @Value("${trusted.app.name}")
    private String trustedAppName;

    @Value("${trusted.app.secret}")
    private String trustedAppSecret;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private CustomAccessTokenConverter customAccessTokenConverter;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints
                .authenticationManager(this.authenticationManager)
                .tokenServices(tokenServices())
                .tokenStore(tokenStore())
                .accessTokenConverter(accessTokenConverter());

    }

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

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer
        // we're allowing access to the token only for clients with 'ROLE_TRUSTED_CLIENT' authority
                //.tokenKeyAccess("permitAll()")
                .tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')")
                .checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient(trustedAppName)
                    .authorizedGrantTypes("client_credentials", "password", "refresh_token")
                    .authorities("ROLE_TRUSTED_CLIENT")
                    .scopes("read", "write")
                    .resourceIds(resourceId)
    //                .accessTokenValiditySeconds(accessTokenValiditySeconds)
    //               .refreshTokenValiditySeconds(refreshTokenValiditySeconds)
                    .secret(trustedAppSecret);
    }

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

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        System.out.println("3333333333333");
        DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
        tokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {
            @Override
            public Authentication extractAuthentication(Map<String, ?> map) {
                Authentication authentication = super.extractAuthentication(map);
                System.out.println("222222222222");
                // User is my custom UserDetails class
                User user = new User();
                user.setTenantId(map.get("tenantId").toString());
                user.setEmail(map.get("email").toString());
                user.setId(map.get("id").toString());
                //user.setPassword(map.get("password").toString());
                //System.out.println("date " + );
                Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
                authorities.addAll(authentication.getAuthorities());
                user.setGrantedAuthorities(authorities);
                user.setRole(Role.valueOf(authorities.iterator().next().toString()));
                //user.setSpecialKey(map.get("specialKey").toString());
                return new UsernamePasswordAuthenticationToken(user,
                        authentication.getCredentials(), authentication.getAuthorities());
            }
        });

        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("key");
        converter.setAccessTokenConverter(customAccessTokenConverter);
        converter.setAccessTokenConverter(tokenConverter);
        return converter;
    }    

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setAccessTokenValiditySeconds(accessTokenValiditySeconds);
        defaultTokenServices.setRefreshTokenValiditySeconds(refreshTokenValiditySeconds);
        defaultTokenServices.setReuseRefreshToken(false);
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
        return defaultTokenServices;
    }    
}

Вот расширение пользовательских токенов:

package com.cs.je.auth.config.jwt;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;

import com.cs.je.auth.enums.Role;
import com.cs.je.auth.model.User;
import com.cs.je.auth.rest.api.call.CustomRestTemplate;
import com.cs.je.auth.service.UserService;
import com.cs.je.auth.utils.EncTokenUtils;
import com.cs.je.auth.utils.UserUtils;

/**
 * @author sawai
 *
 */

@Component
public class CustomTokenEnhancer implements TokenEnhancer {

    @Autowired
    private UserService userService;

    @Autowired
    private CustomRestTemplate customRestTemplate;

    @Value("${microservice.company.protocol}")
    private String protocol;

    @Value("${microservice.company.port}")
    private String port;

    @Value("${microservice.company.ip}")
    private String ipAddress;

    @Value("${microservice.company.user.api}")
    private String userCreateUrl;

    @Autowired
    private TokenStore tokenStore;

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Map<String, Object> additionalInfo = new HashMap<>();
        if (authentication != null) {
            User user = (User)authentication.getPrincipal();
            additionalInfo.put("email", user.getEmail());
            additionalInfo.put("tenantId", user.getTenantId());
            additionalInfo.put("id", user.getId());
            if (user.isSystemGeneratedPassword()) {
                additionalInfo.put("systemGeneratedPassword", true);
            }

            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            System.out.println(accessToken.getRefreshToken());
            System.out.println(accessToken.getRefreshToken().getValue());
            System.out.println(accessToken.getTokenType());


            /*if (user.getRole().equals(Role.ROLE_ADMIN) || user.getRole().equals(Role.ROLE_USER)) {
                 String token = accessToken.toString();
                 try {
                    System.out.println("before call");
                    //ResponseEntity<Object> responseEntity = customRestTemplate.exchange(protocol + "://" + ipAddress + ":" + port + userCreateUrl + "/" + user.getId() + "/last-loggedOn", token, EncTokenUtils.getEncToken(user.getEmail()), HttpMethod.PUT, null);
                    System.out.println("successfull");
                 } catch (Exception e) {
                    e.printStackTrace();
                } 
            }*/
            System.out.println("1111111111");
        }
        return accessToken;
    }
}

Конфигурация безопасности

package com.cs.je.auth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.cs.je.auth.constant.Constants;
import com.cs.je.auth.enums.Role;
import com.cs.je.auth.model.User;
import com.cs.je.auth.repository.UserRepository;
import com.cs.je.auth.service.UserService;
/**
 * @author sawai
 *
 */
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;

    /*@Override
    public void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll()
        .antMatchers("/").permitAll()
        .antMatchers(HttpMethod.POST ,"/user/**").hasAnyAuthority("ROLE_SUPER_USER", "ROLE_ADMIN")
        .anyRequest().authenticated();

        httpSecurity.csrf().disable();
        httpSecurity.headers().frameOptions().disable();
        httpSecurity.requestCache().requestCache(new NullRequestCache());
        httpSecurity.httpBasic();
        //httpSecurity.addFilterBefore(CORSFilter, ChannelProcessingFilter.class);
    }*/

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder);

        if (userRepository.count() < 1) {
            User user = new User();
            user.setEmail("jeAdmin@email.com");
            user.setPassword("jeAdminUser");
            user.setTenantId(Constants.JE_TENANT_ID);
            user.setRole(Role.ROLE_SUPER_USER);
            user.setValidate(true);
            userService.create(user, null);
        }
     }

    @Override
    public void configure(WebSecurity webSecurity) {
        webSecurity.ignoring().antMatchers("/api/candidate/**");
        webSecurity.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
        webSecurity.ignoring().antMatchers("/api/company/**");
        webSecurity.ignoring().antMatchers("/api/forgotPassword/**");
        //webSecurity.ignoring().antMatchers(HttpMethod.POST, "/oauth/**");
    }
}

ResourceConfig.java

package com.cs.je.auth.config.jwt;
/**
 * @author sawai
 *
 */

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.savedrequest.NullRequestCache;
import com.cs.je.auth.filter.CORSFilter;

@Configuration 
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {

    @Value("${security.oauth2.resource.id}")
    private String resourceId;

    // The DefaultTokenServices bean provided at the AuthorizationConfig
    @Autowired
    private DefaultTokenServices tokenServices;

    // The TokenStore bean provided at the AuthorizationConfig
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private CORSFilter corsFilter;

    // To allow the rResourceServerConfigurerAdapter to understand the token,
    // it must share the same characteristics with AuthorizationServerConfigurerAdapter.
    // So, we must wire it up the beans in the ResourceServerSecurityConfigurer.
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
                .resourceId(resourceId)
                .tokenServices(tokenServices)
                .tokenStore(tokenStore);
    }


    public void configure(WebSecurity webSecurity) {
        webSecurity.ignoring().antMatchers("/api/candidate/**");
    //  webSecurity.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    //  webSecurity.ignoring().antMatchers(HttpMethod.POST, "/oauth/**");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http

        .authorizeRequests().antMatchers("/").permitAll().and().authorizeRequests().antMatchers("/console/**")
        .permitAll().and().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll().and()

                // when restricting access to 'Roles' you must remove the "ROLE_" part role
                // for "ROLE_USER" use only "USER"
                //.antMatchers("/api/hello").access("hasAnyRole('USER')")          
                //.antMatchers("/api/admin").hasRole("ADMIN")
                .authorizeRequests().antMatchers(HttpMethod.POST ,"/api/user/**").hasAnyAuthority("ROLE_SUPER_USER", "ROLE_ADMIN")
                // restricting all access to /api/** to authenticated users
                //.antMatchers("/**")
                //.antMatchers("/api/**").authenticated();
                .anyRequest().authenticated();

        http.csrf().disable();
        http.headers().frameOptions().disable();
        http.requestCache().requestCache(new NullRequestCache());
        http.httpBasic();
        http.addFilterBefore(corsFilter, ChannelProcessingFilter.class);
    }
}

Все вышеперечисленные конфигурации находятся на стороне службы аутентификации.Теперь, когда пользователь запрашивает токен jwt при авторизации, мы хотим получить значение токена доступа на уровне сервисов, чтобы я мог вызывать сервис компании по безопасному URL-адресу.

Пожалуйста, сообщите мне, как мы можем получить токен доступазначение, когда пользовательский запрос на токен jwt.Если вы посмотрите на CustomTokenEnhancer, то мы попытались напечатать там токен доступа, используя следующие операторы:

**System.out.println(accessToken.getRefreshToken());
System.out.println(accessToken.getRefreshToken().getValue());
System.out.println(accessToken.getTokenType());**

Но значения похожи на: 642e0cf2-9214-42d8-ae85-29e5cdfccef1 Нонам нужен настоящий токен здесь.

Пожалуйста, ведите меня.

1 Ответ

0 голосов
/ 19 июля 2019

Вы можете использовать этот метод

public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String TOKEN_SEPERATOR = " ";

public static String getAccessToken(){
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    if (requestAttributes instanceof ServletRequestAttributes) {
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        return Arrays.asList(request.getHeader(AUTHORIZATION_HEADER).split(TOKEN_SEPERATOR)).get(1);
    }
    return null;
}
...