# [Security] OAuth2.0 ๋กœ๊ทธ์ธ ํ›„์ฒ˜๋ฆฌ - ๊ถŒํ•œ ๋ถ€์—ฌ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ
Study Repository

[Security] OAuth2.0 ๋กœ๊ทธ์ธ ํ›„์ฒ˜๋ฆฌ - ๊ถŒํ•œ ๋ถ€์—ฌ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ

by rlaehddnd0422

์ด ์ „ ํฌ์ŠคํŒ…์—์„œ OAuth2.0์„ ํ†ตํ•œ ๊ตฌ๊ธ€ ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ์ ์šฉํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‹จ์ˆœํžˆ ๋กœ๊ทธ์ธ๋งŒ ์„ฑ๊ณต ํ–ˆ์„ ๋ฟ ์•„์ง ๋‚จ์€ ๋ฌธ์ œ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. ์•„์ง ์†Œ์…œ ๋กœ๊ทธ์ธ์œผ๋กœ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—†์Šต๋‹ˆ๋‹ค.

โ–ถ๏ธŽ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์†Œ์…œ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ์ปค์Šคํ…€ํ•ด์„œ ์ €์žฅ ํ•„์š”

 

2. ์†Œ์…œ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์ ‘๊ทผ ๊ถŒํ•œ์ด ์ฃผ์–ด์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

โ–ถ๏ธŽ ์†Œ์…œ ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด ์ ‘๊ทผ ๊ถŒํ•œ ์„ค์ • (์ด ๋˜ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ ์‹œ Role ์„ค์ •์œผ๋กœ์จ ํ•ด๊ฒฐ)

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ฃผ์–ด์ง„ ์œ„์˜ ๋ฌธ์ œ๋“ค์„ Spring Security ๋‚ด๋ถ€์—์„œ ์ฒ˜๋ฆฌํ•ด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค!

 

์ผ๋ฐ˜ ๋กœ๊ทธ์ธ ๋กœ์ง์„ ๋‹ค์‹œ ํ•œ ๋ฒˆ ์ •๋ฆฌ. 

์ธ์ฆ ๊ด€๋ จ ์•„ํ‚คํ…์ณ Flow

1. Request โžก๏ธ AuthenticationFilter

: ํด๋ผ์ด์–ธํŠธ๊ฐ€ AuthenticationFilter์— ๋“ฑ๋ก๋œ ์š”์ฒญ์„ ํ•  ๊ฒฝ์šฐ Filter๊ฐ€ ์ด๋ฅผ ๋‚š์•„์ฑ„์„œ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

2. AuthenticationFilter โžก๏ธ UsernamePasswordAuthenticationToken

: Filter์—์„œ UsernamePasswordAuthenticationToken์—๊ฒŒ ์ธ์ฆ ์ „ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์ธ์ฆ์šฉ ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ํ•„ํ„ฐ์—๊ฒŒ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

 

3. AuthenticationFilter โžก๏ธ AuthenticationManager

: ๋ฆฌํ„ด ๋ฐ›์€ ์ธ์ฆ์šฉ ๊ฐ์ฒด๋ฅผ AuthenticationManager์—๊ฒŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

4. AuthenticationManager โžก๏ธ AuthenticationProvider

: AuthenticationManager์— ๋“ฑ๋ก๋œ AuthenticationProvider๋ฅผ ์ฐพ์•„์„œ ์ธ์ฆ์šฉ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

5. AuthenticationProvider โžก๏ธ UserDetailsService

: Provider์—์„œ UserDetailsService์—๊ฒŒ DB์— ์ ‘๊ทผํ•˜์—ฌ ์ธ์ฆ ์ ˆ์ฐจ๋ฅผ ID์™€ PASSWORD๋กœ ๊ฒ€์ฆํ•˜๋„๋ก ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค.

 

6. UserDetailsService โžก๏ธ UserDetails โžก๏ธ UserDetailsService 

: UserDetailsService๋Š” ์‹ค์ œ DB์— ์ ‘๊ทผํ•˜์—ฌ ํ•ด๋‹น ID์™€ PW๊ฐ€ ์ผ์น˜ํ•˜๋Š” ํšŒ์›์ด ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•œ ์ดํ›„, ์žˆ์œผ๋ฉด ํ•ด๋‹น ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ UserDetails์— ๋‹ด์•„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

 

7. UserDetailsService โžก๏ธ AuthenticationProvider

: Provider์—๊ฒŒ UserDetails๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

8. AuthenticationProvider โžก๏ธ AuthenticationManager

: Authentication ๊ฐ์ฒด ์•ˆ์— UserDetails๋ฅผ ๋‹ด์•„ Authentication ๊ฐ์ฒด๋ฅผ AuthenticationManager์—๊ฒŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

9,10. AuthenticationManager โžก๏ธ AuthenticationFilter โžก๏ธ SecurityContext 

: ๋ฆฌํ„ด๋ฐ›์€ Authentication ๊ฐ์ฒด๋ฅผ SecurityContextHolder์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

์ด ๊ณผ์ •์„ ํ† ๋Œ€๋กœ Authentication๊ฐ์ฒด๊ฐ€ SecuritContext์— ์ €์žฅ๋˜์–ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „๋ฐ˜์— ๊ฑธ์ณ ์ „์—ญ์ ์ธ ์ฐธ์กฐ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค!

 

์ด ์ „์— ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” Security ์„ค์ •ํŒŒ์ผ์— ๋“ฑ๋กํ•œ ํ•„ํ„ฐ์—์„œ .loginProcessingUrl("/login")์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์„œ /login ์ฃผ์†Œ๊ฐ€ ํ˜ธ์ถœ์ด ๋˜๋ฉด ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ๋‚š์•„์ฑ„์„œ 2~5 ๊ณผ์ •์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

UserDetailsService๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ–ˆ์—ˆ์ฃ .

 

์•„๋ž˜์™€ ๊ฐ™์ด UserDetailsService์˜ loadUserByUsername(String username)์—์„œ ๋ฐ”๋กœ DB์— ์ ‘๊ทผํ•˜์—ฌ ์ผ์น˜ํ•˜๋Š” ํšŒ์› ์กฐํšŒ๋ฅผ ํ†ตํ•ด UserDetails์˜ ์ƒ์† ํด๋ž˜์Šค์ธ PrincipalDetails์— user ์ •๋ณด๋ฅผ ๋‹ด์•„ ๋ฆฌํ„ดํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

+ Password๋Š” ์ผ์น˜์—ฌ๋ถ€๋ฅผ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ DaoAuthenticationProvider์—์„œ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.

//  ์‹œํ๋ฆฌํ‹ฐ session(๋‚ด๋ถ€ Authentication(๋‚ด๋ถ€ UserDetails))
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    log.info("username = {}",username);
    User user = userRepository.findByUsername(username);
    if (user != null) {
        return new PrincipalDetails(user);
    }
    return null;
}

 

Manager์™€ Provider๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ๋Š” ์•„๋ž˜ ์ž๋ฃŒ๋ฅผ ์ฐธ๊ณ 

 

Spring Security #3 ์•„ํ‚คํ…์ฒ˜ ์ดํ•ดํ•˜๊ธฐ, ์ธ์ฆ ๋ฐ ์ธ๊ฐ€ ์›๋ฆฌ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ - Spring Boot ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” Spring Security - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜ ์ดˆ๊ธ‰์—์„œ ์ค‘.๊ณ ๊ธ‰์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ API ์‚ฌ์šฉ๋ฒ•๊ณผ ๋‚ด๋ถ€ ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ•™์Šตํ•˜๊ฒŒ ๋˜๊ณ  ์ด๋ฅผ

jiwondev.tistory.com

 

Security Context์— Authenticaiton ๊ฐ์ฒด๊ฐ€ ๋‹ด๊น€์„ ๊ฐ•์กฐํ•˜๊ธฐ ์œ„ํ•ด ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ ๋กœ์ง์„ ๋‹ค์‹œ ํ•œ ๋ฒˆ ์ •๋ฆฌํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

์ผ๋ฐ˜ ๋กœ๊ทธ์ธ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ OAuth ๋กœ๊ทธ์ธ ๋˜ํ•œ Authentication์„ Security Context์— ๋‹ด๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค!

๋ฆฌ์†Œ์Šค ์ ‘๊ทผ์— ํ•„์š”ํ•œ ์„ธ์…˜์— SecurityContext๊ฐ€ ๋‹ด๊ฒจ์žˆ๊ณ , SecurityContext ์•ˆ์˜ Authentication์ด ๋‹ด๊ฒจ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, OAuth ์†Œ์…œ ๋กœ๊ทธ์ธ ๋˜ํ•œ Authentication ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

Tip : ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ๋‹ค์‹œ ์‚ฌ์ดํŠธ๋ฅผ ์ ‘์†ํ–ˆ์„ ๋•Œ ์„ธ์…˜์— ์ €์žฅ๋œ SecurityContext๋ฅผ ๊บผ๋‚ด์–ด ์™€์„œ SecurityContextHolder์— ์ €์žฅํ•˜๊ฒŒ ๋˜๊ณ  ์ด๋Š” ์ „์—ญ์œผ๋กœ SecurityContext ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ์›๋ฆฌ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

Authentication ๊ฐ์ฒด

Spring Security๋Š” ์‚ฌ์‹ค Authentication ๊ฐ์ฒด ์•ˆ์—๋Š” ์‚ฌ์‹ค UserDetails ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์†Œ์…œ ๋กœ๊ทธ์ธ ์œ ์ €๋ฅผ ์œ„ํ•œ OAuth2User ๊ฐ์ฒด ๋˜ํ•œ ๋‹ด์„ ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

 

์ด ์ „์— ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ ๋กœ์ง์—์„œ UserDetails ๊ตฌํ˜„์ฒด์ธ PrincipalDetails๋ฅผ ๋งŒ๋“ค์—ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ฐธ๊ณ 

// Security Session => Authentication => UserDetails(PrincipalDetails)
@Data
public class PrincipalDetails implements UserDetails {
    private User user;

    // ํ•ด๋‹น User์˜ ๊ถŒํ•œ์„ ๋ฆฌํ„ดํ•˜๋Š” ๊ณณ
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collection = new ArrayList<>();
        collection.add(
                new GrantedAuthority(){
            @Override
            public String getAuthority() {
                return user.getRole();
            }
        });
        return collection;
    }

 

ํ•˜์ง€๋งŒ ์ƒ์† ๊ตฌ์กฐ๋ฅผ ์ž˜ ์ด์šฉํ•œ๋‹ค๋ฉด ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ๊ณผ ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ๋”ฐ๋กœ ๋‚˜๋ˆ„์ง€ ์•Š๊ณ  PrincipalDetails๋กœ ํ‰์ณ์„œ ์ด๋ ‡๊ฒŒ ํ•œ ๋ฒˆ์— ๋ฌถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

// Security Session => Authentication => UserDetails(PrincipalDetails(user)) or OAuth2User(PrincipalDetails(user,attributes))
@Data
public class PrincipalDetails implements UserDetails, OAuth2User {
    private User user;
    private Map<String, Object> attributes;

    // ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ
    public PrincipalDetails(User user) {
        this.user = user;
    }

    // Oauth ๋กœ๊ทธ์ธ
    public PrincipalDetails(User user, Map<String, Object> attributes) {
        this.user = user;
        this.attributes = attributes;
    }
    
    ...
  • ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ ์œ ์ €์˜ ๊ฒฝ์šฐ, UserDetails๋ฅผ implementsํ•˜๊ฒŒ ๋˜๋ฉด ๋ฐ˜๋“œ์‹œ ๊ถŒํ•œ์„ ๋ฆฌํ„ดํ•˜๋Š” getAuthorities()๋ฅผ ํฌํ•จํ•œ ์—ฌ๋Ÿฌ ๋ฉ”์†Œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜์—ฌ ๊ตฌํ˜„ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์†Œ์…œ ๋กœ๊ทธ์ธ ์œ ์ €์˜ ๊ฒฝ์šฐ, OAuth2User๋ฅผ implementsํ•˜๊ฒŒ ๋˜๋ฉด ๋ฐ˜๋“œ์‹œ attributes์™€ name์„ ๋ฆฌํ„ดํ•˜๋Š” ๋‘ ๊ฐœ์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜์—ฌ ๊ตฌํ˜„ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
@Override
public String getName() {
    return null;
}

@Override
public Map<String, Object> getAttributes() {
    return attributes;
}
  • ์œ„ ์ฒ˜๋Ÿผ ์†Œ์…œ ๋กœ๊ทธ์ธ ์œ ์ €์™€ ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ ์œ ์ €๋ฅผ ์ƒ์„ฑ์ž๋กœ ๋‚˜๋ˆ„์–ด PrincipalDetails๋กœ ํ•œ ๋ฒˆ์— ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

OAuth2User์˜ UserDetailsService ๊ณ„์ธต ๊ฐœ๋ฐœ(๊ถŒํ•œ ๋ถ€์—ฌ ๋ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ)

์ผ๋ฐ˜ ๋กœ๊ทธ์ธ์—์„œ๋Š” UserDetailsService์˜ loadByUsernamepassword() ๋ฉ”์†Œ๋“œ์—์„œ ์‹ค์ œ DB์— ์ ‘๊ทผํ•˜์—ฌ ํšŒ์›์„ ์กฐํšŒํ•˜๊ณ  UserDetails๋ฅผ ๋ฆฌํ„ดํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์†Œ์…œ ๋กœ๊ทธ์ธ ๋˜ํ•œ ๋กœ์ง์€ ๊ฐ™์Šต๋‹ˆ๋‹ค๋งŒ, ๋ช…ํ™•ํ•œ ์ฐจ์ด๊ฐ€ ํ•˜๋‚˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ / UserDetailsService : loadUserByUsername()์—์„œ ํšŒ์› ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ DB์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ PrincipalDetails์— ์ •๋ณด๋ฅผ ๋‹ด์•„ ๋ฆฌํ„ด
  • ์†Œ์…œ ๋กœ๊ทธ์ธ / DefaultOAuth2UserService : userRequest ๊ธฐ๋ฐ˜ ๋ฐ์ดํ„ฐ ๊ฒ€์ƒ‰
    • ์ตœ์ดˆ ๋กœ๊ทธ์ธ ๊ฒฝ์šฐ : loadUser(OAuthRequest userRequest)์—์„œ ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฐ์ดํ„ฐ ์ •๋ณด์ธ userRequest๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ DB์— ๋ฐ์ดํ„ฐ ์‚ฝ์ž… ๋ฐ PrincipalDetails์— ์ •๋ณด๋ฅผ ๋‹ด์•„ ๋ฆฌํ„ด 
    • ์ตœ์ดˆ ๋กœ๊ทธ์ธ์ด ์•„๋‹Œ ๊ฒฝ์šฐ : DB์— ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ์ €์žฅํ•˜์ง€ ์•Š๊ณ  PrincipalDetails์— ์ •๋ณด๋ฅผ ๋‹ด์•„ ๋ฆฌํ„ด

 

์•„๋ž˜๋Š” ๊ตฌํ˜„ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

@Service
@RequiredArgsConstructor
@Slf4j
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    // ๊ตฌ๊ธ€๋กœ ๋ถ€ํ„ฐ ๋ฐ›์€ userRequest์— ๋Œ€ํ•œ ํ›„์ฒ˜๋ฆฌ ํ•จ์ˆ˜
    // ํ•จ์ˆ˜ ์ข…๋ฃŒ์‹œ @AuthenticationPrincipal ์–ด๋…ธํ…Œ์ด์…˜์ด ๋งŒ๋“ค์–ด์ง.
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        log.info("userRequest.clientRegistration : {}",userRequest.getClientRegistration());
        log.info("userRequest.access_Token : {}",userRequest.getAccessToken());
        log.info("userRequest.access_Token.Token_value  : {}",userRequest.getAccessToken().getTokenValue());

        OAuth2User oAuth2User = super.loadUser(userRequest);
        // ์œ„ request ์ •๋ณด๋ฅผ ํ† ๋Œ€๋กœ ๊ฐ•์ œ ํšŒ์›๊ฐ€์ž… ์ง„ํ–‰
        // ๊ตฌ๊ธ€๋กœ๊ทธ์ธ๋ฒ„ํŠผ -> ๊ตฌ๊ธ€๋กœ๊ทธ์ธ ์ฐฝ -> ๋กœ๊ทธ์ธ ์™„๋ฃŒ -> ์‚ฌ์šฉ์ž ์ •๋ณด code๋ฅผ ๋ฆฌํ„ด ๋ฐ›์Œ(userRequest์— ๋‹ด๊น€) -> code๋ฅผ ํ† ๋Œ€๋กœ Access Token ์š”์ฒญ
        //
        // userRequest ์ •๋ณด๋ฅผ ํ† ๋Œ€๋กœ
        // -> ํšŒ์› ํ”„๋กœํ•„์„ ๋ฐ›์•„์•ผํ•จ (loadUser ํ•จ์ˆ˜) -> ๊ตฌ๊ธ€๋กœ๋ถ€ํ„ฐ ํšŒ์› ํ”„๋กœํ•„์„ ๋ฐ›์•„์คŒ
        log.info("userRequest.getAttributes : {}", super.loadUser(userRequest).getAttributes());


        String provider = userRequest.getClientRegistration().getRegistrationId();
        String provider_id = oAuth2User.getAttribute("sub"); // provid

        String email = oAuth2User.getAttribute("email");
        String username = provider + provider_id; // google_sub
        String password = bCryptPasswordEncoder.encode("getInthere");
        String role = "ROLE_USER";

        User userEntity = userRepository.findByUsername(username);

        if (userEntity == null) {
            userEntity = User.builder()
                    .username(username)
                    .email(email)
                    .password(password)
                    .provider(provider)
                    .provider_id(provider_id)
                    .role(role)
                    .build();
            userRepository.save(userEntity);
        } else {
            log.info("์ตœ์ดˆ ๋กœ๊ทธ์ธ์ด ์•„๋‹™๋‹ˆ๋‹ค.");
        }
        return new PrincipalDetails(userEntity, oAuth2User.getAttributes());
    }
}
  • ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์ดํ›„ loadUser()์—์„œ userRequest์— Access Token์„ ์š”์ฒญํ•  code, ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ๋‹ด๊น๋‹ˆ๋‹ค.
  • ๊ถŒํ•œ์ธ ROLE์€ "ROLE_USER"๋กœ ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. (๊ถŒํ•œ ๋ถ€์—ฌ)
  • ์ด ์ •๋ณด๋ฅผ ํ† ๋Œ€๋กœ ์ตœ์ดˆ ๋กœ๊ทธ์ธ ์‹œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜๊ณ , user ์ •๋ณด์™€, userRequest์˜ attributes๋ฅผ PrincipalDetails์— ๋‹ด์•„ ๋ฆฌํ„ดํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ์ตœ์ดˆ ๋กœ๊ทธ์ธ์ด ์•„๋‹ ๊ฒฝ์šฐ if๋ฌธ์ด ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  PrincipalDetails์— user ์ •๋ณด์™€ attribute๋งŒ ๋‹ด์•„์„œ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

๋งŒ๋“  PrincipalOauthUserService๋Š” Filter์— ํ›„์ฒ˜๋ฆฌ ์„œ๋น„์Šค๋กœ ๋“ฑ๋กํ•ด ์ค์‹œ๋‹ค.

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, // @Secured ํ™œ์„ฑํ™”
                            prePostEnabled = true // @PreAuthorized ํ™œ์„ฑํ™”, @PostAuthroized ํ™œ์„ฑํ™”
)
public class SecurityConfig {
    @Autowired
    PrincipalOauth2UserService principalOauth2UserService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests()
                .antMatchers("/user/**").authenticated() // ์ธ์ฆ
                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')") // ์ธ์ฆ + ์ธ๊ฐ€
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") // ์ธ์ฆ + ์ธ๊ฐ€
                .anyRequest().permitAll()
                .and()
                .formLogin()
                .loginPage("/loginForm")
                .loginProcessingUrl("/login") // login ์ฃผ์†Œ๊ฐ€ ํ˜ธ์ถœ์ด๋˜๋ฉด ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ๋‚š์•„์ฑ„์„œ ๋Œ€์‹  ๋กœ๊ทธ์ธ ์ง„ํ–‰ํ•ด์คŒ.
                .defaultSuccessUrl("/")
                .and()
                .oauth2Login()
                .loginPage("/loginForm")
                .userInfoEndpoint()
                .userService(principalOauth2UserService); // ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์ดํ›„ ํ›„์ฒ˜๋ฆฌ

        return http.build();
    }

}

๋ฒˆ์™ธ : PrincipalDetails๋ฅผ OAuth2User, UserDetails ๋‘ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ PrincipalDetailsService ๋˜ํ•œ ์•„๋ž˜์™€ ํ•œ ๊ตฐ๋ฐ์— ๋ฌถ์–ด์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค๋งŒ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ๋ถ„๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

PrincipalDetailsService extends DefaultOAuth2UserService(OAuth ํšŒ์›) implements UserDetailsService(์ผ๋ฐ˜ ํšŒ์›)

SecurityConfig

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, // @Secured ํ™œ์„ฑํ™”
                            prePostEnabled = true // @PreAuthorized ํ™œ์„ฑํ™”, @PostAuthroized ํ™œ์„ฑํ™”
)
public class SecurityConfig {

    @Autowired
    PrincipalDetailsService principalDetailsService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests()
                .antMatchers("/user/**").authenticated() // ์ธ์ฆ
                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')") // ์ธ์ฆ + ์ธ๊ฐ€
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") // ์ธ์ฆ + ์ธ๊ฐ€
                .anyRequest().permitAll()
                .and()
                .formLogin()
                .loginPage("/loginForm")
                .loginProcessingUrl("/login") // login ์ฃผ์†Œ๊ฐ€ ํ˜ธ์ถœ์ด๋˜๋ฉด ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ๋‚š์•„์ฑ„์„œ ๋Œ€์‹  ๋กœ๊ทธ์ธ ์ง„ํ–‰ํ•ด์คŒ.
                .defaultSuccessUrl("/")
                .and()
                .oauth2Login()
                .loginPage("/loginForm")
                .userInfoEndpoint()
                .userService(principalDetailsService); // ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์ดํ›„ ํ›„์ฒ˜๋ฆฌ

        return http.build();
    }

}
  • userInfoEndpoint() ๋Š” OAuth2 ๋กœ๊ทธ์ธ ์„ฑ๊ณต ํ›„ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์„ค์ •์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • ์ฆ‰, userInfoEndpoint().userService()๋Š” OAuth2 ๋กœ๊ทธ์ธ ์„ฑ๊ณต ํ›„ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜จ ์ƒํƒœ์—์„œ ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„(Google, ๋„ค์ด๋ฒ„, ์นด์นด์˜ค ๋“ฑ) ์—์„œ ์ถ”๊ฐ€ ์ง„ํ–‰ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜ ์œ ์ € / OAuth ๋ฅผ ๋‚˜๋ˆ„์ง€ ์•Š์Œ์œผ๋กœ์จ ์ƒ๊ธฐ๋Š” ์žฅ์ 

// ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ
@ResponseBody
@GetMapping("/test/normal/login")
public String loginTest(Authentication authentication, @AuthenticationPrincipal UserDetails userDetails) {
    log.info("/test/normal/login ===================");
    PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal(); // ๋‹ค์šด์บ์ŠคํŒ… ํ•˜๊ฑฐ๋‚˜,
    log.info("authentication : {}", principalDetails.getUser());
    log.info("userDetails : {}", userDetails.getUsername()); // ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ํ•˜๊ฑฐ๋‚˜
    return "์„ธ์…˜ ์ •๋ณด ํ™•์ธํ•˜๊ธฐ";
}

// OAuth ๋กœ๊ทธ์ธ
@ResponseBody
@GetMapping("/test/oauth/login")
public String loginTestOauth(Authentication authentication, @AuthenticationPrincipal OAuth2User oauth) {
    log.info("/test/oauth/login ===================");
    OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();// ๋‹ค์šด์บ์ŠคํŒ… ํ•˜๊ฑฐ๋‚˜,
    log.info("authentication : {}", oAuth2User.getAttributes());
    log.info("oauth2User : {} ", oauth.getAttributes()); // ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ํ•˜๊ฑฐ๋‚˜

    return "Oauth ์„ธ์…˜ ์ •๋ณด ํ™•์ธํ•˜๊ธฐ";
}
  • ์œ„์—์„œ PrincipalDetails๋ฅผ UserDetails๋งŒ ์ƒ์†ํ•˜์—ฌ ์ผ๋ฐ˜ ์œ ์ €๋กœ ์ œํ•œํ•œ ๊ฒƒ์ด ์•„๋‹Œ, OAuth2User๊นŒ์ง€ ์ƒ์†ํ•œ ๋•๋ถ„์— Controller์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ์ผ๋ฐ˜/์†Œ์…œ ๋กœ๊ทธ์ธ์„ ์š”์ฒญ๋งคํ•‘์„ ๋‚˜๋ˆ„์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.
// ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ/OAuth ๋กœ๊ทธ์ธ ๋‚˜๋ˆ„์ง€ ์•Š๊ณ  ์ƒ์†์ฒ˜๋ฆฌ
// UserDetails -> PrincipalDetails
// OAuthUser -> PrincipalDetails
@ResponseBody
@GetMapping("/test/login")
public String loginTest(@AuthenticationPrincipal PrincipalDetails principalDetails) {
    log.info("/test/login ===================");
    log.info("principalDetails : {}", principalDetails.getUser());
    return "Oauth ๋˜๋Š” ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ ์„ธ์…˜ ์ •๋ณด ํ™•์ธํ•˜๊ธฐ";
}
  • ์œ„์™€ ๊ฐ™์ด ํ•˜๋‚˜์˜ ์š”์ฒญ์œผ๋กœ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • OAuth2 ๋กœ๊ทธ์ธ ํ›„์ฒ˜๋ฆฌ ์„œ๋น„์Šค์—์„œ ๋“ฑ๋กํ•œ @AuthenticationPricinpal ์–ด๋…ธํ…Œ์ด์…˜ : ์„ธ์…˜ ์ •๋ณด UserDetails์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ, ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ ๊ณ„์ธต์—์„œ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

<์ •๋ฆฌ>

  • OAuth2.0์„ ์ด์šฉํ•œ ์†Œ์…œ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์ดํ›„ ํ›„์ฒ˜๋ฆฌ(๊ถŒํ•œ๋ถ€์—ฌ ๋ฐ ์ตœ์ดˆ ๋กœ๊ทธ์ธ ์‹œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฝ์ž…)
  • OAuth๋ฅผ ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ ๋กœ์ง์„ ์ž˜ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

 

<์ฐธ๊ณ  ์ž๋ฃŒ>

 

Spring Security #3 ์•„ํ‚คํ…์ฒ˜ ์ดํ•ดํ•˜๊ธฐ, ์ธ์ฆ ๋ฐ ์ธ๊ฐ€ ์›๋ฆฌ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ - Spring Boot ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” Spring Security - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜ ์ดˆ๊ธ‰์—์„œ ์ค‘.๊ณ ๊ธ‰์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ API ์‚ฌ์šฉ๋ฒ•๊ณผ ๋‚ด๋ถ€ ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ•™์Šตํ•˜๊ฒŒ ๋˜๊ณ  ์ด๋ฅผ

jiwondev.tistory.com

 

 

[์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ] @AuthenticationPrincipal ๋กœ๊ทธ์ธ ์ •๋ณด ๋ฐ›์•„์˜ค๊ธฐ

@AuthenticationPrincipal

velog.io

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ