Skip to content

Commit cdcb564

Browse files
committed
[security-db-method-auth] MapBasedSecurityMetadataSource(3)
1 parent b49b116 commit cdcb564

15 files changed

+491
-0
lines changed

security-db-method-auth/src/main/java/kr/seok/security/config/MethodSecurityConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
3232
return mapBasedMethodSecurityMetadataSource();
3333
}
3434

35+
/* 빈으로 등록된 Map 기반 Method MetadataSource */
3536
@Bean
3637
public MapBasedMethodSecurityMetadataSource mapBasedMethodSecurityMetadataSource() {
3738
/* 기본 생성자가 아니라 DB로 부터 가져온 ResourceMap을 전달 */

security-db-method-auth/src/main/java/kr/seok/security/config/SecurityConfig.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
package kr.seok.security.config;
22

33

4+
import kr.seok.security.factory.UrlResourcesMapFactoryBean;
5+
import kr.seok.security.filter.PermitAllFilter;
6+
import kr.seok.security.form.common.FormAuthenticationDetailsSource;
7+
import kr.seok.security.form.handler.FormAccessDeniedHandler;
8+
import kr.seok.security.form.provider.FormAuthenticationProvider;
9+
import kr.seok.security.metadatasource.UrlFilterInvocationSecurityMetadataSource;
410
import kr.seok.security.service.SecurityResourceService;
11+
import kr.seok.security.voter.IpAddressVoter;
512
import org.springframework.beans.factory.annotation.Autowired;
613
import org.springframework.beans.factory.annotation.Qualifier;
14+
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
715
import org.springframework.context.annotation.Bean;
816
import org.springframework.context.annotation.Configuration;
917
import org.springframework.security.access.AccessDecisionManager;

security-db-method-auth/src/main/java/kr/seok/security/factory/MethodResourceFactoryBean.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import java.util.LinkedHashMap;
88
import java.util.List;
99

10+
/**
11+
*
12+
*/
1013
public class MethodResourceFactoryBean implements FactoryBean<LinkedHashMap<String, List<ConfigAttribute>>> {
1114

1215
private SecurityResourceService securityResourceService;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package kr.seok.security.factory;
2+
3+
import kr.seok.security.service.SecurityResourceService;
4+
import org.springframework.beans.factory.FactoryBean;
5+
import org.springframework.security.access.ConfigAttribute;
6+
import org.springframework.security.web.util.matcher.RequestMatcher;
7+
8+
import java.util.LinkedHashMap;
9+
import java.util.List;
10+
11+
public class UrlResourcesMapFactoryBean implements FactoryBean<LinkedHashMap<RequestMatcher, List<ConfigAttribute>>> {
12+
13+
private SecurityResourceService securityResourceService;
14+
private LinkedHashMap<RequestMatcher, List<ConfigAttribute>> requestMap;
15+
16+
public void setSecurityResourceService(SecurityResourceService securityResourceService) {
17+
this.securityResourceService = securityResourceService;
18+
}
19+
20+
@Override
21+
public LinkedHashMap<RequestMatcher, List<ConfigAttribute>> getObject() throws Exception {
22+
if(requestMap == null) {
23+
init();
24+
}
25+
return requestMap;
26+
}
27+
28+
private void init() {
29+
requestMap = securityResourceService.getResourceList();
30+
}
31+
32+
@Override
33+
public Class<?> getObjectType() {
34+
return LinkedHashMap.class;
35+
}
36+
37+
@Override
38+
public boolean isSingleton() {
39+
return true;
40+
}
41+
42+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package kr.seok.security.filter;
2+
3+
import org.springframework.security.access.intercept.InterceptorStatusToken;
4+
import org.springframework.security.web.FilterInvocation;
5+
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
6+
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
7+
import org.springframework.security.web.util.matcher.RequestMatcher;
8+
9+
import javax.servlet.ServletException;
10+
import javax.servlet.http.HttpServletRequest;
11+
import java.io.IOException;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
15+
/**
16+
* 권한이 없어 인가처리가 필요하지 않은 resource에 대해서 permit All 처리하는 Filter
17+
*/
18+
public class PermitAllFilter extends FilterSecurityInterceptor {
19+
20+
private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";
21+
private boolean observeOncePerRequest = true;
22+
23+
public List<RequestMatcher> permitAllRequestMatcher = new ArrayList<>();
24+
25+
public PermitAllFilter(String... permitAllResources) {
26+
for(String resource : permitAllResources) {
27+
permitAllRequestMatcher.add(new AntPathRequestMatcher(resource));
28+
}
29+
}
30+
31+
/**
32+
*
33+
* @param object FilterInvocation (사용자 정보를 가져올 수 있음)
34+
* @return
35+
*/
36+
@Override
37+
protected InterceptorStatusToken beforeInvocation(Object object) {
38+
boolean permitAll = false;
39+
HttpServletRequest request = ((FilterInvocation) object).getRequest();
40+
for(RequestMatcher requestMatcher : permitAllRequestMatcher) {
41+
/* 허용한 resource */
42+
if(requestMatcher.matches(request)) {
43+
permitAll = true;
44+
break;
45+
}
46+
}
47+
/* 해당 경로가 permitAll에 해당하는 경로인 경우 return null; 처리 */
48+
if(permitAll) {
49+
return null;
50+
}
51+
/* 사용자 정의로 작성된 resource 경로 처리 후 상위 클래스로 이동하여 처리 */
52+
return super.beforeInvocation(object);
53+
}
54+
55+
public void invoke(FilterInvocation fi) throws IOException, ServletException {
56+
if ((fi.getRequest() != null)
57+
&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
58+
&& observeOncePerRequest) {
59+
// filter already applied to this request and user wants us to observe
60+
// once-per-request handling, so don't re-do security checking
61+
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
62+
}
63+
else {
64+
// first time this request being called, so perform security checking
65+
if (fi.getRequest() != null && observeOncePerRequest) {
66+
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
67+
}
68+
InterceptorStatusToken token = beforeInvocation(fi);
69+
try {
70+
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
71+
} finally {
72+
super.finallyInvocation(token);
73+
}
74+
super.afterInvocation(token, null);
75+
}
76+
}
77+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package kr.seok.security.form.common;
2+
3+
import org.springframework.security.authentication.AuthenticationDetailsSource;
4+
import org.springframework.security.web.authentication.WebAuthenticationDetails;
5+
import org.springframework.stereotype.Component;
6+
7+
import javax.servlet.http.HttpServletRequest;
8+
9+
/**
10+
* AuthenticationDetails 를 생성하는 Bean 클래스
11+
*/
12+
@Component
13+
public class FormAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest , WebAuthenticationDetails> {
14+
@Override
15+
public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
16+
return new FormWebAuthenticationDetails(context);
17+
}
18+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package kr.seok.security.form.common;
2+
3+
import org.springframework.security.web.authentication.WebAuthenticationDetails;
4+
5+
import javax.servlet.http.HttpServletRequest;
6+
7+
/**
8+
* 사용자가 요청한 request 에 추가적인 값을 설정 할 수 있는 클래스
9+
*/
10+
public class FormWebAuthenticationDetails extends WebAuthenticationDetails {
11+
12+
private String secretKey;
13+
14+
public FormWebAuthenticationDetails(HttpServletRequest request) {
15+
super(request);
16+
secretKey = request.getParameter("secret_key");
17+
}
18+
19+
public String getSecretKey() {
20+
return secretKey;
21+
}
22+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package kr.seok.security.form.handler;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.security.access.AccessDeniedException;
5+
import org.springframework.security.web.access.AccessDeniedHandler;
6+
7+
import javax.servlet.http.HttpServletRequest;
8+
import javax.servlet.http.HttpServletResponse;
9+
import java.io.IOException;
10+
11+
@Slf4j
12+
public class FormAccessDeniedHandler implements AccessDeniedHandler {
13+
14+
private String errorPage;
15+
16+
@Override
17+
public void handle(
18+
HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException
19+
) throws IOException {
20+
21+
log.debug("예외 : {} ", accessDeniedException.getMessage());
22+
String deniedUrl = errorPage + "?exception=" + accessDeniedException.getMessage();
23+
response.setHeader("Content-Type", "application/json;charset=UTF-8");
24+
response.sendRedirect(deniedUrl);
25+
}
26+
27+
public void setErrorPage(String errorPage) {
28+
this.errorPage = errorPage;
29+
}
30+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package kr.seok.security.form.handler;
2+
3+
import org.springframework.security.authentication.BadCredentialsException;
4+
import org.springframework.security.authentication.InsufficientAuthenticationException;
5+
import org.springframework.security.core.AuthenticationException;
6+
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
7+
import org.springframework.stereotype.Component;
8+
9+
import javax.servlet.ServletException;
10+
import javax.servlet.http.HttpServletRequest;
11+
import javax.servlet.http.HttpServletResponse;
12+
import java.io.IOException;
13+
14+
@Component
15+
public class FormAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
16+
/* 인증 실패 처리 Handler */
17+
18+
@Override
19+
public void onAuthenticationFailure(
20+
HttpServletRequest request, HttpServletResponse response, AuthenticationException exception
21+
) throws IOException, ServletException {
22+
23+
/* Failure Handler */
24+
String errorMsg = "Invalid Username or Password";
25+
26+
if(exception instanceof BadCredentialsException) {
27+
errorMsg = "Invalid Username or Password";
28+
} else if(exception instanceof InsufficientAuthenticationException) {
29+
errorMsg = "Invalid Secret Key";
30+
}
31+
32+
setDefaultFailureUrl("/login?error=true&exception=" + errorMsg);
33+
34+
super.onAuthenticationFailure(request, response, exception);
35+
}
36+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package kr.seok.security.form.handler;
2+
3+
import org.springframework.security.core.Authentication;
4+
import org.springframework.security.web.DefaultRedirectStrategy;
5+
import org.springframework.security.web.RedirectStrategy;
6+
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
7+
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
8+
import org.springframework.security.web.savedrequest.RequestCache;
9+
import org.springframework.security.web.savedrequest.SavedRequest;
10+
import org.springframework.stereotype.Component;
11+
12+
import javax.servlet.http.HttpServletRequest;
13+
import javax.servlet.http.HttpServletResponse;
14+
import java.io.IOException;
15+
16+
@Component
17+
public class FormAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
18+
19+
private RequestCache requestCache = new HttpSessionRequestCache();
20+
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
21+
22+
@Override
23+
public void onAuthenticationSuccess(
24+
HttpServletRequest request, HttpServletResponse response, Authentication authentication
25+
) throws IOException {
26+
27+
/* 이전 요청이 없는 경우 이동할 기본 URL*/
28+
setDefaultTargetUrl("/");
29+
30+
SavedRequest savedRequest = requestCache.getRequest(request, response);
31+
/* 이전에 resource를 요청하지 않은 경우가 있을 수 있음 */
32+
if(savedRequest != null) {
33+
String targetUrl = savedRequest.getRedirectUrl();
34+
redirectStrategy.sendRedirect(request, response, targetUrl);
35+
} else {
36+
redirectStrategy.sendRedirect(request, response, getDefaultTargetUrl());
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)