-
Notifications
You must be signed in to change notification settings - Fork 36
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
[김병희] 선택 - RoleHierarchy 리뷰 요청 드립니다. #23
base: beng9re
Are you sure you want to change the base?
Changes from all commits
974d44d
7f0000d
0d59cc0
c354cdb
011e940
347e8f6
6a31473
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,9 +50,18 @@ public SecuredMethodInterceptor securedMethodInterceptor() { | |
return new SecuredMethodInterceptor(); | ||
} | ||
|
||
// @Bean | ||
// public SecuredAspect securedAspect() { | ||
// return new SecuredAspect(); | ||
// } | ||
|
||
|
||
@Bean | ||
public SecuredAspect securedAspect() { | ||
return new SecuredAspect(); | ||
public RoleHierarchy roleHierarchy() { | ||
return RoleHierarchyImpl.with() | ||
.role("ADMIN") | ||
.implies("USER","GUEST") | ||
.build(); | ||
} | ||
|
||
@Bean | ||
|
@@ -62,6 +71,7 @@ public RequestMatcherDelegatingAuthorizationManager requestAuthorizationManager( | |
mappings.add(new RequestMatcherEntry<>(new MvcRequestMatcher(HttpMethod.GET, "/members"), new HasAuthorityAuthorizationManager("ADMIN"))); | ||
mappings.add(new RequestMatcherEntry<>(new MvcRequestMatcher(HttpMethod.GET, "/search"), new PermitAllAuthorizationManager())); | ||
mappings.add(new RequestMatcherEntry<>(new MvcRequestMatcher(HttpMethod.POST, "/private"), new DenyAllAuthorizationManager())); | ||
mappings.add(new RequestMatcherEntry<>(new MvcRequestMatcher(HttpMethod.GET, "/hierarchy"), new HasAuthorityAuthorizationManager("USER").withRoleHierarchy(roleHierarchy()))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 권한 계층을 조회하도록 하셨군요! 단, roleHierarchy()을 매번 설정할 때 마다 주입해주는 형태로 구현해주셨는데 |
||
mappings.add(new RequestMatcherEntry<>(new AnyRequestMatcher(), new PermitAllAuthorizationManager())); | ||
return new RequestMatcherDelegatingAuthorizationManager(mappings); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,33 +2,45 @@ | |
|
||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.ServletRequest; | ||
import jakarta.servlet.ServletResponse; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import nextstep.security.authentication.Authentication; | ||
import nextstep.security.context.SecurityContextHolder; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
import org.springframework.web.filter.GenericFilterBean; | ||
|
||
import java.io.IOException; | ||
|
||
public class CheckAuthenticationFilter extends OncePerRequestFilter { | ||
public class CheckAuthenticationFilter extends GenericFilterBean { | ||
|
||
private final RequestMatcherDelegatingAuthorizationManager requestMatcherDelegatingAuthorizationManager; | ||
private final AuthorizationManager<HttpServletRequest> authorizationManager; | ||
|
||
public CheckAuthenticationFilter(RequestMatcherDelegatingAuthorizationManager requestMatcherDelegatingAuthorizationManager) { | ||
this.requestMatcherDelegatingAuthorizationManager = requestMatcherDelegatingAuthorizationManager; | ||
public CheckAuthenticationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) { | ||
this.authorizationManager = authorizationManager; | ||
} | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { | ||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { | ||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
|
||
AuthorizationDecision authorizationDecision = requestMatcherDelegatingAuthorizationManager.check(authentication, request); | ||
|
||
if (authorizationDecision.isDeny()) { | ||
response.setStatus(HttpServletResponse.SC_FORBIDDEN); | ||
try { | ||
AuthorizationDecision authorizationDecision = authorizationManager.check(authentication, (HttpServletRequest) request); | ||
authorizationCheck(authorizationDecision); | ||
} catch (ForbiddenException e) { | ||
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN); | ||
return; | ||
} | ||
|
||
filterChain.doFilter(request, response); | ||
chain.doFilter(request, response); | ||
} | ||
|
||
|
||
private static void authorizationCheck(AuthorizationDecision authorizationDecision) { | ||
if (authorizationDecision.isDeny()) { | ||
throw new ForbiddenException(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 만약 인증이 되지않은 경우 401응답이 필요할 수 있는데 현재는 모든 에로 상황에서 403이 응답이 되는 것 같네요! |
||
} | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,11 @@ | |
|
||
import nextstep.security.authentication.Authentication; | ||
|
||
import java.util.Collection; | ||
|
||
public class HasAuthorityAuthorizationManager implements AuthorizationManager { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 시큐리티 코드를 보면 AuthorityAuthorizationManager 와 AuthoritiesAuthorizationManager가 있는데요 |
||
private final String allowRole; | ||
private RoleHierarchy roleHierarchy = new NullRoleHierarchy(); | ||
|
||
public HasAuthorityAuthorizationManager(String allowRole) { | ||
this.allowRole = allowRole; | ||
|
@@ -15,10 +18,21 @@ public AuthorizationDecision check(Authentication authentication, Object object) | |
return AuthorizationDecision.deny(); | ||
} | ||
|
||
if (authentication.getAuthorities().contains(allowRole)) { | ||
if (isHasAccess(authentication)) { | ||
return AuthorizationDecision.granted(); | ||
} | ||
|
||
return AuthorizationDecision.deny(); | ||
} | ||
|
||
private boolean isHasAccess(Authentication authentication) { | ||
Collection<String> reachableGrantedAuthorities = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities()); | ||
|
||
return reachableGrantedAuthorities.contains(allowRole); | ||
} | ||
|
||
public HasAuthorityAuthorizationManager withRoleHierarchy(RoleHierarchy roleHierarchy) { | ||
this.roleHierarchy = roleHierarchy; | ||
return this; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package nextstep.security.authorization; | ||
|
||
import java.util.Collection; | ||
|
||
public class NullRoleHierarchy implements RoleHierarchy { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 널 객체 패턴 👍 |
||
|
||
@Override | ||
public Collection<String> getReachableGrantedAuthorities(Collection<String> authorities) { | ||
return authorities; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package nextstep.security.authorization; | ||
|
||
import java.util.Collection; | ||
|
||
public interface RoleHierarchy { | ||
Collection<String> getReachableGrantedAuthorities( | ||
Collection<String> authorities); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package nextstep.security.authorization; | ||
|
||
import java.util.*; | ||
|
||
public class RoleHierarchyImpl implements RoleHierarchy { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RoleHierarchy 구현 잘 해주셨습니다 👍 |
||
private final Map<String, Set<String>> hierarchyPath; | ||
|
||
private RoleHierarchyImpl(Map<String, Set<String>> hierarchyPath) { | ||
this.hierarchyPath = hierarchyPath; | ||
} | ||
|
||
@Override | ||
public Collection<String> getReachableGrantedAuthorities(Collection<String> authorities) { | ||
Set<String> grantedAuthorities = new HashSet<>(); | ||
|
||
for (String authority : authorities) { | ||
if (hierarchyPath.get(authority) != null) { | ||
grantedAuthorities.addAll(hierarchyPath.get(authority)); | ||
} | ||
} | ||
|
||
return grantedAuthorities; | ||
} | ||
|
||
public static RoleBuilder with() { | ||
return new RoleBuilder(); | ||
} | ||
|
||
public static class RoleBuilder { | ||
private static final Map<String, Set<String>> hierarchy = new HashMap<>(); | ||
private String role; | ||
|
||
public RoleBuilder role(String role) { | ||
this.role = role; | ||
hierarchy.put(role, new HashSet<>(Set.of(role))); | ||
return this; | ||
} | ||
|
||
public ImpliesBuilder implies(String... implies) { | ||
hierarchy.get(role).addAll(Set.of(implies)); | ||
|
||
for (int i = 0; i < implies.length; i++) { | ||
Set<String> paths = hierarchy.getOrDefault(implies[i], new HashSet<>()); | ||
paths.addAll(List.of(implies).subList(i, implies.length)); | ||
hierarchy.put(implies[i], paths); | ||
} | ||
|
||
return new ImpliesBuilder(); | ||
} | ||
|
||
public static class ImpliesBuilder { | ||
public RoleHierarchyImpl build() { | ||
return new RoleHierarchyImpl(hierarchy); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요구사항 설정 👍