在Spring Security OAuth2

时间:2015-07-28 17:22:34

标签: java spring spring-security spring-security-oauth2

我正在使用Spring Security OAuth2 2.0.7.RELEASE。因为我使用ORM连接到我的数据库,默认JdbcUserDetailsManager使用jdbc我想实现我自己的UserDetailsS​​ervice,这是

@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    |                |
+--------------+---------------------+------+-----+---------+----------------+

然后我正在注入我的自定义userDetailsS​​ervice:

@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。事实上,当我开始调试时,我发现遵循以下步骤:

  1. 验证客户端(确定,因为我没有修改oauth_client_details表)
  2. 使用我的自定义userDetailsS​​ervice验证用户(确定,用户表已修改,但我的自定义userDetailsS​​ervice支持更改)
  3. 使用默认userDetailsS​​ervice(ERROR)
  4. 对用户进行身份验证

    我不知道为什么会这样。这对我来说听起来像个错误。 你觉得有什么不对吗?

3 个答案:

答案 0 :(得分:3)

您正在使用auth.jdbcAuthentication().dataSource(this.dataSource).and().userDetailsService(‌​this.userService);// Inject custom我在这里创建了两个身份验证管理器 - 一个默认JdbcDaoImpldataSource指向this.dataSource,另一个指定您自定义userService。尝试只放auth.userDetailsService(this.userService)(我希望userService已经在内部自动装配了jdbc)。

这里的要点是.and()用于向身份验证管理器添加不同的身份验证配置,而不是配置jdbcAuthentication()

答案 1 :(得分:1)

2.0.7 POST/GET /oauth/token {}}} {}}} {} {}} password {}} {} {{}} }}

我有类似的问题,这就是我解决它的方法:

ClientDetailsUserDetailsService

UserDetailsServicepublic 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的距离太远。