web-dev-qa-db-ja.com

Spring Boot 2への移行-セキュリティでエンコードされたパスワードはBCryptのように見えません

パスワードの保存にBCryptを使用するSpring Boot 1.5.9認証サーバーがありました。 2.0に移行しようとしていますが、承認のためのトークンを取得できなくなりました。

サーバーからの応答は次のとおりです。

_    "timestamp": "2018-03-09T15:22:06.576+0000",
    "status": 401,
    "error": "Unauthorized",
    "message": "Unauthorized",
    "path": "/oauth/token"
}
_

コンソールが以下を出力します:_2018-03-09 09:22:06.553 WARN 20976 --- [nio-8090-exec-1] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt_。

アプリケーションのこの部分は、以前は正常に機能していました。私が行った唯一の変更は、build.gradleファイルへの変更でした(springBootVersionの変更、_io.spring.dependency-management_プラグインの追加、およびruntime('org.springframework.boot:spring-boot-devtools')の追加。

_buildscript {
    ext {
        springBootVersion = '2.0.0.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'Java'
apply plugin: 'Eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.midamcorp'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}



dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-rest')
    compile('org.springframework.boot:spring-boot-starter-jdbc')
        compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('commons-io:commons-io:2.5')    
    compile('org.springframework.security:spring-security-jwt:1.0.7.RELEASE')
    compile('org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE')
    compile 'com.Microsoft.sqlserver:mssql-jdbc:6.2.2.jre8'   
    runtime('org.springframework.boot:spring-boot-devtools')

    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile('org.springframework.security:spring-security-test')
testCompile('com.h2database:h2:1.4.196')

}
_

パスワードをハッシュするロジックは、2つの個別の構成ファイルにあります。

_package com.midamcorp.auth_server.config;

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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.midamcorp.auth_server.service.OAuthUserDetailsService;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private OAuthUserDetailsService userService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
}

    // Hash password
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService)
        .passwordEncoder(passwordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http
               .sessionManagement()
               .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
               .and()
               .httpBasic()
               .realmName("test")
               .and()
               .csrf()
               .disable();

    }
}
_

そして

_package com.midamcorp.auth_server.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;


// Contains properties common to both authorization and resource servers
@Configuration
public class AppConfig {


        @Value("${spring.datasource.url}")
        private String datasourceUrl;

        @Value("${spring.datasource.driverClassName}")
        private String dbDriverClassName;

        @Value("${spring.datasource.username}")
        private String dbUsername;

        @Value("${spring.datasource.password}")
        private String dbPassword;

        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }

        @Bean
        public DataSource dataSource() {
            final DriverManagerDataSource dataSource = new DriverManagerDataSource();

            dataSource.setDriverClassName(dbDriverClassName);
            dataSource.setUrl(datasourceUrl);
            dataSource.setUsername(dbUsername);
            dataSource.setPassword(dbPassword);

            return dataSource;
        }    

        // Refrence: http://www.baeldung.com/spring-security-oauth-jwt

        /* !!!!!!!!!!!!!!!!!!!!!!!!!! 
        ** TODO 
        * Secure key file for deployment.
        !!!!!!!!!!!!!!!!!!!! */
           @Bean
           public JwtAccessTokenConverter accessTokenConverter() {
              JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
              KeyStoreKeyFactory keyStoreKeyFactory = 
                      new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"), "mypass".toCharArray());
                    converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));
              return converter;
           }


           @Bean
           public TokenStore tokenStore() {
              return new JwtTokenStore(accessTokenConverter());
           }


}
_

OAuthUserクラス:

_    package com.midamcorp.auth_server.model;


import Java.util.List;

import javax.persistence.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.fasterxml.jackson.annotation.JsonIgnore;


@Entity
@Table(name="auth_user")
public class OAuthUser {

//    @Autowired 
//    @Transient
//    private PasswordEncoder passwordEncoder;
//    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name= "username")
    private String userName;

    @Column(name="password")
    @JsonIgnore
    private String password;

    @Column(name="first_name")
    private String firstName;

    @Column(name="last_name")
    private String lastName;

    @Column(name="email")
    private String email;

    @Column(name="is_enabled")
    private boolean isEnabled;

     /**
      * Reference: https://github.com/nydiarra/springboot-jwt/blob/master/src/main/Java/com/nouhoun/springboot/jwt/integration/domain/User.Java
     * Roles are being eagerly loaded here because
     * they are a fairly small collection of items for this example.
     */
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "user_role", joinColumns
            = @JoinColumn(name = "user_id",
            referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "role_id",
                    referencedColumnName = "id"))
private List<Role> roles;


    public OAuthUser() {};
    public OAuthUser(String firstName, String lastName, String user, String pass) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.userName = user;
        this.password = pass;
    }
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public boolean isEnabled() {
        return isEnabled;
    }

    public void setEnabled(boolean isEnabled) {
        this.isEnabled = isEnabled;
    }
}
_

Spring Securityに重大な変更が加えられたことは理解していますが、この問題の解決に取り組むかどうかはわかりません。任意のガイダンスをいただければ幸いです。

ありがとう。

編集

役立つ場合に備えて、さらに詳細を説明します。 Spring Boot 2.0の実行中に新しいユーザーを追加しても:

_OAuthUser user = new OAuthUser();   

            user.setFirstName("K");
            user.setLastName("M");
            user.setPassword(passwordEncoder.encode("L"));
            user.setUserName("KLM");

repository.save(user);
_

それと新しいユーザー名とパスワードを使用してリクエストを行うと、エラーが表示されます。

2つの編集:

エラーの結果をリクエストします。

_curl --request POST \
  --url http://web:secret@localhost:8090/oauth/token \
  --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \
  --form grant_type=password \
  --form username=KLM \
  --form password=L
_

認可サーバーの構成:

_    package com.midamcorp.auth_server.config;

import Java.util.Arrays;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
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.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
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;

// Reference: https://dazito.com/Java/spring-boot-and-oauth2-with-jdbc

@EnableAuthorizationServer
@Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter{
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private AccessTokenConverter converter;

     private final AppConfig appConfig; 

    private AuthenticationManager authenticationManager;

    @Autowired
    public AuthServerConfig(AuthenticationManager authenticationManager, AppConfig appConfig) {
        this.authenticationManager = authenticationManager;
        this.appConfig = appConfig;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.checkTokenAccess("permitAll()");
        security.tokenKeyAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {

    JdbcClientDetailsService details = new JdbcClientDetailsService(appConfig.dataSource());

        configurer.jdbc(appConfig.dataSource());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        endpoints.tokenStore(tokenStore)
                .accessTokenConverter(converter)
            .authenticationManager(authenticationManager);
    }



       @Bean
       @Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
       public DefaultTokenServices tokenServices() {
          DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
          defaultTokenServices.setTokenStore(tokenStore);
          defaultTokenServices.setSupportRefreshToken(true);
          return defaultTokenServices;
       }       



}
_

次のプロパティを使用しています。

_ spring.datasource.url=jdbc:sqlserver://localhost;databaseName=API
spring.datasource.username=**
spring.datasource.password=**
spring.datasource.driverClassName=com.Microsoft.sqlserver.jdbc.SQLServerDriver
server.port=8090
_
10
KellyM

ClientSecretパスワードをエンコードして解決しました

@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
    configurer
            .inMemory()
            .withClient(clientId)
            .secret(encode(clientSecret))
            .authorizedGrantTypes(grantType)
            .scopes(scopeRead, scopeWrite)
            .resourceIds(resourceIds);
}
2
Andres

Oauth2の依存関係がクラウドに移行したとき、私はこの問題に直面し始めました。以前はセキュリティフレームワークの一部でした:

<dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId></dependency>

これはクラウドフレームワークの一部です。

<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>

したがって、クラウドの依存関係(Finchley.RELEASE)を使用している場合、以下のように秘密をエンコードする必要があります。

@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(passwordEncoder.encode("SECRET"));
    }

これがお役に立てば幸いです。

1
PKumar