Spring + oauth2 / api / oauth / token не авторизован после перезапуска Tomcat / сервера - PullRequest
0 голосов
/ 18 декабря 2018

Я использую spring-security-5, spring-boot 2.0.5 и oauth2.Я проверил и протестировал онлайн-справку.

Как:

Spring Security и OAuth2 для защиты конечных точек API REST

Spring Boot 2Приложения и OAuth 2

В моем проекте все нормально.

Когда я запрашиваю этот URL, http://localhost:8080/api/oauth/token, я получаю ответ как

enter image description here

И я перезагружаю сервер (Tomcat) , я снова запрашиваю этот URL, я получаю ответ как enter image description here

Так что мой вопрос, как клиентское приложение может получить access_token снова после Tomcat или spring-boot приложение - перезапуск ?

Одна вещь Для такого рода ситуаций, если я удаляю запись таблицы OAUTH_CLIENT_DETAILS в базе данных, этоОК, чтобы запросить снова.Я также получаю access_token снова.

Обновление

Пожалуйста, не пропустите понимание формата ответа json, каждый ответ, который я обертываю пользовательским объектом, как показано ниже.

{
    "status": "SUCCESS", <-- here my custom
    "data": {
        "timestamp": "2018-12-18T07:17:00.776+0000", <-- actual response from oauth2
        "status": 401,  <-- actual response from oauth2                 
        "error": "Unauthorized", <-- actual response from oauth2
        "message": "Unauthorized", <-- actual response from oauth2
        "path": "/api/oauth/token" <-- actual response from oauth2
    }
}

Обновление 2

Я использую JDBCTokenStore, вся информация oauth хранится в базе данных

package com.mutu.spring.rest.oauth2;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    static final String CLIEN_ID = "zto-api-client";
//  static final String CLIENT_SECRET = "zto-api-client";
    static final String CLIENT_SECRET = "$2a$04$HvD/aIuuta3B5DjXXzL08OSIcYEoFsAYK9Ys4fKpMNHTODZm.mzsq";
    static final String GRANT_TYPE_PASSWORD = "password";
    static final String AUTHORIZATION_CODE = "authorization_code";
    static final String REFRESH_TOKEN = "refresh_token";
    static final String IMPLICIT = "implicit";
    static final String SCOPE_READ = "read";
    static final String SCOPE_WRITE = "write";
    static final String TRUST = "trust";
    static final int ACCESS_TOKEN_VALIDITY_SECONDS = 1*60;
    static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 2*60;

    @Autowired
    private AuthenticationManager authenticationManager;

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

    @Autowired
    private DataSource dataSource;

    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()")
                   .checkTokenAccess("isAuthenticated()");
    }


    @Override
    public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {

        configurer
                .jdbc(dataSource)
                .withClient(CLIEN_ID)
                .secret("{bcrypt}" + CLIENT_SECRET)
                .authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT )
//              .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                .scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
                .accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
                .refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager);
    }
}

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018

В моем вопросе, даже если я использую JdbcTokenStore, он все равно получает Unauthorized ответ после перезапуска сервера.

Теперь я получил решение для моей проблемы, использующее JwtTokenStore.Это stateless.Мне просто нужно изменить мой класс AuthorizationServerConfig, как показано ниже.Мне не нужна таблица, связанная с oauth, в моей базе данных.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    static final String CLIEN_ID = "zto-api-client";
//  static final String CLIENT_SECRET = "zto-api-client";
    static final String CLIENT_SECRET = "$2a$04$HvD/aIuuta3B5DjXXzL08OSIcYEoFsAYK9Ys4fKpMNHTODZm.mzsq";
    static final String GRANT_TYPE_PASSWORD = "password";
    static final String AUTHORIZATION_CODE = "authorization_code";
    static final String REFRESH_TOKEN = "refresh_token";
    static final String IMPLICIT = "implicit";
    static final String SCOPE_READ = "read";
    static final String SCOPE_WRITE = "write";
    static final String TRUST = "trust";
    static final int ACCESS_TOKEN_VALIDITY_SECONDS = 5*60;
    static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 5*60;

    @Autowired
    private AuthenticationManager authenticationManager;

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


    // replace
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("as-you-like-your-key");
        return converter;
    }

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

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()")
                   .checkTokenAccess("isAuthenticated()");
    }


    @Override
    public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {

        configurer
                .inMemory() // replace
                .withClient(CLIEN_ID)
                .secret("{bcrypt}" + CLIENT_SECRET)
                .authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT )
                .scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
                .accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
                .refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager)
                .accessTokenConverter(accessTokenConverter());// replace
    }
}
0 голосов
/ 18 декабря 2018

Вам нужно установить tokenStore на что-то отличное от InMemory.

Я склонен использовать redis, так как он очень хорошо масштабируется, быстр, как ад, и как только он появится, вы можете использовать его каккеш:

@Configuration
@EnableAuthorizationServer
class AuthorizationServerConfig : AuthorizationServerConfigurerAdapter() {

    @Bean
    fun tokenStore(): TokenStore = RedisTokenStore(redisConnectionFactory).apply {
        setAuthenticationKeyGenerator(authenticationKeyGenerator)
    }
}

Application.yaml:

spring:
    redis: 
        host: 0.0.0.0
        password:
        port: 6380
        database: 0

И получить, если работает с докером:

version: '3'
services:

  cache:
    image: redis:latest
    ports:
      - "6380:6379"

  db:
    image: postgres:latest
    ports:
      - "5454:5432"
    environment:
      - POSTGRES_DB=mydb

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

Для небольших приложений токены могут быть пригодны для хранения в базе данных (см. JdbcTokenStore).

...