저는 회사에서 프로젝트를 진행하면서 통합회원연동을 맡아 클라이언트단을 작업했었는데 그때 통합회원 서버쪽에서 사용한 것이 OAuth2이고 인증 서버도 만들어 보면 좋겠다 라고 생각해서 만들게 되었습니다.
OAuth Authorization은 클라이언트가 서비스 제공자로부터 회원정보를 제공받기 위해 인증 및 권한을 부여받는 절차라고 생각하면 됩니다.
예를들면 카카오
, 구글
, 페이스북
등이 대표적인 서비스 제공자입니다.
authorization 서버의 인증 타입은 아래와 같이 4가지가 있습니다.
Authorization Code Grant Type
,Implicit Grant Type
, Resource Owner Password Credential Grant Type
, Client Credential Grant Type
이 중 대중적으로 이용되는 Authorization Code Grant Type
을 사용하여 만들어 보도록 하겠습니다.
plugins {
id 'org.springframework.boot' version '2.6.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'war'
}
group = 'com.mycloset'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.security:spring-security-jwt'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: '2.3.5.RELEASE'
implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.5.2'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.google.code.gson:gson'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor('org.projectlombok:lombok')
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
test {
useJUnitPlatform()
}
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception{
clients.inMemory()
.withClient("foo")
.secret("bar")
.redirectUris("http://localhost:1995/oauth2/callback")
.authorizedGrantTypes("authorization_code")
.scopes("read", "write")
.accessTokenValiditySeconds(30000);
}
}
@Configuration
@EnableAuthorizationServer
어노테이션을 사용하여 인증서버를 활성화 시킵니다.
인증이 완료되면 redirectUris에 명시된 Url로 Code값을 실어서 보내줍니다.
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("{noop}pass")
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.headers().frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers(
"/oauth/**",
"/oauth2/callback",
"/h2/**"
).permitAll()
.and()
.formLogin().and()
.httpBasic();
}
}
아직 로그인 폼이 준비되어있지 않으므로 시큐리티 기본 로그인 폼을 사용합니다.
추후엔 암호화를 하여 저장하지만 현재는 암호화준비가 아직 안되었으므로 NoOpPasswordEncoder를 사용합니다.
Security버전이 높아지면서 PasswordEncoder 사용시 발생되는 에러를 막기위해 {noop}를 앞에 추가해줍니다.
csrf 는 사용안함 처리
headers().frameOptions().disable() 은 시큐리티 사용시 H2콘솔에 접근이 막히므로 세팅합니다.
oauth/**
, /oauth2/callback
, /h2/**
이 3개는 인증없이 접근 가능하도록 변경합니다.
server.port=1995
spring.h2.console.enabled=true
spring.h2.console.path=/h2
#DB
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:oauth;MODE=MySQL;
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.initialization-mode=always
spring.datasource.schema=classpath:h2/schema.sql
spring.datasource.data=classpath:h2/data.sql
#JPA
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type=trace
H2, JPA 설정이 담겨있습니다.