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

[BE] refactor: JWT에 권한에 따른 클레임을 담기 위해 인증 기능 개편 (#982) #985

Merged
merged 4 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.festago.auth;

import com.festago.auth.annotation.Admin;
import com.festago.auth.domain.authentication.AdminAuthentication;
import com.festago.auth.domain.authentication.Authentication;
import com.festago.common.exception.UnexpectedException;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

public class AdminAuthenticationArgumentResolver implements HandlerMethodArgumentResolver {

private final AuthenticateContext authenticateContext;

public AdminAuthenticationArgumentResolver(AuthenticateContext authenticateContext) {
Assert.notNull(authenticateContext, "The authenticateContext must not be null");
this.authenticateContext = authenticateContext;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(AdminAuthentication.class) && parameter.hasParameterAnnotation(
Admin.class);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

굳이 parameter.hasParameterAnnotation(Admin.class)으로 검사할 필요가 있을까 싶네요. 😂
애초에 권한이 맞지 않는 경우가 개발자가 실수로 핸들러 메서드에 Authorization(role = ADMIN)을 해두고, 인자에 MemberAuhentication을 받는 경우일텐데..

Copy link
Member

Choose a reason for hiding this comment

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

parameter가 ~Authentication 으로 바뀌었으니 @Admin 과 같은 어노테이션은 없애도 되지 않을까요?


@Override
public AdminAuthentication resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory
) {
Authentication authentication = authenticateContext.getAuthentication();
if (authentication instanceof AdminAuthentication adminAuthentication) {
return adminAuthentication;
}
throw new UnexpectedException("인가된 권한이 인자의 권한과 맞지 않습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.festago.auth;

import com.festago.auth.annotation.Authorization;
import com.festago.auth.application.HttpRequestTokenExtractor;
import com.festago.auth.domain.AuthenticationTokenExtractor;
import com.festago.auth.domain.authentication.Authentication;
import com.festago.common.exception.ErrorCode;
import com.festago.common.exception.ForbiddenException;
import com.festago.common.exception.UnauthorizedException;
import com.festago.common.exception.UnexpectedException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.Assert;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

public class AnnotationAuthorizationInterceptor implements HandlerInterceptor {

private final HttpRequestTokenExtractor httpRequestTokenExtractor;
private final AuthenticationTokenExtractor authenticationTokenExtractor;
private final AuthenticateContext authenticateContext;

public AnnotationAuthorizationInterceptor(
HttpRequestTokenExtractor httpRequestTokenExtractor,
AuthenticationTokenExtractor authenticationTokenExtractor,
AuthenticateContext authenticateContext)
{
Assert.notNull(httpRequestTokenExtractor, "The httpRequestTokenExtractor must not be null");
Assert.notNull(authenticationTokenExtractor, "The authenticationTokenExtractor must not be null");
Assert.notNull(authenticateContext, "The authenticateContext must not be null");
this.httpRequestTokenExtractor = httpRequestTokenExtractor;
this.authenticationTokenExtractor = authenticationTokenExtractor;
this.authenticateContext = authenticateContext;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Authorization authorization = handlerMethod.getMethodAnnotation(Authorization.class);
if (authorization == null) {
throw new UnexpectedException("HandlerMethod에 Authorization 어노테이션이 없습니다.");
}
String token = httpRequestTokenExtractor.extract(request)
.orElseThrow(() -> new UnauthorizedException(ErrorCode.NEED_AUTH_TOKEN));
Authentication authentication = authenticationTokenExtractor.extract(token);
if (authentication.getRole() != authorization.role()) {
throw new ForbiddenException(ErrorCode.NOT_ENOUGH_PERMISSION);
}
authenticateContext.setAuthentication(authentication);
return true;
}
}
82 changes: 0 additions & 82 deletions backend/src/main/java/com/festago/auth/AuthInterceptor.java

This file was deleted.

18 changes: 11 additions & 7 deletions backend/src/main/java/com/festago/auth/AuthenticateContext.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package com.festago.auth;

import com.festago.auth.domain.Role;
import com.festago.auth.domain.authentication.AnonymousAuthentication;
import com.festago.auth.domain.authentication.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;

@Component
@RequestScope
public class AuthenticateContext {

private Long id;
private Role role = Role.ANONYMOUS;
private Authentication authentication = AnonymousAuthentication.getInstance();

public void setAuthenticate(Long id, Role role) {
this.id = id;
this.role = role;
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}

public Long getId() {
return id;
return authentication.getId();
}

public Role getRole() {
return role;
return authentication.getRole();
}

public Authentication getAuthentication() {
return authentication;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.festago.auth;

import com.festago.auth.application.HttpRequestTokenExtractor;
import com.festago.auth.domain.AuthenticationTokenExtractor;
import com.festago.auth.domain.Role;
import com.festago.auth.domain.authentication.Authentication;
import com.festago.common.exception.ErrorCode;
import com.festago.common.exception.ForbiddenException;
import com.festago.common.exception.UnauthorizedException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.Assert;
import org.springframework.web.servlet.HandlerInterceptor;

public class FixedAuthorizationInterceptor implements HandlerInterceptor {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

기존 경로로 권한을 검증하는 기능을 위해 만들어진 인터셉터 입니다.
(어드민, 레거시)


private final HttpRequestTokenExtractor httpRequestTokenExtractor;
private final AuthenticationTokenExtractor authenticationTokenExtractor;
private final AuthenticateContext authenticateContext;
private final Role role;

public FixedAuthorizationInterceptor(
HttpRequestTokenExtractor httpRequestTokenExtractor,
AuthenticationTokenExtractor authenticationTokenExtractor,
AuthenticateContext authenticateContext,
Role role
) {
Assert.notNull(httpRequestTokenExtractor, "The httpRequestTokenExtractor must not be null");
Assert.notNull(authenticationTokenExtractor, "The authenticationTokenExtractor must not be null");
Assert.notNull(authenticateContext, "The authenticateContext must not be null");
Assert.notNull(role, "The role must not be null");
this.httpRequestTokenExtractor = httpRequestTokenExtractor;
this.authenticationTokenExtractor = authenticationTokenExtractor;
this.authenticateContext = authenticateContext;
this.role = role;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = httpRequestTokenExtractor.extract(request)
.orElseThrow(() -> new UnauthorizedException(ErrorCode.NEED_AUTH_TOKEN));
Authentication authentication = authenticationTokenExtractor.extract(token);
if (authentication.getRole() != role) {
throw new ForbiddenException(ErrorCode.NOT_ENOUGH_PERMISSION);
}
authenticateContext.setAuthentication(authentication);
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.festago.auth;

import com.festago.auth.annotation.Member;
import com.festago.auth.domain.authentication.Authentication;
import com.festago.auth.domain.authentication.MemberAuthentication;
import com.festago.common.exception.UnexpectedException;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

public class MemberAuthenticationArgumentResolver implements HandlerMethodArgumentResolver {

private final AuthenticateContext authenticateContext;

public MemberAuthenticationArgumentResolver(AuthenticateContext authenticateContext) {
Assert.notNull(authenticateContext, "The authenticateContext must not be null");
this.authenticateContext = authenticateContext;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(MemberAuthentication.class) && parameter.hasParameterAnnotation(
Member.class);
}

@Override
public MemberAuthentication resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory
) {
Authentication authentication = authenticateContext.getAuthentication();
if (authentication instanceof MemberAuthentication memberAuthentication) {
return memberAuthentication;
}
throw new UnexpectedException("인가된 권한이 인자의 권한과 맞지 않습니다.");
}
}
12 changes: 8 additions & 4 deletions backend/src/main/java/com/festago/auth/RoleArgumentResolver.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package com.festago.auth;

import com.festago.auth.domain.Role;
import com.festago.common.exception.ErrorCode;
import com.festago.common.exception.ForbiddenException;
import com.festago.common.exception.UnexpectedException;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
* @deprecated 기존 Long으로 식별자를 받는 Controller가 많기에, 해당 클래스 삭제하지 않고 유지
*/
@Deprecated
public class RoleArgumentResolver implements HandlerMethodArgumentResolver {

private final Role role;
Expand All @@ -30,9 +33,10 @@ public boolean supportsParameter(MethodParameter parameter) {

@Override
public Long resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
NativeWebRequest webRequest, WebDataBinderFactory binderFactory
) {
if (authenticateContext.getRole() != this.role) {
throw new ForbiddenException(ErrorCode.NOT_ENOUGH_PERMISSION);
throw new UnexpectedException("인가된 권한이 인자의 권한과 맞지 않습니다.");
}
return authenticateContext.getId();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.festago.auth.annotation;

import com.festago.auth.domain.Role;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {

Role role();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.festago.auth.annotation;

import com.festago.auth.domain.Role;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand All @@ -9,6 +10,7 @@
@SecurityRequirement(name = "bearerAuth")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Authorization(role = Role.MEMBER)
public @interface MemberAuth {

}

This file was deleted.

This file was deleted.

Loading
Loading