迁移到Spring Boot 2 - 安全编码密码看起来不像BCrypt

时间:2018-03-09 15:33:06

标签: java spring spring-boot spring-security

我有一个Spring Boot 1.5.9授权服务器,它使用BCrypt进行密码存储。我正在尝试迁移到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')

}

在两个单独的配置文件中找到密码的逻辑:

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);

并使用新的用户名和密码发出请求,我仍然收到错误。

编辑二:

请求错误结果:

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

3 个答案:

答案 0 :(得分:2)

我解决了它编码clientSecret密码

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

答案 1 :(得分:1)

当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"));
    }

希望这会对您有所帮助。

答案 2 :(得分:0)

仅作记录用途可能对他人有所帮助。我遇到了类似的问题,我注意到

passwordEncoder.encode(rawPassword, hashedPassword)

输入参数的顺序很重要。第一个参数应该是rawPassword,第二个参数应该是hashedPassword。我遇到了这个问题,因为我已经互换了输入参数序列。