Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#29] 인증 및 인가로직 구현 #62

Merged
merged 144 commits into from
Apr 27, 2024
Merged

[#29] 인증 및 인가로직 구현 #62

merged 144 commits into from
Apr 27, 2024

Conversation

Enble
Copy link
Contributor

@Enble Enble commented Apr 14, 2024

📄 Summary

Spring Security + OAuth2 + JWT 이용한 인증/인가로직의 구현입니다.
아직 테스트에서 모든 Controller layer가 통과되지 않아 수정이 필요합니다.

🕰️ Actual Time of Completion

1달

🙋🏻 More

{host}/oauth2/auth/kakao 로 get 요청시 쿼리 파라미터를 통해 JWT Access Token이 반환됩니다.
oauth 로그인이 성공적인 경우 /auth/success로 리다이렉트합니다. (수정 가능)
프론트는 해당 AccessToken을 Authorization 헤더에 Bearer를 붙여서 요청하면 정상적으로 요청이 반환됩니다.

"Refresh Token은 보안상의 이유로 유저에게 공개되지 않고 백에서 관리합니다."
따라서 만약 Access 토큰이 만료된다면 백 로직에서 Access Token을 재발급하여 프론트로 전달합니다.

Enble added 30 commits March 14, 2024 18:57
Copy link
Contributor

@minsang-alt minsang-alt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

핵심 전달을 위해 음슴체로 적겠습니다.

1. base64 디코딩 에러 발생

  1. Authorization헤더에 bearer: accessToken 을 넣고 api 요청
  2. e.g) /api/projects
  3. 해당 토큰을 받은 서버는 파싱 실패
    JWTDecoder클래스에서
    image
    이후 headerJson 깨짐
    image
  4. 그런데 payloadJson은 정상적으로 JWTDecoder에 의해 잘 파싱됨

문제 원인 및 해결

JWT token 생성과정에서 문제가 있었습니다.
기존 코드에서 PREFIX = "Bearer ";은 아래와 같이 Base64에 의해 인코딩이 되지 않아서 추후 디코딩할때 파싱문제가 생긴 것입니다.

    public String generateRefreshToken(String name, String role, String provider, String distinctId) {
        Date now = new Date();
        return PREFIX.concat(JWT.create()
            .withIssuer("AgileHub")
            .withSubject("RefreshToken")
            .withClaim("name", name)
            .withClaim("role", role)
            .withClaim("provider", provider)
            .withClaim("distinctId", distinctId)
            .withIssuedAt(now)
            .withExpiresAt(new Date(now.getTime() + refreshTokenValidationSeconds * 1000))
            .sign(Algorithm.HMAC512(secretKey)));
    }

위의 generateRefreshTokengenerateAccessToken 메서드 로직에서 PREFIX는 아래와 같이 제거하거나 인코딩이 필요합니다.
Bearer을 토큰을 만들때 PREFIX로 붙이지 않아도 문제가 없다는 걸 확인했습니다.

    public String generateRefreshToken(String name, String role, String provider, String distinctId) {
        Date now = new Date();
        return JWT.create()
            .withIssuer("AgileHub")
            .withSubject("RefreshToken")
            .withClaim("name", name)
            .withClaim("role", role)
            .withClaim("provider", provider)
            .withClaim("distinctId", distinctId)
            .withIssuedAt(now)
            .withExpiresAt(new Date(now.getTime() + refreshTokenValidationSeconds * 1000))
            .sign(Algorithm.HMAC512(secretKey));
    }

2. NullPointerException 발생

image

문제 원인

JWTAuthFilter에서 토큰이 정상적으로 확인이되면 saveAuthentication(accessToken);으로 해당 accessToken을 추출하여 SecurityContextHoldersetAuthentication()메서드를 통해 SecurityContext에 인증정보를 저장하는 로직이 있습니다.

그 로직에서 Member를 찾고 SecurityMember에 전달합니다. 현재 SecurityMember에는 UserInfo가 비어있는 상태입니다
image

이때 UsernamePasswordAuthenticationToken에 전달하고 시큐리티 필터 내부에서 getUsername()메서드를 실행합니다.
그런데 SecurityMembergetUsername()에는
userInfo.getNickname()으로 로직이 실행되고 있어서 NullPointerException이 발생합니다.
image

따라서 getUsername()을 Member로 하던지(대신 이때 OSIV를 고려해서 해야할거같아서 복잡..)
아니면 OAuth2UserInfo를 채워주는 로직을 추가적으로 만들어야 할것 같습니다.

이 부분을 대충 고쳐보고 하니 정상적으로 API에 인증되어 접속이 되는 것을 확인했습니다.
image

@Enble
Copy link
Contributor Author

Enble commented Apr 19, 2024

@minsang-alt

  1. base64 디코딩 에러 발생 관련
  • 해당 부분은 제 로컬에서는 에러가 확인이 잘 안되네요. 내부 구현이랑 로직에 대해서 조금 더 확인해보겠습니다.
  1. NullPointerException 발생 관련
  • 차마 그 부분을 고려하지 못했네요..
  • 내부 로직상 Member에서 정보를 가져오는 것이 맞다고 생각되므로, UserInfo를 넣는 것이 아닌 Member.getXxx()를 통해서 구현하는 방향으로 수정하겠습니다.
    => Service 레이어에서 getName()을 호출해 프록시에 값을 채워넣는 방식으로 수정했습니다. de1448b

Enble and others added 4 commits April 26, 2024 14:03
SecurityMember에서 userInfo로 getNickname() 호출 시 NullPointerException 발생.
따라서 member로 getName()을 호출하고, OSIV 옵션이 꺼져있으므로, 서비스 단에서 getName()을 한번 호출해 프록시 객체의 필드가 로드되도록 수정.
@AgileHub-DQ AgileHub-DQ deleted a comment from minsang-alt Apr 26, 2024
Copy link
Contributor

@minsang-alt minsang-alt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@minsang-alt minsang-alt merged commit d4de10d into main Apr 27, 2024
3 checks passed
@minsang-alt minsang-alt deleted the feat/29 branch April 27, 2024 05:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🌱 기능추가 새로운 기능 요청입니다
Projects
None yet
Development

Successfully merging this pull request may close these issues.

인증/인가 로직 구현
2 participants