问题
I am using spring-security-5
, spring-boot 2.0.5
and oauth2
. I have checked and test by online reference.
Like :
Spring Security and OAuth2 to protect REST API endpoints
Spring Boot 2 Applications and OAuth 2
Everything is fine in my project.
When I request this URL , http://localhost:8080/api/oauth/token
, I get response as
And I restart the server(Tomcat), I request that URL again, I get response as
So my question is, how the client-app can get access_token
again after Tomcat
or spring-boot
application is restart?
One thing
For that kind of situation, if I delete the record of OAUTH_CLIENT_DETAILS
table in database, It is OK to request again. I also get access_token
again.
Update
Please don't miss understanding response json
format, every response I wrap by custom object like as below.
{
"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
}
}
Update 2
I use JDBCTokenStore
, all of oauth
information keep in database
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);
}
}
回答1:
You need to set the tokenStore
to something different than InMemory
.
I tend to use redis, as it scales very well, is fast as hell and once it's there you can use it as cache:
@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
And to get if up and running with docker:
version: '3'
services:
cache:
image: redis:latest
ports:
- "6380:6379"
db:
image: postgres:latest
ports:
- "5454:5432"
environment:
- POSTGRES_DB=mydb
The JWTTokenStore
would enable you to go without 3rd party software and scales good as well, but makes it harder to revoke token.
For smaller applications, tokens may be ok to store in the database (see JdbcTokenStore
).
回答2:
In my question, even I use JdbcTokenStore
, it is still getting Unauthorized
response after restart the sever.
Now, I get a solution for my issue which is using JwtTokenStore
. It is stateless
. I just need to modify my AuthorizationServerConfig
class as below. I don't need any oauth
related table in my database now.
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
}
}
来源:https://stackoverflow.com/questions/53828014/spring-oauth2-api-oauth-token-is-unauthorized-after-tomcat-server-is-restar