Spring Security / Servlet Applications / Integrations / Spring MVC
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html
Spring MVC Integration :: Spring Security
Spring Security provides AuthenticationPrincipalArgumentResolver, which can automatically resolve the current Authentication.getPrincipal() for Spring MVC arguments. By using @EnableWebSecurity, you automatically have this added to your Spring MVC configur
docs.spring.io
틀린 내용이 있다면 알려주세요, 감사합니다 💻
Spring MVC Integration
Spring Security 는 Spring MVC 와 a number of optional integrations 을 제공한다
Spring Security 4.0 부터는 @EnableWebMvcSecurity 대신 @EnableWebSecurity 클래스 경로릴 기반으로 MVC 기능을 추가한다
@EnableWebMvcSecurity @EnableWebSecurity
@EnableWebSecurity 애노테이션을 추가하면 된다
만약 Spring MVC의 WebMvcConfigurer, 즉 WebMvcConfigurationSupport 직접 통합과 같은 옵션을 사용할 경우 Spring Security 구성을 수동으로 제공해야한다
MvcRequestMatcher
Spring Security 는 Spring MVC가 MvcRequestMatcher를 사용하여 URL에서 일치하는 방식과의 통합을 제공한다
이는 보안규칙이 요청을 처리하는 데 사용하는 논리와 같은지 확인할 때 유용하다,
MvcRequestMatcher를 사용하려면 DispatcherServlet과 동일한 ApplicationContext에
Spring Security Configuration을 배치해야한다.
Spring Security의 MvcRequestMatcher가 mvcHandlerMappingIntrospector라는 이름의 HandlerMappingIntrospector 빈이 일치해야 사용되는 Spring MVC 구성에 의해 등록되기 때문이다.
DispatcherServlet.xml
xml 예시
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- All Spring Configuration (both MVC and Security) are in /WEB-INF/spring/ -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/*.xml</param-value>
</context-param>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Load from the ContextLoaderListener -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
WebSecurityConfiguration in placed in the ApplicationContext of the DispatcherServlet
Java 에시
public class SecurityInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { RootConfiguration.class,
WebMvcConfiguration.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
HttpServletRequest 및 method security 를 일치시켜 권한 부여 규칙을 제공하는 것이 좋다
왜냐하면 코드 경로의 초기에 발생할 수 있는 Attack surface를 줄이는 데 도움이 된다. method security는 누군가가 웹 권한 부여 규칙을 우회하더라도 애플리케이션이 여전히 안전하도록 보장한다. Defense in Depth
https://en.wikipedia.org/wiki/Attack_surface
https://en.wikipedia.org/wiki/Defense_in_depth_(computing)
매핑이 된 컨트롤러는 어떻게 해야할까?
@RequestMapping("/admin")
public String admin() {
// ...
}
이럴 경우에는 HttpServletRequest 에 권한 부여 규칙을 정해주면 된다
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/admin").hasRole("ADMIN")
);
return http.build();
}
xml예시
<http>
<intercept-url pattern="/admin" access="hasRole('ADMIN')"/>
</http>
이러한 규칙이 있다면 /admin/** 이 들어있는 모든 구성에서 매핑시킬 수 있어야한다
Fortunately, when using the requestMatchers DSL method, Spring Security automatically creates a MvcRequestMatcher if it detects that Spring MVC is available in the classpath.
Therefore, it will protect the same URLs that Spring MVC will match on by using Spring MVC to match on the URL.
One common requirement when using Spring MVC is to specify the servlet path property, for that you can use the MvcRequestMatcher.
Builder to create multiple MvcRequestMatcher instances that share the same servlet path
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path");
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN")
.requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER")
);
return http.build();
}
<http request-matcher="mvc">
<intercept-url pattern="/admin" access="hasRole('ADMIN')"/>
</http>
@AuthenticationPrincipal
Spring Security는 Spring MVC arguments에 대한 현재 Authentication.getPrincipal()을 자동으로 해결할 수 있는 AuthenticationPrincipalArgumentResolver를 제공한다.
@EnableWebSecurity를 사용하면 Spring MVC 구성에 자동으로 추가된다
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
AuthenticationPrincipalArgumentResolver 가 제대로 구성이 되어있다면 Spring Security와 분리도 가능하다
Consider a situation where a custom UserDetailsService returns an Object that implements UserDetails and your own CustomUser Object.
로그인한 사람의 UserDetailsService를 반환하여 액세스하는 예제이다
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser() {
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
CustomUser custom = (CustomUser) authentication == null ? null : authentication.getPrincipal();
// .. find messages for this user and return them ...
}
Spring Security 3.2 부터는 애노테이션으로 해결할 수 있다.
(이거 진짜 굉장히 유용하다)
// ...
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {
// .. find messages for this user and return them ...
}
경우에 따라 principal 을 바꿔야할 수도 있다
final 이 붙으면 확장할 수 없기 때문에
UserDetailsService는 UserDetails를 구현하고 CustomUser(교체할 주체)에 액세스하기 위한 getCustomUser라는 메서드를 제공하는 개체를 반환시키면 된다
public class CustomUserUserDetails extends User {
// ...
public CustomUser getCustomUser() {
return customUser;
}
}
루트 개체로 사용하는 SpEL 표현식으로 접근
// ...
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal(expression = "customUser") CustomUser customUser) {
// .. find messages for this user and return them ...
}
이건 SpEL 표현식에서 bean 참조 및 예시
if we were using JPA to manage our users and if we wanted to modify and save a property on the current user:
// ...
@PutMapping("/users/self")
public ModelAndView updateName(@AuthenticationPrincipal(expression = "@jpaEntityManager.merge(#this)") CustomUser attachedCustomUser,
@RequestParam String firstName) {
// change the firstName on an attached instance which will be persisted to the database
attachedCustomUser.setFirstName(firstName);
// ...
}
@AuthenticationPrincipal 자체 애노테이션에 메타주석을 작성하여 Spring Security에 대한 종속성을 추가 제거할 수 있다
Spring Security 종속성을 제거하기위해 @CurrentUser 을 새로 생성하는 것은 사용하기 위한 소비 application 이기 때문에
꼭 필요한 단계는 아니지만 security 와 격리시켜 central location에서 처리하기 때문에 권고된다
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@CurrentUser CustomUser customUser) {
// .. find messages for this user and return them ...
}
We have isolated our dependency on Spring Security to a single file.
Now that @CurrentUser has been specified, we can use it to signal to resolve our CustomUser of the currently authenticated user
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {}
Spring MVC Async Integration
Spring Web MVC 3.2 부터는 비동기 요청 처리를 아주 잘 지원해주고 있다
추가 구성 없이도 컨트롤러에 반환된 Callable를 호출하는 thread에 대해 SecurityContext 를 자동 설정해줄 수 있다
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public Object call() throws Exception {
// ...
return "someView";
}
};
}
Associating SecurityContext
Spring Security는 WebAsyncManager와 통합된다. Callable을 처리할 때 사용되는 SecurityContext는 startCallableProcessing이 호출될 때 SecurityContextHolder에 존재하는 SecurityContext이다.
컨트롤러에서 반환되는 DeferredResult와는 자동통합되지 않는다. 이는 DeferredResult가 사용자에 의해 처리되기 때문에 자동으로 통합할 수 있는 방법이 없기 때문이다. 그러나 여전히 동시성 지원을 사용하여 Spring Security와의 투명한 통합을 제공할 수 있다.
DelegatingSecurityContextRunnable동시성 지원
https://docs.spring.io/spring-security/reference/features/integrations/concurrency.html#concurrency
Concurrency Support :: Spring Security
In the previous section we found that it was easy to use the DelegatingSecurityContextRunnable, but it was not ideal since we had to be aware of Spring Security in order to use it. Let’s take a look at how DelegatingSecurityContextExecutor can shield our
docs.spring.io
Spring MVC and CSRF Integration
SpringSecurity는 Spring MVC와 통합하여 CSRF 보호 기능을 추가한다
Automatic Token Inclusion
form tag 예시
18. View technologies
As of version 2.0, Spring provides a comprehensive set of data binding-aware tags for handling form elements when using JSP and Spring Web MVC. Each tag provides support for the set of attributes of its corresponding HTML tag counterpart, making the tags f
docs.spring.io
Include the CSRF Token
https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html#servlet-csrf-include
Cross Site Request Forgery (CSRF) for Servlet Environments :: Spring Security
There are a few special considerations to consider when implementing protection against CSRF attacks. This section discusses those considerations as they pertain to servlet environments. See CSRF Considerations for a more general discussion. Logging In It
docs.spring.io
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:form="http://www.springframework.org/tags/form" version="2.0">
<jsp:directive.page language="java" contentType="text/html" />
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<!-- ... -->
<c:url var="logoutUrl" value="/logout"/>
<form:form action="${logoutUrl}"
method="post">
<input type="submit"
value="Log out" />
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
</form:form>
<!-- ... -->
</html>
</jsp:root>
<!-- ... -->
<form action="/context/logout" method="post">
<input type="submit" value="Log out"/>
<input type="hidden" name="_csrf" value="f81d4fae-7dec-11d0-a765-00a0c91e6bf6"/>
</form>
<!-- ... -->
Resolving the CsrfToken
Spring Security는 Spring MVC arguments에 대한 현재의 CsrfToken을 자동으로 해결할 수 있는 CsrfTokenArgumentResolver를 제공된다.
@EnableWebSecurity를 사용하면 Spring MVC 구성에 자동으로 추가가 되며 XML 기반 구성을 사용하는 경우 직접 추가해야 한다. CsrfTokenArgumentResolver가 올바르게 구성되면 정적 HTML 기반 애플리케이션에 CsrfToken을 확인할 수 있다.
@RestController
public class CsrfController {
@RequestMapping("/csrf")
public CsrfToken csrf(CsrfToken token) {
return token;
}
}
It is important to keep the CsrfToken a secret from other domains. This means that, if you use Cross Origin Sharing (CORS), you should NOT expose the CsrfToken to any external domains.
내가 정리한 CORS
https://delightpip.tistory.com/59
[Spring framework Web MVC docs] 1.7. CORS
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-cors Web on Servlet Stack Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The form
delightpip.tistory.com
'Web > spring' 카테고리의 다른 글
[Spring Boot Web Servlet] 1.1.10. Error Handling (0) | 2023.02.21 |
---|---|
[Spring Framework integration] 1. REST Clients (0) | 2023.02.19 |
[Spring framework Web MVC docs] 1.10. HTTP Caching (0) | 2023.02.18 |
[Spring framework Web MVC docs] 2. REST Clients (0) | 2023.02.18 |
[Spring framework Core] 5. Aspect Oriented Programming with Spring (2) (0) | 2023.02.17 |