我正在使用spring-security-5
,spring-boot 2.0.5
和oauth2
。我已经通过在线参考进行了检查和测试。
赞:
Spring Security and OAuth2 to protect REST API endpoints
Spring Boot 2 Applications and OAuth 2
我的项目一切都很好。
当我请求此URL http://localhost:8080/api/oauth/token
时,得到的响应为
然后重新启动服务器(Tomcat),我再次请求该URL,得到的响应为
所以我的问题是,重启后,客户端应用如何在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);
}
}
答案 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
}
}