Skip to content

juhee200/oauth2-demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

7 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ” OAuth2 Demo Project

This project is an OAuth2 social login demo application built with Spring Boot. It supports Google, Kakao, and Naver social login with JWT-based authentication system.

๐Ÿš€ Key Features

  • ๐Ÿ”‘ Social Login (Google, Kakao, Naver)
  • ๐ŸŽซ JWT-based Authentication (Access Token, Refresh Token)
  • ๐Ÿ”’ Security Configuration (CORS, CSRF)
  • ๐Ÿช Cookie-based Token Management
  • ๐Ÿ“ User Terms Agreement Process

๐Ÿ›  Tech Stack

Java 21 Spring Boot 3.4.5 Spring Security Spring OAuth2 Client JWT (jjwt 0.12.3) MySQL Redis QueryDSL Lombok

๐Ÿ” OAuth2 & Security Implementation Details

OAuth2 Implementation

  1. Social Login Configuration Configure each social login provider (Google, Kakao, Naver) using Spring Security OAuth2 Client Set required scope and user-info-uri for each provider Handle social login user information through CustomOAuth2UserService
  2. Authentication Process Handle OAuth2 authentication requests through CustomAuthorizationRequestResolver Issue JWT tokens on authentication success via OAuth2AuthenticationSuccessHandler Handle authentication failures through OAuth2AuthenticationFailureHandler
  3. Token Management Implement JWT-based Access Token and Refresh Token Store and manage Refresh Tokens using Redis Secure token transmission through HttpOnly cookies

Security Implementation

  1. Security Configuration
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) {
        return http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf(AbstractHttpConfigurer::disable)
            .formLogin(AbstractHttpConfigurer::disable)
            .sessionManagement(session -> 
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/private/**").authenticated()
                .anyRequest().denyAll())
            .oauth2Login(oauth2 -> oauth2
                .authorizationEndpoint(endpoint -> endpoint
                    .authorizationRequestResolver(customAuthorizationRequestResolver))
                .userInfoEndpoint(userInfo -> userInfo
                    .userService(customOAuth2UserService))
                .successHandler(oAuth2AuthenticationSuccessHandler)
                .failureHandler(oAuth2AuthenticationFailureHandler))
            .build();
    }
}
  1. Authentication Filters LoginAuthFilter: JWT token validation and authentication handling LogoutAuthFilter: Logout processing and token removal
  2. CORS Configuration
@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("*"));
    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
    configuration.setAllowedHeaders(Arrays.asList("*"));
    configuration.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", configuration);
    return source;
}
  1. Security Enhancements Stateless session management for reduced server load CSRF protection disabled (REST API based) XSS attack prevention through HttpOnly cookies Enhanced security through Redis-based token management

โš™๏ธ Environment Configuration

Required Environment Variables properties

OAuth2 Client Credentials

GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
KAKAO_CLIENT_ID=your_kakao_client_id
KAKAO_CLIENT_SECRET=your_kakao_client_secret
NAVER_CLIENT_ID=your_naver_client_id
NAVER_CLIENT_SECRET=your_naver_client_secret

JWT Keys

ACCESS_TOKEN_KEY=your_access_token_key
REFRESH_TOKEN_KEY=your_refresh_token_key

Database

MYSQL_URL=jdbc:mysql://localhost:3306/oauth2_demo
MYSQL_USER_NAME=your_mysql_username
MYSQL_PASSWORD=your_mysql_password

๐Ÿ— Project Structure

src/main/java/com/example/oauth2demo/
โ”œโ”€โ”€ auth/                 # Authentication related code
โ”‚   โ”œโ”€โ”€ application/     # Authentication service logic
โ”‚   โ”œโ”€โ”€ domain/         # Domain models
โ”‚   โ”œโ”€โ”€ dto/            # Data transfer objects
โ”‚   โ””โ”€โ”€ infrastructure/ # Infrastructure related code
โ”œโ”€โ”€ common/             # Common configuration and utilities
โ”œโ”€โ”€ terms/             # Terms related code
โ””โ”€โ”€ user/              # User related code

๐Ÿ”„ Authentication Process

  1. Social Login Process Client requests social login (/api/public/oauth2/{provider}) Redirect to OAuth2 authentication page User authenticates with social login provider Redirect to callback URL on successful authentication Server obtains and processes user information Issue JWT tokens and store in cookies Redirect to main page
  2. User Registration Process Check for new user after successful social login Redirect to terms agreement page for new users Save user information: Social account information (email, social ID, provider) User role (ROLE_USER) Save terms agreement information Redirect to main page after registration completion
  3. Logout Process
  • Client requests logout (/api/public/logout)
  • LogoutAuthFilter processes the request
  • Delete Refresh Token from Redis
  • Delete Access Token and Refresh Token cookies:
Cookie deletedAccessCookie = cookieProvider.generateDeletedAccessTokenCookie();
Cookie deletedRefreshCookie = cookieProvider.generateDeletedRefreshTokenCookie();
response.addCookie(deletedAccessCookie);
response.addCookie(deletedRefreshCookie);
  • Clear SecurityContext
  • Redirect to login page
  1. Token Refresh Process When Access Token expires (30 minutes) Request new Access Token with Refresh Token Validate Refresh Token in Redis Issue new Access Token if valid Store new Access Token in cookie Maintain existing Refresh Token (2 days)

๐Ÿ”’ Security Settings

CORS Configuration: Allow cross-origin requests for API endpoints CSRF Protection: Disabled for REST API based architecture Session Management: Uses stateless approach Token Management: Uses HttpOnly cookies

๐Ÿงช Testing

./gradlew test

๐Ÿš€ How to Run

Set environment variables Start MySQL and Redis servers Run the application:

./gradlew bootRun

๐Ÿ“ API Documentation

Swagger UI: http://localhost:8080/api/swagger-ui/index.html OpenAPI Documentation: http://localhost:8080/api/api-docs

๐Ÿ”‘ Token Expiration Times

Access Token: 30 minutes Refresh Token: 2 days

๐Ÿ“‹ Prerequisites

Java 21 or higher MySQL 8.0 or higher Redis 6.0 or higher Gradle 7.0 or higher

๐Ÿค Contributing

Fork the repository Create your feature branch (git checkout -b feature/AmazingFeature) Commit your changes (git commit -m 'Add some AmazingFeature') Push to the branch (git push origin feature/AmazingFeature) Open a Pull Request

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ“ž Contact

Author: Your Name Email: your.email@example.com Project Link: https://github.com/yourusername/oauth2-demo


๐Ÿ” OAuth2 Demo Project

์ด ํ”„๋กœ์ ํŠธ๋Š” Spring Boot๋ฅผ ์‚ฌ์šฉํ•œ OAuth2 ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฐ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. Google, Kakao, Naver ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ์ง€์›ํ•˜๋ฉฐ, JWT ๊ธฐ๋ฐ˜์˜ ์ธ์ฆ ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿš€ ์ฃผ์š” ๊ธฐ๋Šฅ

  • ๐Ÿ”‘ ์†Œ์…œ ๋กœ๊ทธ์ธ (Google, Kakao, Naver)
  • ๐ŸŽซ JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ (Access Token, Refresh Token)
  • ๐Ÿ”’ ๋ณด์•ˆ ์„ค์ • (CORS, CSRF)
  • ๐Ÿช ์ฟ ํ‚ค ๊ธฐ๋ฐ˜ ํ† ํฐ ๊ด€๋ฆฌ
  • ๐Ÿ“ ์‚ฌ์šฉ์ž ์•ฝ๊ด€ ๋™์˜ ํ”„๋กœ์„ธ์Šค

๐Ÿ›  ๊ธฐ์ˆ  ์Šคํƒ

  • Java 21
  • Spring Boot 3.4.5
  • Spring Security
  • Spring OAuth2 Client
  • JWT (jjwt 0.12.3)
  • MySQL
  • Redis
  • QueryDSL
  • Lombok

๐Ÿ” OAuth2 & Security ๊ตฌํ˜„ ์ƒ์„ธ

OAuth2 ๊ตฌํ˜„ ๋ฐฉ์‹

  1. ์†Œ์…œ ๋กœ๊ทธ์ธ ์„ค์ •

    • Spring Security OAuth2 Client๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์†Œ์…œ ๋กœ๊ทธ์ธ ์ œ๊ณต์ž(Google, Kakao, Naver) ์„ค์ •
    • ๊ฐ ์ œ๊ณต์ž๋ณ„๋กœ ํ•„์š”ํ•œ scope์™€ user-info-uri ์„ค์ •
    • CustomOAuth2UserService๋ฅผ ํ†ตํ•ด ์†Œ์…œ ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž ์ •๋ณด ์ฒ˜๋ฆฌ
  2. ์ธ์ฆ ํ”„๋กœ์„ธ์Šค

    • CustomAuthorizationRequestResolver๋ฅผ ํ†ตํ•œ OAuth2 ์ธ์ฆ ์š”์ฒญ ์ฒ˜๋ฆฌ
    • OAuth2AuthenticationSuccessHandler์—์„œ ์ธ์ฆ ์„ฑ๊ณต ์‹œ JWT ํ† ํฐ ๋ฐœ๊ธ‰
    • OAuth2AuthenticationFailureHandler์—์„œ ์ธ์ฆ ์‹คํŒจ ์ฒ˜๋ฆฌ
  3. ํ† ํฐ ๊ด€๋ฆฌ

    • JWT ๊ธฐ๋ฐ˜์˜ Access Token๊ณผ Refresh Token ๊ตฌํ˜„
    • Redis๋ฅผ ์‚ฌ์šฉํ•œ Refresh Token ์ €์žฅ ๋ฐ ๊ด€๋ฆฌ
    • HttpOnly ์ฟ ํ‚ค๋ฅผ ํ†ตํ•œ ์•ˆ์ „ํ•œ ํ† ํฐ ์ „์†ก

Security ๊ตฌํ˜„ ๋ฐฉ์‹

  1. ๋ณด์•ˆ ์„ค์ •

    @Configuration
    public class SecurityConfig {
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) {
            return http
                .cors(cors -> cors.configurationSource(corsConfigurationSource()))
                .csrf(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .sessionManagement(session -> 
                    session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> authorize
                    .requestMatchers("/public/**").permitAll()
                    .requestMatchers("/private/**").authenticated()
                    .anyRequest().denyAll())
                .oauth2Login(oauth2 -> oauth2
                    .authorizationEndpoint(endpoint -> endpoint
                        .authorizationRequestResolver(customAuthorizationRequestResolver))
                    .userInfoEndpoint(userInfo -> userInfo
                        .userService(customOAuth2UserService))
                    .successHandler(oAuth2AuthenticationSuccessHandler)
                    .failureHandler(oAuth2AuthenticationFailureHandler))
                .build();
        }
    }
  2. ์ธ์ฆ ํ•„ํ„ฐ

    • LoginAuthFilter: JWT ํ† ํฐ ๊ฒ€์ฆ ๋ฐ ์ธ์ฆ ์ฒ˜๋ฆฌ
    • LogoutAuthFilter: ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ ๋ฐ ํ† ํฐ ์‚ญ์ œ
  3. CORS ์„ค์ •

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", configuration);
        return source;
    }
  4. ๋ณด์•ˆ ๊ฐ•ํ™”

    • Stateless ์„ธ์…˜ ๊ด€๋ฆฌ๋กœ ์„œ๋ฒ„ ๋ถ€ํ•˜ ๊ฐ์†Œ
    • CSRF ๋ณดํ˜ธ ๋น„ํ™œ์„ฑํ™” (REST API ๊ธฐ๋ฐ˜)
    • HttpOnly ์ฟ ํ‚ค๋ฅผ ํ†ตํ•œ XSS ๊ณต๊ฒฉ ๋ฐฉ์ง€
    • Redis๋ฅผ ์‚ฌ์šฉํ•œ ํ† ํฐ ๊ด€๋ฆฌ๋กœ ๋ณด์•ˆ์„ฑ ๊ฐ•ํ™”

โš™๏ธ ํ™˜๊ฒฝ ์„ค์ •

ํ•„์ˆ˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜

# OAuth2 Client Credentials
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
KAKAO_CLIENT_ID=your_kakao_client_id
KAKAO_CLIENT_SECRET=your_kakao_client_secret
NAVER_CLIENT_ID=your_naver_client_id
NAVER_CLIENT_SECRET=your_naver_client_secret

# JWT Keys
ACCESS_TOKEN_KEY=your_access_token_key
REFRESH_TOKEN_KEY=your_refresh_token_key

# Database
MYSQL_URL=jdbc:mysql://localhost:3306/oauth2_demo
MYSQL_USER_NAME=your_mysql_username
MYSQL_PASSWORD=your_mysql_password

๐Ÿ— ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

src/main/java/com/example/oauth2demo/
โ”œโ”€โ”€ auth/                 # ์ธ์ฆ ๊ด€๋ จ ์ฝ”๋“œ
โ”‚   โ”œโ”€โ”€ application/     # ์ธ์ฆ ์„œ๋น„์Šค ๋กœ์ง
โ”‚   โ”œโ”€โ”€ domain/         # ๋„๋ฉ”์ธ ๋ชจ๋ธ
โ”‚   โ”œโ”€โ”€ dto/            # ๋ฐ์ดํ„ฐ ์ „์†ก ๊ฐ์ฒด
โ”‚   โ””โ”€โ”€ infrastructure/ # ์ธํ”„๋ผ ๊ด€๋ จ ์ฝ”๋“œ
โ”œโ”€โ”€ common/             # ๊ณตํ†ต ์„ค์ • ๋ฐ ์œ ํ‹ธ๋ฆฌํ‹ฐ
โ”œโ”€โ”€ terms/             # ์•ฝ๊ด€ ๊ด€๋ จ ์ฝ”๋“œ
โ””โ”€โ”€ user/              # ์‚ฌ์šฉ์ž ๊ด€๋ จ ์ฝ”๋“œ

๐Ÿ”„ ์ธ์ฆ ํ”„๋กœ์„ธ์Šค

1. ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ณผ์ •

  1. ํด๋ผ์ด์–ธํŠธ์—์„œ ์†Œ์…œ ๋กœ๊ทธ์ธ ์š”์ฒญ (/api/public/oauth2/{provider})
  2. OAuth2 ์ธ์ฆ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  3. ์‚ฌ์šฉ์ž๊ฐ€ ์†Œ์…œ ๋กœ๊ทธ์ธ ์ œ๊ณต์ž์—์„œ ์ธ์ฆ
  4. ์ธ์ฆ ์„ฑ๊ณต ์‹œ ์ฝœ๋ฐฑ URL๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  5. ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด ํš๋“ ๋ฐ ์ฒ˜๋ฆฌ
  6. JWT ํ† ํฐ ๋ฐœ๊ธ‰ ๋ฐ ์ฟ ํ‚ค์— ์ €์žฅ
  7. ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ

2. ํšŒ์›๊ฐ€์ž… ๊ณผ์ •

  1. ์†Œ์…œ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ํ›„ ์‹ ๊ทœ ์‚ฌ์šฉ์ž ํ™•์ธ
  2. ์‹ ๊ทœ ์‚ฌ์šฉ์ž์ธ ๊ฒฝ์šฐ ์•ฝ๊ด€ ๋™์˜ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  3. ์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ
    • ์†Œ์…œ ๊ณ„์ • ์ •๋ณด (์ด๋ฉ”์ผ, ์†Œ์…œ ID, ์ œ๊ณต์ž)
    • ์‚ฌ์šฉ์ž ์—ญํ•  (ROLE_USER)
  4. ์•ฝ๊ด€ ๋™์˜ ์ •๋ณด ์ €์žฅ
  5. ํšŒ์›๊ฐ€์ž… ์™„๋ฃŒ ํ›„ ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ

3. ๋กœ๊ทธ์•„์›ƒ ๊ณผ์ •

  1. ํด๋ผ์ด์–ธํŠธ์—์„œ ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ (/api/public/logout)
  2. LogoutAuthFilter์—์„œ ์š”์ฒญ ์ฒ˜๋ฆฌ
  3. Redis์—์„œ Refresh Token ์‚ญ์ œ
  4. Access Token๊ณผ Refresh Token ์ฟ ํ‚ค ์‚ญ์ œ
    Cookie deletedAccessCookie = cookieProvider.generateDeletedAccessTokenCookie();
    Cookie deletedRefreshCookie = cookieProvider.generateDeletedRefreshTokenCookie();
    response.addCookie(deletedAccessCookie);
    response.addCookie(deletedRefreshCookie);
  5. SecurityContext ์ดˆ๊ธฐํ™”
  6. ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ

4. ํ† ํฐ ๊ฐฑ์‹  ๊ณผ์ •

  1. Access Token ๋งŒ๋ฃŒ ์‹œ (30๋ถ„)
  2. Refresh Token์œผ๋กœ ์ƒˆ๋กœ์šด Access Token ์š”์ฒญ
  3. Redis์—์„œ Refresh Token ์œ ํšจ์„ฑ ๊ฒ€์ฆ
  4. ์œ ํšจํ•œ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด Access Token ๋ฐœ๊ธ‰
  5. ์ƒˆ๋กœ์šด Access Token์„ ์ฟ ํ‚ค์— ์ €์žฅ
  6. ๊ธฐ์กด Refresh Token ์œ ์ง€ (2์ผ)

๐Ÿ”’ ๋ณด์•ˆ ์„ค์ •

  • CORS ์„ค์ •: API ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ ํฌ๋กœ์Šค ์˜ค๋ฆฌ์ง„ ์š”์ฒญ ํ—ˆ์šฉ
  • CSRF ๋ณดํ˜ธ: REST API ๊ธฐ๋ฐ˜์œผ๋กœ CSRF ๋น„ํ™œ์„ฑํ™”
  • ์„ธ์…˜ ๊ด€๋ฆฌ: Stateless ๋ฐฉ์‹ ์‚ฌ์šฉ
  • ํ† ํฐ ๊ด€๋ฆฌ: HttpOnly ์ฟ ํ‚ค ์‚ฌ์šฉ

๐Ÿงช ํ…Œ์ŠคํŠธ

./gradlew test

๐Ÿš€ ์‹คํ–‰ ๋ฐฉ๋ฒ•

  1. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
  2. MySQL ๋ฐ Redis ์„œ๋ฒ„ ์‹คํ–‰
  3. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰:
./gradlew bootRun

๐Ÿ“ API ๋ฌธ์„œ

  • Swagger UI: http://localhost:8080/api/swagger-ui/index.html
  • OpenAPI ๋ฌธ์„œ: http://localhost:8080/api/api-docs

๐Ÿ”‘ ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„

  • Access Token: 30๋ถ„
  • Refresh Token: 2์ผ

About

oauth2 module

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages