[SPRING] Spring 부트 2로 마이그레이션 - 보안 인코딩 된 암호가 BCrypt처럼 보이지 않음
SPRINGSpring 부트 2로 마이그레이션 - 보안 인코딩 된 암호가 BCrypt처럼 보이지 않음
암호 저장을 위해 BCrypt를 사용하는 스프링 부트 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.sc.bcrypt.BCryptPasswordEncoder : 암호화된 암호가 BCrypt처럼 보이지 않습니다.
이 응용 프로그램 조각은 이전에 잘 작동했습니다. 내가 만든 유일한 변경 사항은 build.gradle 파일 (springBootVersion 변경, io.spring.dependency 관리 플러그인 추가 및 런타임 추가 ( '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 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
해결법
-
==============================
1.clientSecret 암호를 인코딩하여 해결했습니다.
clientSecret 암호를 인코딩하여 해결했습니다.
@Override public void configure(ClientDetailsServiceConfigurer configurer) throws Exception { configurer .inMemory() .withClient(clientId) .secret(encode(clientSecret)) .authorizedGrantTypes(grantType) .scopes(scopeRead, scopeWrite) .resourceIds(resourceIds); }
-
==============================
2.oauth2 의존성이 클라우드로 옮겨지면서 나는이 문제에 직면했다. 이전에는 보안 프레임 워크의 일부였습니다.
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")); }
희망이 당신을 도울 것입니다.
from https://stackoverflow.com/questions/49197111/migration-to-spring-boot-2-security-encoded-password-does-not-look-like-bcrypt by cc-by-sa and MIT license