Tomcat /服务器重新启动后,spring + oauth2 / api / oauth / token为“未授权”

时间:2018-12-18 07:14:32

标签: java spring spring-boot spring-security spring-oauth2

我正在使用spring-security-5spring-boot 2.0.5oauth2。我已经通过在线参考进行了检查和测试。

赞:

Spring Security and OAuth2 to protect REST API endpoints

Spring Boot 2 Applications and OAuth 2

我的项目一切都很好。

当我请求此URL http://localhost:8080/api/oauth/token时,得到的响应为

enter image description here

然后重新启动服务器(Tomcat),我再次请求该URL,得到的响应为 enter image description here

所以我的问题是,重启后,客户端应用如何在access_tokenTomcat应用程序之后再次获得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 :(得分:0)

您需要将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

并获取是否与docker一起运行:

version: '3'
services:

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

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

JWTTokenStore将使您无需使用第三方软件,并且伸缩性也很好,但是使令牌的撤销变得更加困难。

对于较小的应用程序,可以将令牌存储在数据库中(请参见JdbcTokenStore)。

答案 1 :(得分:0)

在我的问题中,即使我使用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
    }
}