Ynns

spring security 프로젝트 (1) 본문

JAVA/Spring

spring security 프로젝트 (1)

yunassnn 2019. 11. 7. 15:46

# 프로젝트 생성

SpringLegacyProject 생성

# pom.xml 환경설정에 라이브러리 추가

	<!-- spring security taglibs 라이브러리  -->
	<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-taglibs</artifactId>
	    <version>5.1.2.RELEASE</version>
	</dependency>
	<!-- spring security config 라이브러리 : security와 관련된 XML 설정 or 자바 설정을 위해 필요한 라이브러리  -->
	<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-config</artifactId>
	    <version>5.1.2.RELEASE</version>
	</dependency>
	<!-- spring security web 라이브러리 : 웹 기반의 인증과 URL 기반의 접근 제어를 위해 필요한 라이브러리-->
	<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-web</artifactId>
	    <version>5.1.2.RELEASE</version>
	</dependency>
	<!-- lombok -->
	<dependency>
	    <groupId>org.projectlombok</groupId>
	    <artifactId>lombok</artifactId>
	    <version>1.18.10</version>
	    <scope>provided</scope>
	</dependency>

 

# security-context.xml 추가

security-context.xml 추가 위치

<!--security-context.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
	<!-- security-context 환경설정 파일 만들고 뒤에 버전을 없애준다 -->

 

# web.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 <!-- security-context.xml 추가 -->
		</param-value>
	</context-param>
    
    ...
    
    <!-- spring-security 필터 설정 -->
	<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>

 

# jsp 추가

admin, all, member jsp 추가

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>/security/member</h3> <!-- 각 페이지에 경로를 띄워 준다 -->
</body>
</html>

# SecurityController/CommonController 생성

SecurityController/CommonController

# 6 security-context.xml 수정

//security-context.xml

<security:http>
		<!-- 특정한 URI에 접근할 때 인터셉터를 이용해서 접근을 제한하는 설정 -->
		<security:intercept-url pattern="/security/all" access="permitAll"/><!-- patter : 들어오는 uri -->
		<security:intercept-url pattern="/security/member" access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/security/admin" access="hasRole('ROLE_ADMIN')"/>
		<!-- 403 : 권한이 없는 사용자가 접근이 제한된 uri에 접근하는 경우 -->
		<security:access-denied-handler error-page="/accessError"/>
		<!-- 로그인이 성공하면 시작했던 페이지를 유지하려는 기본 값 때문에 시작했던 페이지로 돌려보낸다
		http://localhost:8083/security/member 
		-> http://localhost:8083/signin -> 로그인 성공 시
		-> http://localhost:8083/security/member 로 화면이 종료-->
		<!-- authentication-success-handler-ref 값을 지정해 주면 로그인 성공 후 원하는 페이지로 이동 시킬 수 있다
		-> Handler 를 통해서 작업한다 -->
		<!-- /signin페이지로 이동하게 요청함 -->
		<security:form-login login-page="/signin" authentication-success-handler-ref="customLoginSuccess"/>
	
	</security:http>
	
	<!-- 인증 담당 -->
	<security:authentication-manager> 
		<!-- 인증 권한 관련 -->
		<security:authentication-provider>
		<!-- 사용자 인증을 위해 in-Memory 방식으로 인증하기 -->
			<security:user-service>
				<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
				<security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
			</security:user-service>
		</security:authentication-provider> 
	</security:authentication-manager>

# 페이지 이동 경로

/security/member => filter => interceptor =(인증된 권한이 없으면)> controller =(컨트롤러를 통해서 로그인 페이지로 이동)> /signin 화면

 

# 로그인 성공 시 사용할 핸들러 추가 

Handler 패키지 추가 후 핸들러 생성

...
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {
		//Authentication : 인증된 정보를 담기 위해 사용하는 객체, 인증이 끝난 정보들(id, password, role)
		List<String> roleNames = new ArrayList<>();
		//List인 roleNames에 authentication.getAuthorities로 정보를 끌고와서 담는다
		authentication.getAuthorities().forEach(auth -> roleNames.add(auth.getAuthority()));
		if(roleNames.contains("ROLE_ADMIN")) {
			response.sendRedirect("/security/admin");
			return;
		}	
		if(roleNames.contains("ROLE_MEMBER")) {
			response.sendRedirect("security/member");
			return;
		}
		response.sendRedirect("/");
	}
}

# 핸들러를 생성함으로서 localhost:8083/signin 으로 이동하면 로그인 성공 시에도 루트로 리턴되지만 핸들러를 이용해 /signin으로 들어가 로그인을 성공해도 /security/member 화면으로 리턴하게 된다

 

# signin.jsp 추가

...
<body class="text-center">
    <form class="form-signin" method="post" action="/login">      
      <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>           
      <label for="inputId" class="sr-only">아이디</label>
      <!-- id=username : 꼭 지켜야 하는 부분 -->
      <input type="text" id="inputId" name="username" class="form-control" placeholder="아이디" required autofocus>
      <label for="inputPassword" class="sr-only">비밀번호</label>
      <input type="password" id="inputPassword" name="password" class="form-control" placeholder="비밀번호" required>
      <div class="checkbox mb-3">
        <label>
          <input type="checkbox" name="remember-me"> Remember me
        </label>
      </div>    
      <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
      <h4>${error}</h4>
      <h4>${logout}</h4>
      <p class="mt-5 mb-3 text-muted">&copy; 2017-2018</p>
     	<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    </form>   
  </body>
  ...

# CSRF - cross-site request forgery 공격을 방지하기 위한 토큰

   • 사용자가 임의로 변하는 특정한 토큰 값을 서버에서 체크하는 방식

CSRF 토큰

 

# logout.jsp 추가

<body>
	<h3>/security/logout</h3>
	<form action="/logout" method="post">
		<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
		<button>로그아웃</button>
	</form>
</body>

# security-context.xml 에 로그아웃 추가

...
<security:logout logout-url="/logout" invalidate-session="true"/>
...

# CommonController 에 logout 메소드 추가

...
	@GetMapping("/logout")
	public String logout() { //(7)
		log.info("logout 페이지 요청");
		return "/security/logout";
	}
	
	@PostMapping("/logout")
	public void logoutPost() { //(9)
		log.info("logout 요청");
	}
...

 

Comments