[英]problems injecting custom userDetailsService in Spring Security OAuth2
I am using Spring Security OAuth2 2.0.7.RELEASE. 我正在使用Spring Security OAuth2 2.0.7.RELEASE。 As i am using ORM to connect to my database and default JdbcUserDetailsManager uses jdbc i wanted to implement my own UserDetailsService, which is 当我使用ORM连接到数据库并且默认JdbcUserDetailsManager使用jdbc时,我想实现自己的UserDetailsService,即
@Service
public class UserService
implements UserDetailsService {
@Override
public UserDetailsService loadUserByUsername(String username) throws UsernameNotFoundException {
// I tested this logic and works fine so i avoid this lines
return userDetailsService;
}
}
Besides, i've modified authorities schema as follows: 此外,我还修改了权限架构,如下所示:
mysql> describe authorities;
+--------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------------------+------+-----+---------+----------------+
| authority_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| user_id | bigint(20) unsigned | NO | MUL | NULL | |
| authority | varchar(256) | NO | | NULL | |
+--------------+---------------------+------+-----+---------+----------------+
Then i am injecting my custom userDetailsService like this: 然后,我像这样注入我的自定义userDetailsService:
@Configuration
@Import(OAuth2SupportConfig.class)
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends
AuthorizationServerConfigurerAdapter {
...
@Autowired
private UserDetailsService userDetailsService
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore).tokenServices(tokenService);
endpoints.userDetailsService(userDetailsService); // Inject custom
endpoints.authorizationCodeServices(authorizationCodeServices);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.jdbc(dataSource);
}
}
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AuthenticationManagerConfiguration
extends GlobalAuthenticationConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailsService userService;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(this.dataSource).and().userDetailsService(this.userService);// Inject custom
}
}
If i send /oauth/token request with grant_type=password then i get this error 如果我使用Grant_type = password发送/ oauth / token请求,则会出现此错误
POST /oauth/token HTTP/1.1
Host: localhost:8080
Authorization: Basic aW5kaXJhOnNlY3JldA==
Cache-Control: no-cache
Postman-Token: c89baf37-8ad2-4270-5251-9715bfab470a
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=user&password=pass
(where clientId and clientSecret is encoded) (其中对clientId和clientSecret进行编码)
{
"error": "unauthorized",
"error_description": "PreparedStatementCallback; bad SQL grammar [select username,authority from authorities where username = ?]; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'username' in 'field list'"
}
Apparently is still using the default JdbcDaoImpl. 显然仍在使用默认的JdbcDaoImpl。 In fact, when i started debugging, i found that is following these steps: 实际上,当我开始调试时,发现执行以下步骤:
I don't know why this is happening. 我不知道为什么会这样。 It sounds like a bug to me. 对我来说,这听起来像个虫子。 Do you find anything wrong? 你有没有发现什么问题?
You are using auth.jdbcAuthentication().dataSource(this.dataSource).and().userDetailsService(this.userService);// Inject custom
and I here it's creating two auth managers - one with default JdbcDaoImpl
and dataSource
pointing to this.dataSource
and another with your custom userService
. 您正在使用auth.jdbcAuthentication().dataSource(this.dataSource).and().userDetailsService(this.userService);// Inject custom
,我在这里创建了两个auth管理器-一个具有默认的JdbcDaoImpl
和dataSource
指向this.dataSource
和另一个带有自定义userService
。 Try just putting auth.userDetailsService(this.userService)
(I hope that userService already have jdbc autowired inside). 尝试仅将auth.userDetailsService(this.userService)
(我希望userService已将jdbc自动连接到其中)。
The point here is that .and()
is used to add different authentication configurations to the authentication manager, not configuring the jdbcAuthentication()
one. 这里的重点是.and()
用于向身份验证管理器添加不同的身份验证配置,而不是配置jdbcAuthentication()
。
In 2.0.7
when you do a POST/GET
request on /oauth/token
with grant type as password
, it will actually except a ClientDetailsUserDetailsService
but not UserDetailsService
. 在2.0.7
当您以授予类型作为password
对/oauth/token
进行POST/GET
请求时,它实际上将是ClientDetailsUserDetailsService
而不是UserDetailsService
。
I had similar issue and this is how I solved it : 我有类似的问题,这就是我解决的方法:
public class AppClientDetailsUserDetailsService extends ClientDetailsUserDetailsService {
public AppClientDetailsUserDetailsService(ClientDetailsService clientDetailsService) {
super(clientDetailsService);
}
}
public class AppConsumerDetailsService implements ClientDetailsService {
public ClientDetails loadClientByClientId(String clientId)
throws OAuth2Exception {
//some logic
}
}
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="authenticationManager"
entry-point-ref="entryPoint" xmlns="http://www.springframework.org/schema/security"
>
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="entryPoint" />
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
</http>
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
authenticationManager
is the bean for AppClientDetailsUserDetailsService
whose constructor argument is AppConsumerDetailsService
. authenticationManager
是AppClientDetailsUserDetailsService
的bean,其构造函数参数为AppConsumerDetailsService
。
The problem is that you are using default JdbcDaoImpl
. 问题是您正在使用默认的JdbcDaoImpl
。 The query which is causing the problem is 引起问题的查询是
public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
"select username,authority " +
"from authorities " +
"where username = ?";
This is used as a default query to load all authorities for the user by userName inside JdbcDaoImpl
. 这是默认查询,它通过JdbcDaoImpl
的userName加载用户的所有权限。 So if you still want to use default JdbcDaoImpl
- you can set the custom query, which will do the job with your tables, as the parameter of it. 因此,如果您仍然想使用默认的JdbcDaoImpl
您可以设置自定义查询,该查询将对您的表执行此操作,并将其作为参数。
I'm not sure what is the schema of you users table, but something like this should be similar(if you are configuring JdbcDaoImpl bean programmatically): 我不确定users表的模式是什么,但是类似的东西应该是相似的(如果您以编程方式配置JdbcDaoImpl bean):
String query = "select username,authority " +
"from authorities join users on users.id = authorities.user_id " +
"where users.username = ?";
jdbcDaoImpl.setAuthoritiesByUsernameQuery(query);
Or if you create JdbcDaoImpl from XML: 或者,如果您从XML创建JdbcDaoImpl:
<bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="authoritiesByUsernameQuery"
value="select username,authority from authorities
join users on users.id = authorities.user_id
where users.username = ?"/>
</bean>
There might be some other default queries that you would like to change to fit your schema, take a look at them inside JdbcDaoImpl
. 您可能需要更改其他一些默认查询以适合您的模式,请在JdbcDaoImpl
中对其进行查看。
You might also consider a possibility of writing your own implementation of UserDetailsService
if it will start getting too far from default JdbcDaoImpl
one. 如果与默认的JdbcDaoImpl
相距太远,您可能还考虑编写自己的UserDetailsService
实现的可能性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.