본문 바로가기
프로그래밍/Spring Boot

[Spring Boot Security OAuth 2.0] 원모 싸이버 스쿨 인증서버

by yonmoyonmo 2021. 7. 15.

원모 싸이버 스쿨 서비스 구성도

 

Spring Boot OAuth2 Social Login with Google, Facebook, and Github - Part 1

In this article, You'll learn how to add social as well as email and password based login to your spring boot application using Spring Security and Spring Security's OAuth2 client. You'll build a full stack application with Spring Boot and React containing

www.callicoder.com

이 분의 설명 글을 보고 참고하여 작업했습니다.

원모 싸이버 스쿨 인증 서버

간단 설명

원모 싸이버 인증 서버의 기능은 아래와 같습니다.

  • google OAuth 2.0 방식의 인증
  • 아이디 / 비밀번호 기반의 인증
  • 인증된 계정 정보를 담은 JWT 생성 후 인증 요청한 앱에 전달
  • 원모 싸이버 스쿨 유저 정보를 담은 데이터베이스를 따로 유지합니다.
  • 원모 싸이버 스쿨의 앱들은 유저 인증 요청을 이 앱으로 보내고 JWT를 받습니다.

OAuth 2.0에 대한 자료... 

https://d2.naver.com/helloworld/24942

https://showerbugs.github.io/2017-11-16/OAuth-란-무엇일까


코드 살피기

spring boot, spring security, jwt 등에 대한 설명은 없는 글입니다.
제가 구현한 것을 바탕으로 구현해야할 것들에 대한 언급 정도만 있는 글이므로...
........크게 도움이 안될 수도 있습니다!
 

yonmoyonmo/wcs-authentication

real auth app for wcs blog. Contribute to yonmoyonmo/wcs-authentication development by creating an account on GitHub.

github.com

원모 싸이버 인증 서버의 코드는 저의 깃허브 리포지토리에서 열람할 수 있습니다.

아래 같은 디펜던시가 필요합니다. spring boot security 랑 spring boot oauth2 client 입니다.

#### Maven ####
...다른 것은 생략...
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
...다른 것은 생략...

#### Gradle ####
## 본인은 메이븐을 이용하였음, 아래 디펜던시는 검증이 필요함 ##
dependencies {
	...다른 것은 생략...
	implementation: 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
	...다른 것은 생략...
}

그리고 jwt를 위한 디펜던시도 필요합니다.

#### maven ####
<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
</dependency>

#### gradle ####
dependencies {
	compile('org.springframework.boot:spring-boot-starter-web')
	compile('io.jsonwebtoken:jjwt-api:0.9.1')
    runtime('io.jsonwebtoken:jjwt-impl:0.9.1', 'io.jsonwebtoken:jjwt-jackson:0.9.1')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

버전은 상관 없습니다. 그냥 최신버전 쓰시면 될 것 같습니다.

application.properties 의 모습 -> 이 부분은 깃허브에 없으므로 자세히 설명했습니다.

sever.address=localhost
## (저는 운영서버 내부에서 localhost로 프로세스를 켜놓습니다 이후 리버스 프록시를 이용해 적절히 라우팅 해 주기만 합니다.)
server.port=당신의 포트
server.use-forward-headers=true

## mysql + jpa 관련 속성 ##
spring.datasource.url=jdbc:mysql://당신의IP:당신의MYSQL포트/당신의테이블이름?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=당신의이름
spring.datasource.password=당신의비밀번호

spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=false
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true

spring.security.oauth2.client.registration.google.clientId=당신의 구글 클라이언트 아이디
spring.security.oauth2.client.registration.google.clientSecret=당신의 구글 클라이언트 시크릿
spring.security.oauth2.client.registration.google.scope=email,profile

## default는 {baseUrl}/login/oauth2/code/google
## 이 속성은 OAuth2.0 과정에서 구글의 유저 인증 화면에서 인증 후 code를 담은 요청을 다시 보낼 곳입니다.
spring.security.oauth2.client.registration.google.redirectUri=당신의 인증서버 도메인/login/oauth2/code/google


## JWT 관련 속성
## java 코드로 이 속성 값을 가져와 사용합니다.
app.auth.tokenSecret=당신의 JWT 시크릿
app.auth.tokenExpirationMsec=당신의 토큰 유지 시간
## 인증서버에서 인증 성공 후 JWT를 넘겨줄 곳
app.oauth2.authorizedRedirectUris[0]=당신의 토큰 받을 페이지

## 프론트엔드 등에서 Google OAuth 2.0을 요청할 때 인증서버에 아래와 같이 GET으로 때리면 됩니다.
## 당신의 인증서버 도메인/oauth2/authorize/google?redirect_uri=당신의 토큰 받을 페이지(위에 적은거)

이 파일은 작성 후 이왕이면 GitHub에 public으로 올리지 않도록 합니다. 어디 잘 놔 두었다가 빌드할 때 가져와서 쓰면 됩니다.


AppProperties : JWT 관련 속성들 위에서 썼던 것, 자바 코드로 갖다 쓰기 위한 클래스

@ConfigurationProperties(prefix = "app") : application.properties에 app으로 시작하는 항목 갖다 쓸 수 있게 해주는 어노테이션

그 전에 스프링 부트 메인 어플리케이션 클래스에 @EnableConfigurationProperties 어노테이션을 적절히 달아 줍니다.

파라미터로 @ConfigurationProperties로 정의한 클래스를 받습니다.

@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class) //스프링부트 메인 클래스 이렇게 어노테이션을 달아줍니다.
public class AuthenticationServerApplication {
    public static void main(String[] args) {
    SpringApplication.run(AuthenticationServerApplication.class, args);
    }
}
package com.wonmocyberschool.authenticationserver.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.List;

//JWT, token redirect properties
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    private final Auth auth = new Auth();
    private final OAuth2 oauth2 = new OAuth2();

    public static class Auth {
        private String tokenSecret;
        private Long tokenExpirationMsec;

        public String getTokenSecret(){return tokenSecret;}
        public Long getTokenExpirationMsec(){return tokenExpirationMsec;}

        public void setTokenSecret(String tokenSecret) {
            this.tokenSecret = tokenSecret;
        }

        public void setTokenExpirationMsec(Long tokenExpirationMsec) {
            this.tokenExpirationMsec = tokenExpirationMsec;
        }
    }
    public static class OAuth2 {
        private List<String> authorizedRedirectUris = new ArrayList<>();
        public List<String> getAuthorizedRedirectUris(){
            return authorizedRedirectUris;
        }

        public void setAuthorizedRedirectUris(List<String> authorizedRedirectUris) {
            this.authorizedRedirectUris = authorizedRedirectUris;
        }
    }
    public Auth getAuth(){
        return auth;
    }
    public OAuth2 getOauth2(){
        return oauth2;
    }
}

SecurityConfig :

WebSecurityConfigurerAdapter를 상속하는 클래스를 하나 만들어서 이것저것 해야합니다.

Spring boot security를 사용할 때는 항상 이 클래스를 쓰게 되지요

공식문서 : https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html

 

WebSecurityConfigurerAdapter (spring-security-docs-manual 5.5.1 API)

Provides a convenient base class for creating a WebSecurityConfigurer instance. The implementation allows customization by overriding methods. Will automatically apply the result of looking up AbstractHttpConfigurer from SpringFactoriesLoader to allow deve

docs.spring.io

 

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

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private CustomOAuth2UserService customOAuth2UserService;

    @Autowired
    private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;

    @Autowired
    private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;

    @Autowired
    private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;

    @Bean
    public TokenAuthenticationFilter tokenAuthenticationFilter() {
        return new TokenAuthenticationFilter();
    }

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

    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .csrf()
                    .disable()
                .formLogin()
                    .disable()
                .httpBasic()
                    .disable()
                .exceptionHandling()
                    .authenticationEntryPoint(new RestAuthenticationEntryPoint())
                    .and()
                .authorizeRequests()
                    .antMatchers("/","/auth/**","/oauth2/**")
                    .permitAll()
                .anyRequest()
                    .authenticated()
                .and()
                .oauth2Login()
                    .authorizationEndpoint()
                        .baseUri("/oauth2/authorize")
                        .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
                        .and()
                .userInfoEndpoint()
                    .userService(customOAuth2UserService)
                    .and()
                .successHandler(oAuth2AuthenticationSuccessHandler)
                .failureHandler(oAuth2AuthenticationFailureHandler);
        http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

설명 : 이 클래스는 아래에 적힌 것들을 갖다가 configure(HttpSecurity)메소드에 체이닝하여 spring security가 작동할 수 있도록 해주는 설정 클래스입니다.

  • 기본적인 아이디/패스워드 인증을 위해 UserDetailsService를 구현한 클래스와 비밀번호 암호화에 이용되는 passwordEncoder
  • OAuth2 인증과 유저등록을 위해 DefaultOAuth2UserService를 구현한 클래스와 Oauth2 인증 성공, 실패 핸들러
  • 기본 인증방식과 OAuth2 인증 방식이 성공했을 때 JWT를 만들어 보낼 수 있도록 구현된 JWT 관련 유틸
  • 세션이 아닌 쿠키에 OAuth2에 필요한 정보를 저장해서 쓸 수 있도록 AuthorizationRequestRepository<OAuth2AuthorizationRequest> 를 구현한 쿠키 유틸

나머지 파일들은 이 동작을 위한 부품이 되는 것이지요!

이제 나머지는 구현할 Spring Security 인터페이스에 관한 문서를 찾아 보면서 그냥 꾸역꾸역 코딩하시면 완성됩니다.

꾸역꾸역 작업의 예 )

UserDetailsService가 뭐지? -> 공식문서를 찾아 봄 ->

https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/core/userdetails/UserDetailsService.html

 

UserDetailsService (Spring Security 4.0.4.RELEASE API)

Core interface which loads user-specific data. It is used throughout the framework as a user DAO and is the strategy used by the DaoAuthenticationProvider. The interface requires only one read-only method, which simplifies support for new data-access strat

docs.spring.io

-> 아 대충 이런건가? -> 작업 -> 잘 안되면 구글링 -> 될 때까지 작업  -> 완성


방울이 떼어지는 가혹한 탈락

 

댓글