Skip to content

Commit 7120f74

Browse files
committed
default - aop
1 parent 5ab767e commit 7120f74

File tree

10 files changed

+144
-5
lines changed

10 files changed

+144
-5
lines changed

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ repositories {
1414

1515
dependencies {
1616
implementation 'org.springframework.boot:spring-boot-starter-web'
17-
testImplementation('org.springframework.boot:spring-boot-starter-test')
17+
implementation 'org.springframework.boot:spring-boot-starter-aop'
18+
testImplementation 'org.springframework.boot:spring-boot-starter-test'
1819
}
1920

2021
test {

src/main/java/nextstep/app/SecurityConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import nextstep.security.authentication.BasicAuthenticationFilter;
77
import nextstep.security.authentication.UsernamePasswordAuthenticationFilter;
88
import nextstep.security.authorization.CheckAuthenticationFilter;
9+
import nextstep.security.authorization.SecuredAspect;
10+
import nextstep.security.authorization.SecuredMethodInterceptor;
911
import nextstep.security.config.DefaultSecurityFilterChain;
1012
import nextstep.security.config.DelegatingFilterProxy;
1113
import nextstep.security.config.FilterChainProxy;
@@ -15,10 +17,12 @@
1517
import nextstep.security.userdetails.UserDetailsService;
1618
import org.springframework.context.annotation.Bean;
1719
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.context.annotation.EnableAspectJAutoProxy;
1821

1922
import java.util.List;
2023
import java.util.Set;
2124

25+
@EnableAspectJAutoProxy
2226
@Configuration
2327
public class SecurityConfig {
2428

@@ -38,6 +42,15 @@ public FilterChainProxy filterChainProxy(List<SecurityFilterChain> securityFilte
3842
return new FilterChainProxy(securityFilterChains);
3943
}
4044

45+
@Bean
46+
public SecuredMethodInterceptor securedMethodInterceptor() {
47+
return new SecuredMethodInterceptor();
48+
}
49+
// @Bean
50+
// public SecuredAspect securedAspect() {
51+
// return new SecuredAspect();
52+
// }
53+
4154
@Bean
4255
public SecurityFilterChain securityFilterChain() {
4356
return new DefaultSecurityFilterChain(

src/main/java/nextstep/app/ui/MemberController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import nextstep.app.domain.Member;
44
import nextstep.app.domain.MemberRepository;
5+
import nextstep.security.authorization.Secured;
56
import org.springframework.http.ResponseEntity;
67
import org.springframework.web.bind.annotation.GetMapping;
78
import org.springframework.web.bind.annotation.RestController;
@@ -22,4 +23,11 @@ public ResponseEntity<List<Member>> list() {
2223
List<Member> members = memberRepository.findAll();
2324
return ResponseEntity.ok(members);
2425
}
26+
27+
@Secured("ADMIN")
28+
@GetMapping("/search")
29+
public ResponseEntity<List<Member>> search() {
30+
List<Member> members = memberRepository.findAll();
31+
return ResponseEntity.ok(members);
32+
}
2533
}

src/main/java/nextstep/security/authentication/BasicAuthenticationFilter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
3434
return;
3535
}
3636

37-
Authentication authenticate = this.authenticationManager.authenticate(authentication);
37+
Authentication authResult = this.authenticationManager.authenticate(authentication);
3838
SecurityContext context = SecurityContextHolder.createEmptyContext();
39-
context.setAuthentication(authenticate);
39+
context.setAuthentication(authResult);
4040
SecurityContextHolder.setContext(context);
4141

4242
filterChain.doFilter(request, response);

src/main/java/nextstep/security/authorization/CheckAuthenticationFilter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@
1212
import java.util.Set;
1313

1414
public class CheckAuthenticationFilter extends OncePerRequestFilter {
15+
private static final String DEFAULT_REQUEST_URI = "/members";
1516

1617
@Override
1718
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
19+
if (!DEFAULT_REQUEST_URI.equals(request.getRequestURI())) {
20+
filterChain.doFilter(request, response);
21+
return;
22+
}
23+
1824
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
1925
if (authentication == null || !authentication.isAuthenticated()) {
2026
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package nextstep.security.authorization;
2+
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.web.bind.annotation.ResponseStatus;
5+
6+
@ResponseStatus(HttpStatus.FORBIDDEN)
7+
public class ForbiddenException extends RuntimeException {
8+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package nextstep.security.authorization;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target(ElementType.METHOD)
9+
@Retention(RetentionPolicy.RUNTIME)
10+
public @interface Secured {
11+
String value();
12+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package nextstep.security.authorization;
2+
3+
import nextstep.security.authentication.Authentication;
4+
import nextstep.security.authentication.AuthenticationException;
5+
import nextstep.security.context.SecurityContextHolder;
6+
import org.aspectj.lang.JoinPoint;
7+
import org.aspectj.lang.annotation.Aspect;
8+
import org.aspectj.lang.annotation.Before;
9+
import org.aspectj.lang.reflect.MethodSignature;
10+
11+
import java.lang.reflect.Method;
12+
13+
@Aspect
14+
public class SecuredAspect {
15+
16+
@Before("@annotation(nextstep.security.authorization.Secured)")
17+
public void checkSecured(JoinPoint joinPoint) throws NoSuchMethodException {
18+
Method method = getMethodFromJoinPoint(joinPoint);
19+
String secured = method.getAnnotation(Secured.class).value();
20+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
21+
if (authentication == null) {
22+
throw new AuthenticationException();
23+
}
24+
if (!authentication.getAuthorities().contains(secured)) {
25+
throw new ForbiddenException();
26+
}
27+
}
28+
29+
private Method getMethodFromJoinPoint(JoinPoint joinPoint) throws NoSuchMethodException {
30+
Class<?> targetClass = joinPoint.getTarget().getClass();
31+
String methodName = joinPoint.getSignature().getName();
32+
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
33+
34+
return targetClass.getMethod(methodName, parameterTypes);
35+
}
36+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package nextstep.security.authorization;
2+
3+
import nextstep.security.authentication.Authentication;
4+
import nextstep.security.authentication.AuthenticationException;
5+
import nextstep.security.context.SecurityContextHolder;
6+
import org.aopalliance.aop.Advice;
7+
import org.aopalliance.intercept.MethodInterceptor;
8+
import org.aopalliance.intercept.MethodInvocation;
9+
import org.springframework.aop.Pointcut;
10+
import org.springframework.aop.PointcutAdvisor;
11+
import org.springframework.aop.framework.AopInfrastructureBean;
12+
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
13+
14+
import java.lang.reflect.Method;
15+
16+
public class SecuredMethodInterceptor implements MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
17+
18+
private final Pointcut pointcut;
19+
20+
public SecuredMethodInterceptor() {
21+
this.pointcut = new AnnotationMatchingPointcut(null, Secured.class);
22+
}
23+
24+
@Override
25+
public Object invoke(MethodInvocation invocation) throws Throwable {
26+
Method method = invocation.getMethod();
27+
if (method.isAnnotationPresent(Secured.class)) {
28+
Secured secured = method.getAnnotation(Secured.class);
29+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
30+
if (authentication == null) {
31+
throw new AuthenticationException();
32+
}
33+
if (!authentication.getAuthorities().contains(secured.value())) {
34+
throw new ForbiddenException();
35+
}
36+
}
37+
return invocation.proceed();
38+
}
39+
40+
@Override
41+
public Pointcut getPointcut() {
42+
return pointcut;
43+
}
44+
45+
@Override
46+
public Advice getAdvice() {
47+
return this;
48+
}
49+
50+
@Override
51+
public boolean isPerInstance() {
52+
return true;
53+
}
54+
}

src/test/java/nextstep/app/SecuredTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.Set;
1818

1919
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
20+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
2021
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
2122

2223
@SpringBootTest
@@ -45,7 +46,7 @@ void request_search_success_with_admin_user() throws Exception {
4546
ResultActions response = mockMvc.perform(get("/search")
4647
.header("Authorization", "Basic " + token)
4748
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
48-
);
49+
).andDo(print());
4950

5051
response.andExpect(status().isOk())
5152
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2));
@@ -59,7 +60,7 @@ void request_search_fail_with_general_user() throws Exception {
5960
ResultActions response = mockMvc.perform(get("/search")
6061
.header("Authorization", "Basic " + token)
6162
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
62-
);
63+
).andDo(print());
6364

6465
response.andExpect(status().isForbidden());
6566
}

0 commit comments

Comments
 (0)