如何在 Spring 中为 ClientDetailsServiceConfigurer 使用 JDBC 添加客户端?
How to add a client using JDBC for ClientDetailsServiceConfigurer in Spring?
我在内存中的工作方式如下:
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456");
}
我想使用 JDBC 实现。为此,我创建了下表(使用 MySQL):
-- Tables for OAuth token store
CREATE TABLE oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove TINYINT
);
CREATE TABLE oauth_client_token (
token_id VARCHAR(255),
token BLOB,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255)
);
CREATE TABLE oauth_access_token (
token_id VARCHAR(255),
token BLOB,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication BLOB,
refresh_token VARCHAR(255)
);
CREATE TABLE oauth_refresh_token (
token_id VARCHAR(255),
token BLOB,
authentication BLOB
);
CREATE TABLE oauth_code (
code VARCHAR(255),
authentication BLOB
);
我需要在 MySQL 表中手动添加客户端吗?
我试过这个:
clients.jdbc(dataSource).withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456");
希望 Spring 将正确的内容插入到好的表中,但它似乎并没有这样做。为什么你可以在 jdbc()
之后进一步链接?
请执行以下步骤:
-
将此 schema.sql 放入您的资源文件夹中,以便在您启动服务器后由 SpringBoot 检测到。如果您不使用 spring 启动,不用担心,只需从任何 Mysql 应用程序客户端(phpmyadmin、HeidiSQL、Navicat..)导入此脚本
drop table if exists oauth_client_details;
create table oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(255)
);
drop table if exists oauth_client_token;
create table oauth_client_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication_id VARCHAR(255) PRIMARY KEY,
user_name VARCHAR(255),
client_id VARCHAR(255)
);
drop table if exists oauth_access_token;
create table oauth_access_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication_id VARCHAR(255) PRIMARY KEY,
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication LONG VARBINARY,
refresh_token VARCHAR(255)
);
drop table if exists oauth_refresh_token;
create table oauth_refresh_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication LONG VARBINARY
);
drop table if exists oauth_code;
create table oauth_code (
code VARCHAR(255), authentication LONG VARBINARY
);
drop table if exists oauth_approvals;
create table oauth_approvals (
userId VARCHAR(255),
clientId VARCHAR(255),
scope VARCHAR(255),
status VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
);
drop table if exists ClientDetails;
create table ClientDetails (
appId VARCHAR(255) PRIMARY KEY,
resourceIds VARCHAR(255),
appSecret VARCHAR(255),
scope VARCHAR(255),
grantTypes VARCHAR(255),
redirectUrl VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(255)
);
-
将您的 DataSource、authenticationManager、UserDetailsService 注入您的 OthorizationServer
@Autowired
private MyUserDetailsService userDetailsService;
@Inject
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
-
您将需要创建这两个 bean
@Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
请不要忘记 AuthorizationServer 之上的 @Configuration class
-
配置要在 mysql 数据库中创建的客户端应用程序:
clients.jdbc(dataSource).withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456");
你已经这样做了。
-
最重要的事情(我想你忘了它..)是:使用 AuthorizationServerEndpointsConfigurer 配置你的端点:
endpoints.userDetailsService(userDetailsService) .authorizationCodeServices(authorizationCodeServices()).authenticationManager(this.authenticationManager).tokenStore(tokenStore()).approvalStoreDisabled();
就是这样,伙计,现在它应该可以工作了;)
并随时询问更多...我很乐意提供帮助
我已经从 tweeter 给你发消息了!
@AndroidLover 的回答很好,但可以简化。您不需要创建 table 之类的 oauth_access_token、oauth_refresh_token 等,除非您需要 jdbc 令牌存储。
因为你只需要一个jdbc客户详情服务,你需要做的就是:
1。创建客户详细信息 table oauth_client_details,例如:
drop table if exists oauth_client_details;
create table oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(255)
);
2。创建一个实现 UserDetail 接口的用户模型,例如(我在这种情况下使用 spring jpa,你可以使用 mybatis,jdbc,随便什么):
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "user_id", nullable = false, updatable = false)
private String id;
@Column(name = "username", nullable = false, unique = true)
private String username;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "enabled", nullable = false)
@Type(type = "org.hibernate.type.NumericBooleanType")
private boolean enabled;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add((GrantedAuthority) () -> "ROLE_USER");
return authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}
3。创建自定义用户详细信息服务。请注意,在您的实现中,您应该注入您的 dao 服务(在我的例子中,我注入了一个 jpaRepository。)并且您的 dao 服务必须 有一个通过用户名查找用户的方法。:
@Service("userDetailsService")
public class UserService implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String userName) throws
UsernameNotFoundException {
return userRepository.findByUsername(userName);
}
}
4。最后,配置您的身份验证服务器:
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("dataSource")
DataSource dataSource;
@Autowired
@Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) {
configurer
.authenticationManager(authenticationManager)
.approvalStoreDisabled()
.userDetailsService(userDetailsService);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
{
clients
.jdbc(dataSource)
.inMemory().withClient("my-trusted-
client").secret("secret").accessTokenValiditySeconds(3600)
.scopes("read", "write").authorizedGrantTypes("password",
"refresh_token").resourceIds("resource");
}
}
这个问题比较老,但是 none 的回复回答了提问者原来的问题。我在熟悉 spring 的 oauth2 实现时偶然发现了同样的问题,想知道为什么 ClientDetailsServiceConfigurer
没有持久化通过 JdbcClientDetailsServiceBuilder
以编程方式添加的客户端(这是通过在配置器上调用 jdbc(datasource)
方法实例化),尽管网络上的所有教程都显示了类似的示例,例如 Wim 发布的示例。在深入研究代码后,我注意到了原因。好吧,这只是因为从未调用更新 oauth_clients_details
table 的代码。缺少的是配置所有客户端后的以下调用:.and().build()
。因此,Wim 的代码实际上必须如下所示:
clients.jdbc(dataSource).withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456").and().build();
Et voila,客户端 clientapp
现在已保存到数据库中。
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
if(!jdbcClientDetailsService.listClientDetails().isEmpty() ) {
jdbcClientDetailsService.removeClientDetails(CLIEN_ID);
}
if(jdbcClientDetailsService.listClientDetails().isEmpty() ) {
configurer.jdbc(dataSource).withClient(CLIEN_ID).secret(encoder.encode(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).and().build();
}
configurer.jdbc(dataSource).build().loadClientByClientId(CLIEN_ID);
}
我在这里检查数据库中是否存在任何客户端 table oauth_client_details。如果存在任何客户端,我将删除该条目,因为它第一次运行时不会出现任何错误,但是当您重新启动应用程序时,它会在数据库中添加条目时出现主键错误。这就是我添加此代码的原因:
if(!jdbcClientDetailsService.listClientDetails().isEmpty() ) {
jdbcClientDetailsService.removeClientDetails(CLIEN_ID);
}
删除客户端条目后,您需要添加客户端,这里是添加客户端的代码:
if(jdbcClientDetailsService.listClientDetails().isEmpty() ) {
configurer.jdbc(dataSource).withClient(CLIEN_ID).secret(encoder.encode(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).and().build();
}
在此代码中,您可以根据需要更改配置,因为每次重新启动应用程序后我们都会删除客户端条目。
这里我们正在加载所有客户详细信息:
configurer.jdbc(dataSource).build().loadClientByClientId(CLIEN_ID);
它将为您正常工作,没有任何错误。谢谢
加上我的两分钱。
如果您在启动时初始化数据库结构(删除前一个),例如:
@Bean
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
//...setting dataSource and databasePopulator
}
private DatabasePopulator databasePopulator() {
//...adding your schema script
}
@Bean
public DataSource dataSource() {
//...setting driverclassname, url, etc
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//...
clients.jdbc(this.dataSource()).withClient("example").(...).build()
}
当心。
Bean 不必按特定顺序创建,因此当您在旧表中插入行,然后用模式中的新表替换它时,您可能会遇到这种情况。所以,你可能会想一会,为什么它仍然不插入行。我希望这会对某人有所帮助。
在关注@AndroidLover 回答时,使用以下 table Postgres 架构。
create table IF NOT EXISTS oauth_client_details
(
client_id VARCHAR(256) PRIMARY KEY,
resource_ids VARCHAR(256),
client_secret VARCHAR(256),
scope VARCHAR(256),
authorized_grant_types VARCHAR(256),
web_server_redirect_uri VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(256)
);
create table IF NOT EXISTS oauth_client_token
(
token_id VARCHAR(256),
token bytea,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256)
);
create table IF NOT EXISTS oauth_access_token
(
token_id VARCHAR(256),
token bytea,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication bytea,
refresh_token VARCHAR(256)
);
create table IF NOT EXISTS oauth_refresh_token
(
token_id VARCHAR(256),
token bytea,
authentication bytea
);
create table IF NOT EXISTS oauth_code
(
code VARCHAR(256),
authentication bytea
);
create table IF NOT EXISTS oauth_approvals
(
userId VARCHAR(256),
clientId VARCHAR(256),
scope VARCHAR(256),
status VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
);
-- customized oauth_client_details table
create table IF NOT EXISTS ClientDetails
(
appId VARCHAR(256) PRIMARY KEY,
resourceIds VARCHAR(256),
appSecret VARCHAR(256),
scope VARCHAR(256),
grantTypes VARCHAR(256),
redirectUrl VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(256)
);
以下是我的 OAuth2 配置从数据库加载和验证令牌
@Configuration
@EnableAuthorizationServer
class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
@Qualifier("dataSource")
DataSource dataSource;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer) throws Exception {
authorizationServerEndpointsConfigurer.tokenStore(tokenStore());
}
}
我在内存中的工作方式如下:
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456");
}
我想使用 JDBC 实现。为此,我创建了下表(使用 MySQL):
-- Tables for OAuth token store
CREATE TABLE oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove TINYINT
);
CREATE TABLE oauth_client_token (
token_id VARCHAR(255),
token BLOB,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255)
);
CREATE TABLE oauth_access_token (
token_id VARCHAR(255),
token BLOB,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication BLOB,
refresh_token VARCHAR(255)
);
CREATE TABLE oauth_refresh_token (
token_id VARCHAR(255),
token BLOB,
authentication BLOB
);
CREATE TABLE oauth_code (
code VARCHAR(255),
authentication BLOB
);
我需要在 MySQL 表中手动添加客户端吗?
我试过这个:
clients.jdbc(dataSource).withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456");
希望 Spring 将正确的内容插入到好的表中,但它似乎并没有这样做。为什么你可以在 jdbc()
之后进一步链接?
请执行以下步骤:
-
将此 schema.sql 放入您的资源文件夹中,以便在您启动服务器后由 SpringBoot 检测到。如果您不使用 spring 启动,不用担心,只需从任何 Mysql 应用程序客户端(phpmyadmin、HeidiSQL、Navicat..)导入此脚本
drop table if exists oauth_client_details; create table oauth_client_details ( client_id VARCHAR(255) PRIMARY KEY, resource_ids VARCHAR(255), client_secret VARCHAR(255), scope VARCHAR(255), authorized_grant_types VARCHAR(255), web_server_redirect_uri VARCHAR(255), authorities VARCHAR(255), access_token_validity INTEGER, refresh_token_validity INTEGER, additional_information VARCHAR(4096), autoapprove VARCHAR(255) ); drop table if exists oauth_client_token; create table oauth_client_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication_id VARCHAR(255) PRIMARY KEY, user_name VARCHAR(255), client_id VARCHAR(255) ); drop table if exists oauth_access_token; create table oauth_access_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication_id VARCHAR(255) PRIMARY KEY, user_name VARCHAR(255), client_id VARCHAR(255), authentication LONG VARBINARY, refresh_token VARCHAR(255) ); drop table if exists oauth_refresh_token; create table oauth_refresh_token ( token_id VARCHAR(255), token LONG VARBINARY, authentication LONG VARBINARY ); drop table if exists oauth_code; create table oauth_code ( code VARCHAR(255), authentication LONG VARBINARY ); drop table if exists oauth_approvals; create table oauth_approvals ( userId VARCHAR(255), clientId VARCHAR(255), scope VARCHAR(255), status VARCHAR(10), expiresAt TIMESTAMP, lastModifiedAt TIMESTAMP ); drop table if exists ClientDetails; create table ClientDetails ( appId VARCHAR(255) PRIMARY KEY, resourceIds VARCHAR(255), appSecret VARCHAR(255), scope VARCHAR(255), grantTypes VARCHAR(255), redirectUrl VARCHAR(255), authorities VARCHAR(255), access_token_validity INTEGER, refresh_token_validity INTEGER, additionalInformation VARCHAR(4096), autoApproveScopes VARCHAR(255) );
-
将您的 DataSource、authenticationManager、UserDetailsService 注入您的 OthorizationServer
@Autowired private MyUserDetailsService userDetailsService; @Inject private AuthenticationManager authenticationManager; @Autowired private DataSource dataSource;
-
您将需要创建这两个 bean
@Bean public JdbcTokenStore tokenStore() { return new JdbcTokenStore(dataSource); } @Bean protected AuthorizationCodeServices authorizationCodeServices() { return new JdbcAuthorizationCodeServices(dataSource); }
请不要忘记 AuthorizationServer 之上的 @Configuration class
-
配置要在 mysql 数据库中创建的客户端应用程序:
clients.jdbc(dataSource).withClient("clientapp") .authorizedGrantTypes("password", "refresh_token") .authorities("USER") .scopes("read", "write") .resourceIds(RESOURCE_ID) .secret("123456");
你已经这样做了。
-
最重要的事情(我想你忘了它..)是:使用 AuthorizationServerEndpointsConfigurer 配置你的端点:
endpoints.userDetailsService(userDetailsService) .authorizationCodeServices(authorizationCodeServices()).authenticationManager(this.authenticationManager).tokenStore(tokenStore()).approvalStoreDisabled();
就是这样,伙计,现在它应该可以工作了;)
并随时询问更多...我很乐意提供帮助
我已经从 tweeter 给你发消息了!
@AndroidLover 的回答很好,但可以简化。您不需要创建 table 之类的 oauth_access_token、oauth_refresh_token 等,除非您需要 jdbc 令牌存储。
因为你只需要一个jdbc客户详情服务,你需要做的就是:
1。创建客户详细信息 table oauth_client_details,例如:
drop table if exists oauth_client_details;
create table oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(255)
);
2。创建一个实现 UserDetail 接口的用户模型,例如(我在这种情况下使用 spring jpa,你可以使用 mybatis,jdbc,随便什么):
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "user_id", nullable = false, updatable = false)
private String id;
@Column(name = "username", nullable = false, unique = true)
private String username;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "enabled", nullable = false)
@Type(type = "org.hibernate.type.NumericBooleanType")
private boolean enabled;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add((GrantedAuthority) () -> "ROLE_USER");
return authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}
3。创建自定义用户详细信息服务。请注意,在您的实现中,您应该注入您的 dao 服务(在我的例子中,我注入了一个 jpaRepository。)并且您的 dao 服务必须 有一个通过用户名查找用户的方法。:
@Service("userDetailsService")
public class UserService implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String userName) throws
UsernameNotFoundException {
return userRepository.findByUsername(userName);
}
}
4。最后,配置您的身份验证服务器:
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("dataSource")
DataSource dataSource;
@Autowired
@Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) {
configurer
.authenticationManager(authenticationManager)
.approvalStoreDisabled()
.userDetailsService(userDetailsService);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
{
clients
.jdbc(dataSource)
.inMemory().withClient("my-trusted-
client").secret("secret").accessTokenValiditySeconds(3600)
.scopes("read", "write").authorizedGrantTypes("password",
"refresh_token").resourceIds("resource");
}
}
这个问题比较老,但是 none 的回复回答了提问者原来的问题。我在熟悉 spring 的 oauth2 实现时偶然发现了同样的问题,想知道为什么 ClientDetailsServiceConfigurer
没有持久化通过 JdbcClientDetailsServiceBuilder
以编程方式添加的客户端(这是通过在配置器上调用 jdbc(datasource)
方法实例化),尽管网络上的所有教程都显示了类似的示例,例如 Wim 发布的示例。在深入研究代码后,我注意到了原因。好吧,这只是因为从未调用更新 oauth_clients_details
table 的代码。缺少的是配置所有客户端后的以下调用:.and().build()
。因此,Wim 的代码实际上必须如下所示:
clients.jdbc(dataSource).withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456").and().build();
Et voila,客户端 clientapp
现在已保存到数据库中。
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
if(!jdbcClientDetailsService.listClientDetails().isEmpty() ) {
jdbcClientDetailsService.removeClientDetails(CLIEN_ID);
}
if(jdbcClientDetailsService.listClientDetails().isEmpty() ) {
configurer.jdbc(dataSource).withClient(CLIEN_ID).secret(encoder.encode(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).and().build();
}
configurer.jdbc(dataSource).build().loadClientByClientId(CLIEN_ID);
}
我在这里检查数据库中是否存在任何客户端 table oauth_client_details。如果存在任何客户端,我将删除该条目,因为它第一次运行时不会出现任何错误,但是当您重新启动应用程序时,它会在数据库中添加条目时出现主键错误。这就是我添加此代码的原因:
if(!jdbcClientDetailsService.listClientDetails().isEmpty() ) {
jdbcClientDetailsService.removeClientDetails(CLIEN_ID);
}
删除客户端条目后,您需要添加客户端,这里是添加客户端的代码:
if(jdbcClientDetailsService.listClientDetails().isEmpty() ) {
configurer.jdbc(dataSource).withClient(CLIEN_ID).secret(encoder.encode(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).and().build();
}
在此代码中,您可以根据需要更改配置,因为每次重新启动应用程序后我们都会删除客户端条目。
这里我们正在加载所有客户详细信息:
configurer.jdbc(dataSource).build().loadClientByClientId(CLIEN_ID);
它将为您正常工作,没有任何错误。谢谢
加上我的两分钱。
如果您在启动时初始化数据库结构(删除前一个),例如:
@Bean
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
//...setting dataSource and databasePopulator
}
private DatabasePopulator databasePopulator() {
//...adding your schema script
}
@Bean
public DataSource dataSource() {
//...setting driverclassname, url, etc
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//...
clients.jdbc(this.dataSource()).withClient("example").(...).build()
}
当心。
Bean 不必按特定顺序创建,因此当您在旧表中插入行,然后用模式中的新表替换它时,您可能会遇到这种情况。所以,你可能会想一会,为什么它仍然不插入行。我希望这会对某人有所帮助。
在关注@AndroidLover 回答时,使用以下 table Postgres 架构。
create table IF NOT EXISTS oauth_client_details
(
client_id VARCHAR(256) PRIMARY KEY,
resource_ids VARCHAR(256),
client_secret VARCHAR(256),
scope VARCHAR(256),
authorized_grant_types VARCHAR(256),
web_server_redirect_uri VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(256)
);
create table IF NOT EXISTS oauth_client_token
(
token_id VARCHAR(256),
token bytea,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256)
);
create table IF NOT EXISTS oauth_access_token
(
token_id VARCHAR(256),
token bytea,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication bytea,
refresh_token VARCHAR(256)
);
create table IF NOT EXISTS oauth_refresh_token
(
token_id VARCHAR(256),
token bytea,
authentication bytea
);
create table IF NOT EXISTS oauth_code
(
code VARCHAR(256),
authentication bytea
);
create table IF NOT EXISTS oauth_approvals
(
userId VARCHAR(256),
clientId VARCHAR(256),
scope VARCHAR(256),
status VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
);
-- customized oauth_client_details table
create table IF NOT EXISTS ClientDetails
(
appId VARCHAR(256) PRIMARY KEY,
resourceIds VARCHAR(256),
appSecret VARCHAR(256),
scope VARCHAR(256),
grantTypes VARCHAR(256),
redirectUrl VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(256)
);
以下是我的 OAuth2 配置从数据库加载和验证令牌
@Configuration
@EnableAuthorizationServer
class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
@Qualifier("dataSource")
DataSource dataSource;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer) throws Exception {
authorizationServerEndpointsConfigurer.tokenStore(tokenStore());
}
}