개발자의 오르막

[SpringSecurity #05] Authentication 와 SecurityContextHolder 본문

SpringFrameWork/SpringSecurity

[SpringSecurity #05] Authentication 와 SecurityContextHolder

계단 2020. 9. 27. 14:04

# AuthenticationManager 가 인증을 마친 뒤 리턴 받은 Authentication 객체의 행방은?

  → SecurityContextHolder 안에 들어간 것

  → 로그인 할 때 로그인 정보가 밑의 2개의 필터에 의해 관리됨

  

 

# SecurityContextPersistenceFilter

- SecurityContext를 HTTP session에 캐시(기본 전략)하여 여러 요청에서 Authentication을 공유하는 필터

- SecurityContextRepository를 교체하여 세션을 HTTP session 이 아닌 다른 곳에 저장하는 것도 가능하다.

 

HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
				response);
		SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

		try {
			SecurityContextHolder.setContext(contextBeforeChainExecution);

			chain.doFilter(holder.getRequest(), holder.getResponse());

		}
        finally {
			SecurityContext contextAfterChainExecution = SecurityContextHolder
					.getContext();
			// Crucial removal of SecurityContextHolder contents - do this before anything
			// else.
			SecurityContextHolder.clearContext();
			repo.saveContext(contextAfterChainExecution, holder.getRequest(),
					holder.getResponse());
			request.removeAttribute(FILTER_APPLIED);

			if (debug) {
				logger.debug("SecurityContextHolder now cleared, as request processing completed");
			}
		}

  만들어진 Context를 읽어와서 SecurityContextHolder 에 Set 해주는 구간이다.

  넣어준 이후 작업이 완료된 이후에는 (Request, Response) 다시 ContextHolder 를 비워주는 작업을 해준다.

 

 

# UsernamePasswordAuthenticationFilter

- 폼 인증을 처리하는 시큐리티 필터

- 인증된 Authentication 객체를 SecurityContextHolder에 넣어주는 필터

- SecurityContextHolder.getContext().getAuthentication(authentication)

 

public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		}

		String username = obtainUsername(request);
		String password = obtainPassword(request);

		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		username = username.trim();

		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);

		return this.getAuthenticationManager().authenticate(authRequest);
	}

  Authentication 인증정보가 있을 때에는 UsernamePasswordAuthenticationFilter 에 걸리게 된다.

  return 부분에서 보면 AuthenticationManager 가 인증을 실행하고 있는 부분을 볼 수 있다.

 

 

# AbstractAuthenticationProcessingFilter

- UsernamePasswordAuthenticationFilter 의 부모

- attemptAuthentication 가 실행되어 authResult (Authentication 타입)으로 리턴됨

 

protected void successfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain, Authentication authResult)
			throws IOException, ServletException {

		if (logger.isDebugEnabled()) {
			logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
					+ authResult);
		}

		SecurityContextHolder.getContext().setAuthentication(authResult);

		rememberMeServices.loginSuccess(request, response, authResult);

		// Fire event
		if (this.eventPublisher != null) {
			eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
					authResult, this.getClass()));
		}

		successHandler.onAuthenticationSuccess(request, response, authResult);
	}

  인증이 성공하게 되면 이 부분을 통해 SecurityContextHolder 에 인증정보를 Set 해주게 된다.

  로그인 처리가 되면 SecurityContextPersistenceFilter 에서 인증정보를 사용하고, 다시 Finally 에서

  비워주게 된다.

 

  → 즉 Session 에 넣어논 로그인 인증 정보를 불러와서 사용하고 비워준다.  (매 Request 마다 인증을 한다.)

      세션이 초기화되면 로그인 인증정보를 못 갖고와 사용할 수 없게 된다.

      

 

Comments