[로그인처리] Spring security 1

2021. 10. 4. 16:20
< 보안 관련 프로젝트 생성 >
1. 프로젝트 생성
2. 한글 처리
3. 보안 관련 라이브러리 추가
4. 스프링 설정 파일 분리
5. 보안 관련 설정 파일 만들기

 

pom.xml
4개의 라이브러리 설정 - 버전주의
  • 스프링 시큐리티를 사용하기 위해서 다운로드 해야한다.(4가지)
  • 스프링 시큐리티는 스프링 프레임워크(5.0.7)보다 낮은 버전(5.0.6)을 사용해야 한다.

 

<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
		<org.aspectj-version>1.9.0</org.aspectj-version>
		<org.slf4j-version>1.7.25</org.slf4j-version>
		<org.security-version>5.0.6.RELEASE</org.security-version>
</properties>


<!-- Spring Security -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>${org.security-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${org.security-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${org.security-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>${org.security-version}</version>
		</dependency>

 

 

web.xml
  • 한글 처리 밑에 시큐리티 객체 생성
  • contextConfigLocation에 해당 xml 집어넣기 
  • /WEB-INF/spring/security-context.xml 추가하기
	
    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/spring/root-context.xml
			/WEB-INF/spring/security-context.xml
		</param-value>
	</context-param>
    
 
    
    <!-- Spring Security Filter -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
 
    <filter-mapping>
          <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

⇒ 12개의 필터 객체를 생성함.

 

 

security-context.xml
  • root-context.xml 복사하고 Namespaces에서 ✔️security
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
   xmlns="http://www.springframework.org/schema/security"
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   <http> 
      <form-login />
   </http> 
   
   <!-- provider --> 
   <authentication-manager>

   </authentication-manager>

</beans:beans>

 

*책에는 이렇게 되어있다. (security:)

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
   xmlns="http://www.springframework.org/schema/security"
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   <security:http> 
      <security:form-login />
   </security:http> 
   
   <!-- provider --> 
   <security:authentication-manager>

   </security:authentication-manager>

</beans:beans>

  • 지금까지 우리가 했던 프로그래밍은 컨트롤러에서 login.jsp로 처리했는데, 스프링 시큐리티가 로그인을 처리해주고 있다. (왜? 내가 컨트롤러에서 처리하고 있지 않으므로)
  • 내가 처리해주지 않는 이상 누군가가 낚아채고 있는게 핵심이다.

 

어디에서 처리하는가?
1)
Maven Dependecies → spring-security-web-5.0.6.RELEASE.jar → org.springframework.security.web.authentication.ui → DefaultLoginPageGenerationFilter.class

2) 
Maven Dependecies → spring-security-web-5.0.6.RELEASE.jar →
org.springframework.security.web.server.ui → LoginPageGeneratingWebFilter.class

 

if (formLoginEnabled) {
			sb.append("<h3>Login with Username and Password</h3>");
			sb.append("<form name='f' action='").append(request.getContextPath())
					.append(authenticationUrl).append("' method='POST'>\n");
			sb.append("<table>\n");
			sb.append("	<tr><td>User:</td><td><input type='text' name='");
			sb.append(usernameParameter).append("' value='").append("'></td></tr>\n");
			sb.append("	<tr><td>Password:</td><td><input type='password' name='")
					.append(passwordParameter).append("'/></td></tr>\n");

			if (rememberMeParameter != null) {
				sb.append("	<tr><td><input type='checkbox' name='")
						.append(rememberMeParameter)
						.append("'/></td><td>Remember me on this computer.</td></tr>\n");
			}

			sb.append("	<tr><td colspan='2'><input name=\"submit\" type=\"submit\" value=\"Login\"/></td></tr>\n");
			renderHiddenInputs(sb, request);
			sb.append("</table>\n");
			sb.append("</form>");
		}

 

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.web.server.ui;

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.web.server.csrf.CsrfToken;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;

/**
 * Generates a default log in page used for authenticating users.
 *
 * @author Rob Winch
 * @since 5.0
 */
public class LoginPageGeneratingWebFilter implements WebFilter {
	private ServerWebExchangeMatcher matcher = ServerWebExchangeMatchers
		.pathMatchers(HttpMethod.GET, "/login");

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
		return this.matcher.matches(exchange)
			.filter(ServerWebExchangeMatcher.MatchResult::isMatch)
			.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
			.flatMap(matchResult -> render(exchange));
	}

	private Mono<Void> render(ServerWebExchange exchange) {
		ServerHttpResponse result = exchange.getResponse();
		result.setStatusCode(HttpStatus.OK);
		result.getHeaders().setContentType(MediaType.TEXT_HTML);
		return result.writeWith(createBuffer(exchange));
	}

	private Mono<DataBuffer> createBuffer(ServerWebExchange exchange) {
		MultiValueMap<String, String> queryParams = exchange.getRequest()
			.getQueryParams();
		Mono<CsrfToken> token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
		return token
			.map(LoginPageGeneratingWebFilter::csrfToken)
			.defaultIfEmpty("")
			.map(csrfTokenHtmlInput -> {
				boolean isError = queryParams.containsKey("error");
				boolean isLogoutSuccess = queryParams.containsKey("logout");
				byte[] bytes = createPage(isError, isLogoutSuccess, csrfTokenHtmlInput);
				DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
				return bufferFactory.wrap(bytes);
			});
	}

	private static byte[] createPage(boolean isError, boolean isLogoutSuccess, String csrfTokenHtmlInput) {
		String page =  "<!DOCTYPE html>\n"
			+ "<html lang=\"en\">\n"
			+ "  <head>\n"
			+ "    <meta charset=\"utf-8\">\n"
			+ "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
			+ "    <meta name=\"description\" content=\"\">\n"
			+ "    <meta name=\"author\" content=\"\">\n"
			+ "    <title>Please sign in</title>\n"
			+ "    <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"
			+ "    <link href=\"http://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"
			+ "  </head>\n"
			+ "  <body>\n"
			+ "     <div class=\"container\">\n"
			+ "      <form class=\"form-signin\" method=\"post\" action=\"/login\">\n"
			+ "        <h2 class=\"form-signin-heading\">Please sign in</h2>\n"
			+ createError(isError)
			+ createLogoutSuccess(isLogoutSuccess)
			+ "        <p>\n"
			+ "          <label for=\"username\" class=\"sr-only\">Username</label>\n"
			+ "          <input type=\"text\" id=\"username\" name=\"username\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n"
			+ "        </p>\n"
			+ "        <p>\n"
			+ "          <label for=\"password\" class=\"sr-only\">Password</label>\n"
			+ "          <input type=\"password\" id=\"password\" name=\"password\" class=\"form-control\" placeholder=\"Password\" required>\n"
			+ "        </p>\n"
			+ csrfTokenHtmlInput
			+ "        <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n"
			+ "      </form>\n"
			+ "    </div>\n"
			+ "  </body>\n"
			+ "</html>";

		return page.getBytes(Charset.defaultCharset());
	}

	private static String csrfToken(CsrfToken token) {
		return "          <input type=\"hidden\" name=\"" + token.getParameterName() + "\" value=\"" + token.getToken() + "\">\n";
	}

	private static String createError(boolean isError) {
		return isError ? "<div class=\"alert alert-danger\" role=\"alert\">Invalid credentials</div>" : "";
	}

	private static String createLogoutSuccess(boolean isLogoutSuccess) {
		return isLogoutSuccess ? "<div class=\"alert alert-success\" role=\"alert\">You have been signed out</div>" : "";
	}
}

BELATED ARTICLES

more