我正在使用Spring Security OAuth2 2.0.7.RELEASE。因为我使用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;
}
}
此外,我修改了权限模式如下:
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 | |
+--------------+---------------------+------+-----+---------+----------------+
然后我正在注入我的自定义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
}
}
如果我使用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
(其中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'"
}
显然仍在使用默认的JdbcDaoImpl。事实上,当我开始调试时,我发现遵循以下步骤:
我不知道为什么会这样。这对我来说听起来像个错误。 你觉得有什么不对吗?
答案 0 :(得分:3)
您正在使用auth.jdbcAuthentication().dataSource(this.dataSource).and().userDetailsService(this.userService);// Inject custom
我在这里创建了两个身份验证管理器 - 一个默认JdbcDaoImpl
和dataSource
指向this.dataSource
,另一个指定您自定义userService
。尝试只放auth.userDetailsService(this.userService)
(我希望userService已经在内部自动装配了jdbc)。
这里的要点是.and()
用于向身份验证管理器添加不同的身份验证配置,而不是配置jdbcAuthentication()
。
答案 1 :(得分:1)
在2.0.7
POST/GET
/oauth/token
{}}} {}}} {} {}} password
{}} {} {{}} }}
我有类似的问题,这就是我解决它的方法:
ClientDetailsUserDetailsService
UserDetailsService
是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>
的bean,其构造函数参数为authenticationManager
。
答案 2 :(得分:1)
问题是您使用的是默认<?php
## Function to set file permissions to 0644 and folder permissions to 0755
function AllDirChmod( $dir = "./", $dirModes = 0755, $fileModes = 0644 ){
$d = new RecursiveDirectoryIterator( $dir );
foreach( new RecursiveIteratorIterator( $d, 1 ) as $path ){
if( $path->isDir() ) chmod( $path, $dirModes );
else if( is_file( $path ) ) chmod( $path, $fileModes );
}
}
## Function to clean out the contents of specified directory
function cleandir($dir) {
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if ($file != '.' && $file != '..' && is_file($dir.'/'.$file)) {
if (unlink($dir.'/'.$file)) { }
else { echo $dir . '/' . $file . ' (file) NOT deleted!<br />'; }
}
else if ($file != '.' && $file != '..' && is_dir($dir.'/'.$file)) {
cleandir($dir.'/'.$file);
if (rmdir($dir.'/'.$file)) { }
else { echo $dir . '/' . $file . ' (directory) NOT deleted!<br />'; }
}
}
closedir($handle);
}
}
function isDirEmpty($dir){
return (($files = @scandir($dir)) && count($files) <= 2);
}
echo "----------------------- CLEANUP START -------------------------<br/>";
$start = (float) array_sum(explode(' ',microtime()));
echo "<br/>*************** SETTING PERMISSIONS ***************<br/>";
echo "Setting all folder permissions to 755<br/>";
echo "Setting all file permissions to 644<br/>";
AllDirChmod( "." );
echo "Setting pear permissions to 550<br/>";
chmod("pear", 550);
echo "<br/>****************** CLEARING CACHE ******************<br/>";
if (file_exists("var/cache")) {
echo "Clearing var/cache<br/>";
cleandir("var/cache");
}
if (file_exists("var/session")) {
echo "Clearing var/session<br/>";
cleandir("var/session");
}
if (file_exists("var/minifycache")) {
echo "Clearing var/minifycache<br/>";
cleandir("var/minifycache");
}
if (file_exists("downloader/pearlib/cache")) {
echo "Clearing downloader/pearlib/cache<br/>";
cleandir("downloader/pearlib/cache");
}
if (file_exists("downloader/pearlib/download")) {
echo "Clearing downloader/pearlib/download<br/>";
cleandir("downloader/pearlib/download");
}
if (file_exists("downloader/pearlib/pear.ini")) {
echo "Removing downloader/pearlib/pear.ini<br/>";
unlink ("downloader/pearlib/pear.ini");
}
echo "<br/>************** CHECKING FOR EXTENSIONS ***********<br/>";
If (!isDirEmpty("app/code/local/")) {
echo "-= WARNING =- Overrides or extensions exist in the app/code/local folder<br/>";
}
If (!isDirEmpty("app/code/community/")) {
echo "-= WARNING =- Overrides or extensions exist in the app/code/community folder<br/>";
}
$end = (float) array_sum(explode(' ',microtime()));
echo "<br/>------------------- CLEANUP COMPLETED in:". sprintf("%.4f", ($end-$start))." seconds ------------------<br/>";
?>
。导致问题的查询是
JdbcDaoImpl
这用作默认查询,以通过public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
"select username,authority " +
"from authorities " +
"where username = ?";
内的userName加载用户的所有权限。因此,如果您仍想使用默认JdbcDaoImpl
- 您可以设置自定义查询,该查询将使用您的表作为其参数。
我不确定你的用户表的架构是什么,但是这样的东西应该是类似的(如果你是以编程方式配置JdbcDaoImpl bean):
JdbcDaoImpl
或者如果您从XML创建JdbcDaoImpl:
String query = "select username,authority " +
"from authorities join users on users.id = authorities.user_id " +
"where users.username = ?";
jdbcDaoImpl.setAuthoritiesByUsernameQuery(query);
您可能希望更改某些其他默认查询以适合您的架构,请在<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>
中查看它们。
您可能还会考虑编写自己的JdbcDaoImpl
实现的可能性,如果它开始与默认UserDetailsService
的距离太远。